Add convertObjectToJava to code executors - for converting language objects to java objects

This commit is contained in:
2023-06-20 09:06:57 -05:00
parent 0f799339d6
commit 3791c069c7
6 changed files with 277 additions and 1 deletions

View File

@ -27,6 +27,10 @@ import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.kingsrook.qqq.backend.core.actions.scripts.QCodeExecutor;
import com.kingsrook.qqq.backend.core.actions.scripts.logging.QCodeExecutionLoggerInterface;
@ -36,8 +40,10 @@ import com.kingsrook.qqq.backend.core.utils.ExceptionUtils;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
import org.apache.commons.lang.NotImplementedException;
import org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory;
import org.openjdk.nashorn.api.scripting.ScriptObjectMirror;
import org.openjdk.nashorn.internal.runtime.ECMAException;
import org.openjdk.nashorn.internal.runtime.ParserException;
import org.openjdk.nashorn.internal.runtime.Undefined;
/*******************************************************************************
@ -59,6 +65,59 @@ public class QJavaScriptExecutor implements QCodeExecutor
/*******************************************************************************
**
*******************************************************************************/
@Override
public Object convertObjectToJava(Object object)
{
if(object == null || object instanceof String || object instanceof Boolean || object instanceof Integer || object instanceof Long || object instanceof BigDecimal)
{
return (object);
}
else if(object instanceof Float f)
{
return (new BigDecimal(f));
}
else if(object instanceof Double d)
{
return (new BigDecimal(d));
}
else if(object instanceof Undefined)
{
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// well, we always said we wanted javascript to treat null & undefined the same way... here's our chance //
////////////////////////////////////////////////////////////////////////////////////////////////////////////
return (null);
}
if(object instanceof ScriptObjectMirror scriptObjectMirror)
{
if(scriptObjectMirror.isArray())
{
List<Object> result = new ArrayList<>();
for(String key : scriptObjectMirror.keySet())
{
result.add(Integer.parseInt(key), convertObjectToJava(scriptObjectMirror.get(key)));
}
return (result);
}
else
{
Map<String, Object> result = new HashMap<>();
for(String key : scriptObjectMirror.keySet())
{
result.put(key, convertObjectToJava(scriptObjectMirror.get(key)));
}
return (result);
}
}
return QCodeExecutor.super.convertObjectToJava(object);
}
/*******************************************************************************
**
*******************************************************************************/

View File

@ -23,7 +23,12 @@ package com.kingsrook.qqq.languages.javascript;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.kingsrook.qqq.backend.core.actions.scripts.ExecuteCodeAction;
import com.kingsrook.qqq.backend.core.actions.scripts.QCodeExecutor;
import com.kingsrook.qqq.backend.core.actions.scripts.QCodeExecutorAware;
import com.kingsrook.qqq.backend.core.exceptions.QCodeException;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.scripts.ExecuteCodeInput;
@ -31,6 +36,7 @@ import com.kingsrook.qqq.backend.core.model.actions.scripts.ExecuteCodeOutput;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType;
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
@ -241,10 +247,50 @@ class ExecuteCodeActionTest extends BaseTest
/*******************************************************************************
**
*******************************************************************************/
@Test
void testConvertObjectToJava() throws QException
{
TestQCodeExecutorAware converter = new TestQCodeExecutorAware();
testOne(1, """
converter.convertObject("one", 1);
converter.convertObject("two", "two");
converter.convertObject("true", true);
converter.convertObject("null", null);
converter.convertObject("undefined", undefined);
converter.convertObject("flatMap", {"a": 1, "b": "c"});
converter.convertObject("flatList", ["a", 1, "b", "c"]);
converter.convertObject("mixedMap", {"a": [1, {"2": "3"}], "b": {"c": ["d"]}});
""", MapBuilder.of("converter", converter));
assertEquals(1, converter.getConvertedObject("one"));
assertEquals("two", converter.getConvertedObject("two"));
assertEquals(true, converter.getConvertedObject("true"));
assertNull(converter.getConvertedObject("null"));
assertNull(converter.getConvertedObject("undefined"));
assertEquals(Map.of("a", 1, "b", "c"), converter.getConvertedObject("flatMap"));
assertEquals(List.of("a", 1, "b", "c"), converter.getConvertedObject("flatList"));
assertEquals(Map.of("a", List.of(1, Map.of("2", "3")), "b", Map.of("c", List.of("d"))), converter.getConvertedObject("mixedMap"));
}
/*******************************************************************************
**
*******************************************************************************/
private OneTestOutput testOne(Integer inputValueC, String code) throws QException
{
return (testOne(inputValueC, code, null));
}
/*******************************************************************************
**
*******************************************************************************/
private OneTestOutput testOne(Integer inputValueC, String code, Map<String, Serializable> additionalContext) throws QException
{
System.out.println();
QInstance instance = TestUtils.defineInstance();
@ -259,6 +305,14 @@ class ExecuteCodeActionTest extends BaseTest
input.withContext("input", testInput);
input.withContext("output", testOutput);
if(additionalContext != null)
{
for(Map.Entry<String, Serializable> entry : additionalContext.entrySet())
{
input.withContext(entry.getKey(), entry.getValue());
}
}
ExecuteCodeOutput output = new ExecuteCodeOutput();
ExecuteCodeAction executeCodeAction = new ExecuteCodeAction();
@ -269,6 +323,49 @@ class ExecuteCodeActionTest extends BaseTest
/*******************************************************************************
**
*******************************************************************************/
public static class TestQCodeExecutorAware implements QCodeExecutorAware, Serializable
{
private QCodeExecutor qCodeExecutor;
private Map<String, Object> convertedObjectMap = new HashMap<>();
/*******************************************************************************
**
*******************************************************************************/
@Override
public void setQCodeExecutor(QCodeExecutor qCodeExecutor)
{
this.qCodeExecutor = qCodeExecutor;
}
/*******************************************************************************
**
*******************************************************************************/
public void convertObject(String name, Object inputObject)
{
convertedObjectMap.put(name, qCodeExecutor.convertObjectToJava(inputObject));
}
/*******************************************************************************
**
*******************************************************************************/
public Object getConvertedObject(String name)
{
return (convertedObjectMap.get(name));
}
}
/*******************************************************************************
**
*******************************************************************************/