mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 05:01:07 +00:00
Add convertJavaObject to QCodeExecutor
This commit is contained in:
@ -46,7 +46,17 @@ public interface QCodeExecutor
|
|||||||
** e.g., a Nashorn ScriptObjectMirror will end up as a "primitive", or a List or Map of such
|
** e.g., a Nashorn ScriptObjectMirror will end up as a "primitive", or a List or Map of such
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
default Object convertObjectToJava(Object object)
|
default Object convertObjectToJava(Object object) throws QCodeException
|
||||||
|
{
|
||||||
|
return (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Convert a native java object into one for the script's language/runtime.
|
||||||
|
** e.g., a java Instant to a Nashorn Date
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
default Object convertJavaObject(Object object, Object requestedTypeHint) throws QCodeException
|
||||||
{
|
{
|
||||||
return (object);
|
return (object);
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ import javax.script.ScriptException;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.time.temporal.ChronoField;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -74,72 +75,108 @@ public class QJavaScriptExecutor implements QCodeExecutor
|
|||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@Override
|
@Override
|
||||||
public Object convertObjectToJava(Object object)
|
public Object convertObjectToJava(Object object) throws QCodeException
|
||||||
{
|
{
|
||||||
if(object == null || object instanceof String || object instanceof Boolean || object instanceof Integer || object instanceof Long || object instanceof BigDecimal)
|
try
|
||||||
{
|
{
|
||||||
return (object);
|
if(object == null || object instanceof String || object instanceof Boolean || object instanceof Integer || object instanceof Long || object instanceof BigDecimal)
|
||||||
}
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
if("Date".equals(scriptObjectMirror.getClassName()))
|
return (object);
|
||||||
{
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
// looks like the js Date is in UTC (is that because our JVM is?) //
|
|
||||||
// so the instant being in UTC matches //
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
Double millis = (Double) scriptObjectMirror.callMember("getTime");
|
|
||||||
Instant instant = Instant.ofEpochMilli(millis.longValue());
|
|
||||||
return (instant);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
else if(object instanceof Float f)
|
||||||
{
|
{
|
||||||
LOG.debug("Error unwrapping javascript date", e);
|
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(scriptObjectMirror.isArray())
|
if(object instanceof ScriptObjectMirror scriptObjectMirror)
|
||||||
{
|
{
|
||||||
List<Object> result = new ArrayList<>();
|
try
|
||||||
for(String key : scriptObjectMirror.keySet())
|
|
||||||
{
|
{
|
||||||
result.add(Integer.parseInt(key), convertObjectToJava(scriptObjectMirror.get(key)));
|
if("Date".equals(scriptObjectMirror.getClassName()))
|
||||||
|
{
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// looks like the js Date is in UTC (is that because our JVM is?) //
|
||||||
|
// so the instant being in UTC matches //
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
Double millis = (Double) scriptObjectMirror.callMember("getTime");
|
||||||
|
Instant instant = Instant.ofEpochMilli(millis.longValue());
|
||||||
|
return (instant);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return (result);
|
catch(Exception e)
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// last thing we know to try (though really, there's probably some check we should have around this) //
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
Map<String, Object> result = new HashMap<>();
|
|
||||||
for(String key : scriptObjectMirror.keySet())
|
|
||||||
{
|
{
|
||||||
result.put(key, convertObjectToJava(scriptObjectMirror.get(key)));
|
LOG.debug("Error unwrapping javascript date", e);
|
||||||
}
|
}
|
||||||
return (result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return QCodeExecutor.super.convertObjectToJava(object);
|
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
|
||||||
|
{
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// last thing we know to try (though really, there's probably some check we should have around this) //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
throw (new QCodeException("Error converting java object", e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Convert a native java object into one for the script's language/runtime.
|
||||||
|
** e.g., a java Instant to a Nashorn Date
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Object convertJavaObject(Object object, Object requestedTypeHint) throws QCodeException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if("Date".equals(requestedTypeHint))
|
||||||
|
{
|
||||||
|
if(object instanceof Instant i)
|
||||||
|
{
|
||||||
|
long millis = (i.getEpochSecond() * 1000 + i.getLong(ChronoField.MILLI_OF_SECOND));
|
||||||
|
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
|
||||||
|
return engine.eval("new Date(" + millis + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (QCodeExecutor.super.convertJavaObject(object, requestedTypeHint));
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
throw (new QCodeException("Error converting java object", e));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,9 +41,11 @@ import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
|||||||
import org.assertj.core.api.Assertions;
|
import org.assertj.core.api.Assertions;
|
||||||
import org.assertj.core.data.Offset;
|
import org.assertj.core.data.Offset;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.openjdk.nashorn.api.scripting.ScriptObjectMirror;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
|
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
@ -284,6 +286,27 @@ class ExecuteCodeActionTest extends BaseTest
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testConvertJavaObject() throws QException
|
||||||
|
{
|
||||||
|
TestQCodeExecutorAware converter = new TestQCodeExecutorAware();
|
||||||
|
|
||||||
|
Instant originalInstant = Instant.parse("2023-07-03T11:42:42Z");
|
||||||
|
testOne(1, """
|
||||||
|
converter.convertJavaObject("jsDate", instant, "Date");
|
||||||
|
converter.convertObject("backToInstant", converter.getConvertedObject("jsDate"));
|
||||||
|
""", MapBuilder.of("converter", converter, "instant", originalInstant));
|
||||||
|
|
||||||
|
assertThat(converter.getConvertedObject("jsDate")).isInstanceOf(ScriptObjectMirror.class);
|
||||||
|
assertEquals(originalInstant, converter.getConvertedObject("backToInstant"));
|
||||||
|
assertNotSame(originalInstant, converter.getConvertedObject("backToInstant"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -355,13 +378,23 @@ class ExecuteCodeActionTest extends BaseTest
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public void convertObject(String name, Object inputObject)
|
public void convertObject(String name, Object inputObject) throws QCodeException
|
||||||
{
|
{
|
||||||
convertedObjectMap.put(name, qCodeExecutor.convertObjectToJava(inputObject));
|
convertedObjectMap.put(name, qCodeExecutor.convertObjectToJava(inputObject));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void convertJavaObject(String name, Object inputObject, Object hint) throws QCodeException
|
||||||
|
{
|
||||||
|
convertedObjectMap.put(name, qCodeExecutor.convertJavaObject(inputObject, hint));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -36,6 +36,7 @@ import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaDataContainer;
|
|||||||
import com.kingsrook.qqq.backend.core.actions.scripts.QCodeExecutor;
|
import com.kingsrook.qqq.backend.core.actions.scripts.QCodeExecutor;
|
||||||
import com.kingsrook.qqq.backend.core.actions.scripts.QCodeExecutorAware;
|
import com.kingsrook.qqq.backend.core.actions.scripts.QCodeExecutorAware;
|
||||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QCodeException;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||||
@ -208,7 +209,7 @@ public class ApiScriptUtils implements QCodeExecutorAware, Serializable
|
|||||||
** Take a "body" object, which maybe defined in the script's language/run-time,
|
** Take a "body" object, which maybe defined in the script's language/run-time,
|
||||||
** and try to process it into a JSON String (which is what the API Implementation wants)
|
** and try to process it into a JSON String (which is what the API Implementation wants)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private Object processBodyToJsonString(Object body)
|
private Object processBodyToJsonString(Object body) throws QCodeException
|
||||||
{
|
{
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// if the caller already supplied the object as a string, then return that string. //
|
// if the caller already supplied the object as a string, then return that string. //
|
||||||
@ -234,7 +235,7 @@ public class ApiScriptUtils implements QCodeExecutorAware, Serializable
|
|||||||
** script's language into a (more) native java object.
|
** script's language into a (more) native java object.
|
||||||
** e.g., a Nashorn ScriptObjectMirror will end up as a "primitive", or a List or Map of such
|
** e.g., a Nashorn ScriptObjectMirror will end up as a "primitive", or a List or Map of such
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private Object processInputObjectViaCodeExecutor(Object body)
|
private Object processInputObjectViaCodeExecutor(Object body) throws QCodeException
|
||||||
{
|
{
|
||||||
if(qCodeExecutor == null || body == null)
|
if(qCodeExecutor == null || body == null)
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user