Script Tests and further enhancements

This commit is contained in:
2022-11-10 14:21:31 -06:00
parent 6d08afa4c2
commit a5ec33b51b
28 changed files with 1239 additions and 601 deletions

View File

@ -49,6 +49,12 @@
<version>${revision}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.kingsrook.qqq</groupId>
<artifactId>qqq-language-support-javascript</artifactId>
<version>${revision}</version>
<scope>test</scope>
</dependency>
<!-- 3rd party deps specifically for this module -->
<dependency>

View File

@ -42,8 +42,6 @@ import com.kingsrook.qqq.backend.core.actions.metadata.MetaDataAction;
import com.kingsrook.qqq.backend.core.actions.metadata.ProcessMetaDataAction;
import com.kingsrook.qqq.backend.core.actions.metadata.TableMetaDataAction;
import com.kingsrook.qqq.backend.core.actions.reporting.ExportAction;
import com.kingsrook.qqq.backend.core.actions.scripts.StoreAssociatedScriptAction;
import com.kingsrook.qqq.backend.core.actions.scripts.TestScriptAction;
import com.kingsrook.qqq.backend.core.actions.tables.CountAction;
import com.kingsrook.qqq.backend.core.actions.tables.DeleteAction;
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
@ -68,10 +66,6 @@ import com.kingsrook.qqq.backend.core.model.actions.metadata.TableMetaDataInput;
import com.kingsrook.qqq.backend.core.model.actions.metadata.TableMetaDataOutput;
import com.kingsrook.qqq.backend.core.model.actions.reporting.ExportInput;
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportFormat;
import com.kingsrook.qqq.backend.core.model.actions.scripts.StoreAssociatedScriptInput;
import com.kingsrook.qqq.backend.core.model.actions.scripts.StoreAssociatedScriptOutput;
import com.kingsrook.qqq.backend.core.model.actions.scripts.TestScriptInput;
import com.kingsrook.qqq.backend.core.model.actions.scripts.TestScriptOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
@ -80,9 +74,6 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
@ -95,13 +86,11 @@ import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetOutput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.AssociatedScript;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.session.QSession;
import com.kingsrook.qqq.backend.core.modules.authentication.Auth0AuthenticationModule;
import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleDispatcher;
import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleInterface;
import com.kingsrook.qqq.backend.core.processes.utils.GeneralProcessUtils;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.ExceptionUtils;
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
@ -109,7 +98,6 @@ import com.kingsrook.qqq.backend.core.utils.StringUtils;
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
import io.javalin.Javalin;
import io.javalin.apibuilder.EndpointGroup;
import io.javalin.http.ContentType;
import io.javalin.http.Context;
import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.LogManager;
@ -308,10 +296,7 @@ public class QJavalinImplementation
put("", QJavalinImplementation::dataUpdate); // todo - want different semantics??
delete("", QJavalinImplementation::dataDelete);
get("/developer", QJavalinImplementation::getRecordDeveloperMode);
post("/developer/associatedScript/{fieldName}", QJavalinImplementation::storeRecordAssociatedScript);
get("/developer/associatedScript/{fieldName}/{scriptRevisionId}/logs", QJavalinImplementation::getAssociatedScriptLogs);
post("/developer/associatedScript/{fieldName}/test", QJavalinImplementation::testAssociatedScript);
QJavalinScriptsHandler.defineRecordRoutes();
});
});
@ -986,213 +971,6 @@ public class QJavalinImplementation
/*******************************************************************************
**
*******************************************************************************/
private static void getRecordDeveloperMode(Context context)
{
try
{
String tableName = context.pathParam("table");
QTableMetaData table = qInstance.getTable(tableName);
String primaryKey = context.pathParam("primaryKey");
GetInput getInput = new GetInput(qInstance);
setupSession(context, getInput);
getInput.setTableName(tableName);
getInput.setShouldGenerateDisplayValues(true);
getInput.setShouldTranslatePossibleValues(true);
// todo - validate that the primary key is of the proper type (e.g,. not a string for an id field)
// and throw a 400-series error (tell the user bad-request), rather than, we're doing a 500 (server error)
getInput.setPrimaryKey(primaryKey);
GetAction getAction = new GetAction();
GetOutput getOutput = getAction.execute(getInput);
///////////////////////////////////////////////////////
// throw a not found error if the record isn't found //
///////////////////////////////////////////////////////
QRecord record = getOutput.getRecord();
if(record == null)
{
throw (new QNotFoundException("Could not find " + table.getLabel() + " with "
+ table.getFields().get(table.getPrimaryKeyField()).getLabel() + " of " + primaryKey));
}
Map<String, Serializable> rs = new HashMap<>();
rs.put("record", record);
ArrayList<HashMap<String, Serializable>> associatedScripts = new ArrayList<>();
rs.put("associatedScripts", associatedScripts);
///////////////////////////////////////////////////////
// process each associated script type for the table //
///////////////////////////////////////////////////////
for(AssociatedScript associatedScript : CollectionUtils.nonNullList(table.getAssociatedScripts()))
{
HashMap<String, Serializable> thisScriptData = new HashMap<>();
associatedScripts.add(thisScriptData);
thisScriptData.put("associatedScript", associatedScript);
String fieldName = associatedScript.getFieldName();
Serializable scriptId = record.getValue(fieldName);
if(scriptId != null)
{
GetInput getScriptInput = new GetInput(qInstance);
setupSession(context, getScriptInput);
getScriptInput.setTableName("script");
getScriptInput.setPrimaryKey(scriptId);
GetOutput getScriptOutput = new GetAction().execute(getScriptInput);
if(getScriptOutput.getRecord() != null)
{
thisScriptData.put("script", getScriptOutput.getRecord());
QueryInput queryInput = new QueryInput(qInstance);
setupSession(context, queryInput);
queryInput.setTableName("scriptRevision");
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria("scriptId", QCriteriaOperator.EQUALS, List.of(getScriptOutput.getRecord().getValue("id"))))
.withOrderBy(new QFilterOrderBy("id", false))
);
QueryOutput queryOutput = new QueryAction().execute(queryInput);
thisScriptData.put("scriptRevisions", new ArrayList<>(queryOutput.getRecords()));
}
}
}
context.result(JsonUtils.toJson(rs));
}
catch(Exception e)
{
handleException(context, e);
}
}
/*******************************************************************************
**
*******************************************************************************/
public static void getAssociatedScriptLogs(Context context)
{
try
{
String scriptRevisionId = context.pathParam("scriptRevisionId");
QueryInput queryInput = new QueryInput(qInstance);
setupSession(context, queryInput);
queryInput.setTableName("scriptLog");
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria("scriptRevisionId", QCriteriaOperator.EQUALS, List.of(scriptRevisionId)))
.withOrderBy(new QFilterOrderBy("id", false))
);
queryInput.setLimit(100);
QueryOutput queryOutput = new QueryAction().execute(queryInput);
if(CollectionUtils.nullSafeHasContents(queryOutput.getRecords()))
{
GeneralProcessUtils.addForeignRecordsListToRecordList(queryInput, queryOutput.getRecords(), "id", "scriptLogLine", "scriptLogId");
}
Map<String, Serializable> rs = new HashMap<>();
rs.put("scriptLogRecords", new ArrayList<>(queryOutput.getRecords()));
context.result(JsonUtils.toJson(rs));
}
catch(Exception e)
{
handleException(context, e);
}
}
/*******************************************************************************
**
*******************************************************************************/
private static void storeRecordAssociatedScript(Context context)
{
context.contentType(ContentType.APPLICATION_JSON);
try
{
StoreAssociatedScriptInput input = new StoreAssociatedScriptInput(qInstance);
setupSession(context, input);
input.setCode(context.formParam("contents"));
input.setCommitMessage(context.formParam("commitMessage"));
input.setFieldName(context.pathParam("fieldName"));
input.setTableName(context.pathParam("table"));
input.setRecordPrimaryKey(context.pathParam("primaryKey"));
StoreAssociatedScriptOutput output = new StoreAssociatedScriptOutput();
StoreAssociatedScriptAction storeAssociatedScriptAction = new StoreAssociatedScriptAction();
storeAssociatedScriptAction.run(input, output);
context.result(JsonUtils.toJson(output));
}
catch(Exception e)
{
handleException(context, e);
}
}
/*******************************************************************************
**
*******************************************************************************/
private static void testAssociatedScript(Context context)
{
try
{
TestScriptInput input = new TestScriptInput(qInstance);
setupSession(context, input);
input.setTableName(context.pathParam("table"));
input.setRecordPrimaryKey(context.pathParam("primaryKey"));
// context.pathParam("fieldName");
Map<String, String> inputValues = new HashMap<>();
input.setInputValues(inputValues);
for(Map.Entry<String, List<String>> entry : context.formParamMap().entrySet())
{
String key = entry.getKey();
String value = entry.getValue().get(0);
if(key.equals("code"))
{
input.setCode(value);
}
else if(key.equals("scriptTypeId"))
{
input.setScriptTypeId(ValueUtils.getValueAsInteger(value));
}
else
{
inputValues.put(key, value);
}
}
TestScriptOutput output = new TestScriptOutput();
new TestScriptAction().run(input, output);
//////////////////////////////////////////////////////////////
// todo - output to frontend - then add assertions in test. //
//////////////////////////////////////////////////////////////
context.result(JsonUtils.toJson("OK"));
}
catch(Exception e)
{
handleException(context, e);
}
}
/*******************************************************************************
**
*******************************************************************************/

View File

@ -174,10 +174,10 @@ public class QJavalinProcessHandler
return (new RunProcessAction().execute(runProcessInput));
});
LOG.info("Process result error? " + runProcessOutput.getException());
LOG.debug("Process result error? " + runProcessOutput.getException());
for(QFieldMetaData outputField : QJavalinImplementation.qInstance.getProcess(runProcessInput.getProcessName()).getOutputFields())
{
LOG.info("Process result output value: " + outputField.getName() + ": " + runProcessOutput.getValues().get(outputField.getName()));
LOG.debug("Process result output value: " + outputField.getName() + ": " + runProcessOutput.getValues().get(outputField.getName()));
}
serializeRunProcessResultForCaller(resultForCaller, runProcessOutput);

View File

@ -0,0 +1,341 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.javalin;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
import com.kingsrook.qqq.backend.core.actions.scripts.StoreAssociatedScriptAction;
import com.kingsrook.qqq.backend.core.actions.scripts.TestScriptActionInterface;
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException;
import com.kingsrook.qqq.backend.core.instances.QInstanceEnricher;
import com.kingsrook.qqq.backend.core.model.actions.scripts.StoreAssociatedScriptInput;
import com.kingsrook.qqq.backend.core.model.actions.scripts.StoreAssociatedScriptOutput;
import com.kingsrook.qqq.backend.core.model.actions.scripts.TestScriptInput;
import com.kingsrook.qqq.backend.core.model.actions.scripts.TestScriptOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
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.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.AssociatedScript;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.scripts.ScriptType;
import com.kingsrook.qqq.backend.core.processes.utils.GeneralProcessUtils;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
import io.javalin.http.ContentType;
import io.javalin.http.Context;
import static io.javalin.apibuilder.ApiBuilder.get;
import static io.javalin.apibuilder.ApiBuilder.post;
/*******************************************************************************
** endpoints and handlers for deal with record scripts
*******************************************************************************/
public class QJavalinScriptsHandler
{
/*******************************************************************************
** Define routes under the basic /data/${table}/${primaryKey} path - e.g.,
** record-specific script routes.
*******************************************************************************/
public static void defineRecordRoutes()
{
get("/developer", QJavalinScriptsHandler::getRecordDeveloperMode);
post("/developer/associatedScript/{fieldName}", QJavalinScriptsHandler::storeRecordAssociatedScript);
get("/developer/associatedScript/{fieldName}/{scriptRevisionId}/logs", QJavalinScriptsHandler::getAssociatedScriptLogs);
post("/developer/associatedScript/{fieldName}/test", QJavalinScriptsHandler::testAssociatedScript);
}
/*******************************************************************************
**
*******************************************************************************/
private static void getRecordDeveloperMode(Context context)
{
try
{
String tableName = context.pathParam("table");
QTableMetaData table = QJavalinImplementation.qInstance.getTable(tableName);
String primaryKey = context.pathParam("primaryKey");
GetInput getInput = new GetInput(QJavalinImplementation.qInstance);
QJavalinImplementation.setupSession(context, getInput);
getInput.setTableName(tableName);
getInput.setShouldGenerateDisplayValues(true);
getInput.setShouldTranslatePossibleValues(true);
// todo - validate that the primary key is of the proper type (e.g,. not a string for an id field)
// and throw a 400-series error (tell the user bad-request), rather than, we're doing a 500 (server error)
getInput.setPrimaryKey(primaryKey);
GetAction getAction = new GetAction();
GetOutput getOutput = getAction.execute(getInput);
///////////////////////////////////////////////////////
// throw a not found error if the record isn't found //
///////////////////////////////////////////////////////
QRecord record = getOutput.getRecord();
if(record == null)
{
throw (new QNotFoundException("Could not find " + table.getLabel() + " with "
+ table.getFields().get(table.getPrimaryKeyField()).getLabel() + " of " + primaryKey));
}
Map<String, Serializable> rs = new HashMap<>();
rs.put("record", record);
ArrayList<HashMap<String, Serializable>> associatedScripts = new ArrayList<>();
rs.put("associatedScripts", associatedScripts);
Map<Serializable, QRecord> scriptTypeMap = GeneralProcessUtils.loadTableToMap(getInput, ScriptType.TABLE_NAME, "id");
///////////////////////////////////////////////////////
// process each associated script type for the table //
///////////////////////////////////////////////////////
QInstanceEnricher qInstanceEnricher = new QInstanceEnricher(QJavalinImplementation.qInstance);
for(AssociatedScript associatedScript : CollectionUtils.nonNullList(table.getAssociatedScripts()))
{
HashMap<String, Serializable> thisScriptData = new HashMap<>();
associatedScripts.add(thisScriptData);
thisScriptData.put("associatedScript", associatedScript);
thisScriptData.put("scriptType", scriptTypeMap.get(associatedScript.getScriptTypeId()));
/////////////////////////////////////////////////////////////////////
// load the associated script and current revision from the record //
/////////////////////////////////////////////////////////////////////
String fieldName = associatedScript.getFieldName();
Serializable scriptId = record.getValue(fieldName);
if(scriptId != null)
{
GetInput getScriptInput = new GetInput(QJavalinImplementation.qInstance);
QJavalinImplementation.setupSession(context, getScriptInput);
getScriptInput.setTableName("script");
getScriptInput.setPrimaryKey(scriptId);
GetOutput getScriptOutput = new GetAction().execute(getScriptInput);
if(getScriptOutput.getRecord() != null)
{
thisScriptData.put("script", getScriptOutput.getRecord());
QueryInput queryInput = new QueryInput(QJavalinImplementation.qInstance);
QJavalinImplementation.setupSession(context, queryInput);
queryInput.setTableName("scriptRevision");
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria("scriptId", QCriteriaOperator.EQUALS, List.of(getScriptOutput.getRecord().getValue("id"))))
.withOrderBy(new QFilterOrderBy("id", false))
);
QueryOutput queryOutput = new QueryAction().execute(queryInput);
thisScriptData.put("scriptRevisions", new ArrayList<>(queryOutput.getRecords()));
}
}
///////////////////////////////////////////////////////////
// load testing info about the script type, if available //
///////////////////////////////////////////////////////////
QCodeReference scriptTesterCodeRef = associatedScript.getScriptTester();
if(scriptTesterCodeRef != null)
{
TestScriptActionInterface scriptTester = QCodeLoader.getAdHoc(TestScriptActionInterface.class, scriptTesterCodeRef);
thisScriptData.put("testInputFields", enrichFieldsToArrayList(qInstanceEnricher, scriptTester.getTestInputFields()));
thisScriptData.put("testOutputFields", enrichFieldsToArrayList(qInstanceEnricher, scriptTester.getTestOutputFields()));
}
}
context.result(JsonUtils.toJson(rs));
}
catch(Exception e)
{
QJavalinImplementation.handleException(context, e);
}
}
/*******************************************************************************
**
*******************************************************************************/
private static Serializable enrichFieldsToArrayList(QInstanceEnricher qInstanceEnricher, List<QFieldMetaData> fields)
{
ArrayList<QFieldMetaData> rs = new ArrayList<>();
if(CollectionUtils.nullSafeIsEmpty(fields))
{
return (rs);
}
for(QFieldMetaData field : fields)
{
qInstanceEnricher.enrichField(field);
rs.add(field);
}
return (rs);
}
/*******************************************************************************
**
*******************************************************************************/
public static void getAssociatedScriptLogs(Context context)
{
try
{
String scriptRevisionId = context.pathParam("scriptRevisionId");
QueryInput queryInput = new QueryInput(QJavalinImplementation.qInstance);
QJavalinImplementation.setupSession(context, queryInput);
queryInput.setTableName("scriptLog");
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria("scriptRevisionId", QCriteriaOperator.EQUALS, List.of(scriptRevisionId)))
.withOrderBy(new QFilterOrderBy("id", false))
);
queryInput.setLimit(100);
QueryOutput queryOutput = new QueryAction().execute(queryInput);
if(CollectionUtils.nullSafeHasContents(queryOutput.getRecords()))
{
GeneralProcessUtils.addForeignRecordsListToRecordList(queryInput, queryOutput.getRecords(), "id", "scriptLogLine", "scriptLogId");
}
Map<String, Serializable> rs = new HashMap<>();
rs.put("scriptLogRecords", new ArrayList<>(queryOutput.getRecords()));
context.result(JsonUtils.toJson(rs));
}
catch(Exception e)
{
QJavalinImplementation.handleException(context, e);
}
}
/*******************************************************************************
**
*******************************************************************************/
private static void storeRecordAssociatedScript(Context context)
{
context.contentType(ContentType.APPLICATION_JSON);
try
{
StoreAssociatedScriptInput input = new StoreAssociatedScriptInput(QJavalinImplementation.qInstance);
QJavalinImplementation.setupSession(context, input);
input.setCode(context.formParam("contents"));
input.setCommitMessage(context.formParam("commitMessage"));
input.setFieldName(context.pathParam("fieldName"));
input.setTableName(context.pathParam("table"));
input.setRecordPrimaryKey(context.pathParam("primaryKey"));
StoreAssociatedScriptOutput output = new StoreAssociatedScriptOutput();
StoreAssociatedScriptAction storeAssociatedScriptAction = new StoreAssociatedScriptAction();
storeAssociatedScriptAction.run(input, output);
context.result(JsonUtils.toJson(output));
}
catch(Exception e)
{
QJavalinImplementation.handleException(context, e);
}
}
/*******************************************************************************
**
*******************************************************************************/
private static void testAssociatedScript(Context context)
{
context.contentType(ContentType.APPLICATION_JSON);
try
{
TestScriptInput input = new TestScriptInput(QJavalinImplementation.qInstance);
QJavalinImplementation.setupSession(context, input);
// todo delete? input.setRecordPrimaryKey(context.pathParam("primaryKey"));
Map<String, Serializable> inputValues = new HashMap<>();
input.setInputValues(inputValues);
String tableName = context.pathParam("table");
String fieldName = context.pathParam("fieldName");
QTableMetaData table = QJavalinImplementation.qInstance.getTable(tableName);
Optional<AssociatedScript> optionalAssociatedScript = table.getAssociatedScripts().stream().filter(as -> as.getFieldName().equals(fieldName)).findFirst();
if(optionalAssociatedScript.isEmpty())
{
throw new IllegalArgumentException("No associated script was found for field " + fieldName + " on table " + tableName);
}
AssociatedScript associatedScript = optionalAssociatedScript.get();
QCodeReference scriptTesterCodeRef = associatedScript.getScriptTester();
if(scriptTesterCodeRef == null)
{
throw (new IllegalArgumentException("This scriptType cannot be tested, as it does not define a scriptTester codeReference."));
}
for(Map.Entry<String, List<String>> entry : context.formParamMap().entrySet())
{
String key = entry.getKey();
String value = entry.getValue().get(0);
if(key.equals("code"))
{
input.setCodeReference(new QCodeReference().withInlineCode(value).withCodeType(QCodeType.JAVA_SCRIPT));
}
else
{
inputValues.put(key, value);
}
}
TestScriptActionInterface scriptTester = QCodeLoader.getAdHoc(TestScriptActionInterface.class, scriptTesterCodeRef);
TestScriptOutput output = new TestScriptOutput();
scriptTester.execute(input, output);
context.result(JsonUtils.toJson(output));
}
catch(Exception e)
{
QJavalinImplementation.handleException(context, e);
}
}
}

View File

@ -26,23 +26,9 @@ import java.io.Serializable;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportFormat;
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.WidgetType;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.session.QSession;
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
import kong.unirest.HttpResponse;
import kong.unirest.Unirest;
@ -630,135 +616,4 @@ class QJavalinImplementationTest extends QJavalinTestBase
assertEquals(5, jsonObject.getJSONArray("options").getJSONObject(1).getInt("id"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testGetRecordDeveloperMode() throws QException
{
UpdateInput updateInput = new UpdateInput(TestUtils.defineInstance());
updateInput.setSession(new QSession());
updateInput.setTableName("person");
updateInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("testScriptId", 47)));
new UpdateAction().execute(updateInput);
InsertInput insertInput = new InsertInput(TestUtils.defineInstance());
insertInput.setSession(new QSession());
insertInput.setTableName("script");
insertInput.setRecords(List.of(new QRecord().withValue("id", 47).withValue("currentScriptRevisionId", 100)));
new InsertAction().execute(insertInput);
insertInput.setTableName("scriptRevision");
insertInput.setRecords(List.of(new QRecord().withValue("id", 1000).withValue("scriptId", 47).withValue("content", "var i;")));
new InsertAction().execute(insertInput);
HttpResponse<String> response = Unirest.get(BASE_URL + "/data/person/1/developer").asString();
assertEquals(200, response.getStatus());
JSONObject jsonObject = JsonUtils.toJSONObject(response.getBody());
System.out.println(jsonObject.toString(3));
assertNotNull(jsonObject);
assertNotNull(jsonObject.getJSONObject("record"));
assertEquals("Darin", jsonObject.getJSONObject("record").getJSONObject("values").getString("firstName"));
assertEquals("Darin", jsonObject.getJSONObject("record").getJSONObject("displayValues").getString("firstName"));
assertNotNull(jsonObject.getJSONArray("associatedScripts"));
assertNotNull(jsonObject.getJSONArray("associatedScripts").getJSONObject(0));
assertNotNull(jsonObject.getJSONArray("associatedScripts").getJSONObject(0).getJSONArray("scriptRevisions"));
assertEquals("var i;", jsonObject.getJSONArray("associatedScripts").getJSONObject(0).getJSONArray("scriptRevisions").getJSONObject(0).getJSONObject("values").getString("content"));
assertNotNull(jsonObject.getJSONArray("associatedScripts").getJSONObject(0).getJSONObject("script"));
assertEquals(100, jsonObject.getJSONArray("associatedScripts").getJSONObject(0).getJSONObject("script").getJSONObject("values").getInt("currentScriptRevisionId"));
assertNotNull(jsonObject.getJSONArray("associatedScripts").getJSONObject(0).getJSONObject("associatedScript"));
assertEquals("testScriptId", jsonObject.getJSONArray("associatedScripts").getJSONObject(0).getJSONObject("associatedScript").getString("fieldName"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testStoreRecordAssociatedScript() throws QException
{
InsertInput insertInput = new InsertInput(TestUtils.defineInstance());
insertInput.setSession(new QSession());
insertInput.setTableName("scriptType");
insertInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("name", "Test")));
new InsertAction().execute(insertInput);
HttpResponse<String> response = Unirest.post(BASE_URL + "/data/person/1/developer/associatedScript/testScriptId")
.field("contents", "var j = 0;")
.field("commitMessage", "Javalin Commit")
.asString();
QueryInput queryInput = new QueryInput(TestUtils.defineInstance());
queryInput.setSession(new QSession());
queryInput.setTableName("scriptRevision");
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria("contents", QCriteriaOperator.EQUALS, List.of("var j = 0;")))
.withCriteria(new QFilterCriteria("commitMessage", QCriteriaOperator.EQUALS, List.of("Javalin Commit")))
);
QueryOutput queryOutput = new QueryAction().execute(queryInput);
assertEquals(1, queryOutput.getRecords().size());
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testTestAssociatedScript() throws QException
{
InsertInput insertInput = new InsertInput(TestUtils.defineInstance());
insertInput.setSession(new QSession());
insertInput.setTableName("scriptType");
insertInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("name", "Test")));
new InsertAction().execute(insertInput);
HttpResponse<String> response = Unirest.post(BASE_URL + "/data/person/1/developer/associatedScript/testScriptId/test")
.field("code", "var j = 0;")
.field("scriptTypeId", "1")
.field("x", "47")
.asString();
/////////////////////////////////////////
// todo - assertions after implemented //
/////////////////////////////////////////
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testGetAssociatedScriptLogs() throws QException
{
InsertInput insertInput = new InsertInput(TestUtils.defineInstance());
insertInput.setSession(new QSession());
insertInput.setTableName("scriptLog");
insertInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("output", "testOutput").withValue("scriptRevisionId", 100)));
new InsertAction().execute(insertInput);
insertInput.setTableName("scriptLogLine");
insertInput.setRecords(List.of(
new QRecord().withValue("scriptLogId", 1).withValue("text", "line one"),
new QRecord().withValue("scriptLogId", 1).withValue("text", "line two")
));
new InsertAction().execute(insertInput);
HttpResponse<String> response = Unirest.get(BASE_URL + "/data/person/1/developer/associatedScript/testScriptId/100/logs").asString();
assertEquals(200, response.getStatus());
JSONObject jsonObject = JsonUtils.toJSONObject(response.getBody());
assertNotNull(jsonObject.getJSONArray("scriptLogRecords"));
assertEquals(1, jsonObject.getJSONArray("scriptLogRecords").length());
assertNotNull(jsonObject.getJSONArray("scriptLogRecords").getJSONObject(0).getJSONObject("values"));
assertEquals("testOutput", jsonObject.getJSONArray("scriptLogRecords").getJSONObject(0).getJSONObject("values").getString("output"));
assertNotNull(jsonObject.getJSONArray("scriptLogRecords").getJSONObject(0).getJSONObject("values").getJSONArray("scriptLogLine"));
assertEquals(2, jsonObject.getJSONArray("scriptLogRecords").getJSONObject(0).getJSONObject("values").getJSONArray("scriptLogLine").length());
assertEquals("line one", jsonObject.getJSONArray("scriptLogRecords").getJSONObject(0).getJSONObject("values").getJSONArray("scriptLogLine").getJSONObject(0).getJSONObject("values").getString("text"));
assertEquals("line two", jsonObject.getJSONArray("scriptLogRecords").getJSONObject(0).getJSONObject("values").getJSONArray("scriptLogLine").getJSONObject(1).getJSONObject("values").getString("text"));
}
}

View File

@ -0,0 +1,187 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.javalin;
import java.util.List;
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.session.QSession;
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
import kong.unirest.HttpResponse;
import kong.unirest.Unirest;
import org.json.JSONObject;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/*******************************************************************************
** Unit test for the javalin scripts handler methods.
*******************************************************************************/
class QJavalinScriptsHandlerTest extends QJavalinTestBase
{
/*******************************************************************************
**
*******************************************************************************/
@Test
void testGetRecordDeveloperMode() throws QException
{
UpdateInput updateInput = new UpdateInput(TestUtils.defineInstance());
updateInput.setSession(new QSession());
updateInput.setTableName("person");
updateInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("testScriptId", 47)));
new UpdateAction().execute(updateInput);
InsertInput insertInput = new InsertInput(TestUtils.defineInstance());
insertInput.setSession(new QSession());
insertInput.setTableName("script");
insertInput.setRecords(List.of(new QRecord().withValue("id", 47).withValue("currentScriptRevisionId", 100)));
new InsertAction().execute(insertInput);
insertInput.setTableName("scriptRevision");
insertInput.setRecords(List.of(new QRecord().withValue("id", 1000).withValue("scriptId", 47).withValue("content", "var i;")));
new InsertAction().execute(insertInput);
HttpResponse<String> response = Unirest.get(BASE_URL + "/data/person/1/developer").asString();
assertEquals(200, response.getStatus());
JSONObject jsonObject = JsonUtils.toJSONObject(response.getBody());
System.out.println(jsonObject.toString(3));
assertNotNull(jsonObject);
assertNotNull(jsonObject.getJSONObject("record"));
assertEquals("Darin", jsonObject.getJSONObject("record").getJSONObject("values").getString("firstName"));
assertEquals("Darin", jsonObject.getJSONObject("record").getJSONObject("displayValues").getString("firstName"));
assertNotNull(jsonObject.getJSONArray("associatedScripts"));
assertNotNull(jsonObject.getJSONArray("associatedScripts").getJSONObject(0));
assertNotNull(jsonObject.getJSONArray("associatedScripts").getJSONObject(0).getJSONArray("scriptRevisions"));
assertEquals("var i;", jsonObject.getJSONArray("associatedScripts").getJSONObject(0).getJSONArray("scriptRevisions").getJSONObject(0).getJSONObject("values").getString("content"));
assertNotNull(jsonObject.getJSONArray("associatedScripts").getJSONObject(0).getJSONObject("script"));
assertEquals(100, jsonObject.getJSONArray("associatedScripts").getJSONObject(0).getJSONObject("script").getJSONObject("values").getInt("currentScriptRevisionId"));
assertNotNull(jsonObject.getJSONArray("associatedScripts").getJSONObject(0).getJSONObject("associatedScript"));
assertEquals("testScriptId", jsonObject.getJSONArray("associatedScripts").getJSONObject(0).getJSONObject("associatedScript").getString("fieldName"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testStoreRecordAssociatedScript() throws QException
{
InsertInput insertInput = new InsertInput(TestUtils.defineInstance());
insertInput.setSession(new QSession());
insertInput.setTableName("scriptType");
insertInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("name", "Test")));
new InsertAction().execute(insertInput);
HttpResponse<String> response = Unirest.post(BASE_URL + "/data/person/1/developer/associatedScript/testScriptId")
.field("contents", "var j = 0;")
.field("commitMessage", "Javalin Commit")
.asString();
QueryInput queryInput = new QueryInput(TestUtils.defineInstance());
queryInput.setSession(new QSession());
queryInput.setTableName("scriptRevision");
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria("contents", QCriteriaOperator.EQUALS, List.of("var j = 0;")))
.withCriteria(new QFilterCriteria("commitMessage", QCriteriaOperator.EQUALS, List.of("Javalin Commit")))
);
QueryOutput queryOutput = new QueryAction().execute(queryInput);
assertEquals(1, queryOutput.getRecords().size());
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testTestAssociatedScript() throws QException
{
InsertInput insertInput = new InsertInput(TestUtils.defineInstance());
insertInput.setSession(new QSession());
insertInput.setTableName("scriptType");
insertInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("name", "Test")));
new InsertAction().execute(insertInput);
HttpResponse<String> response = Unirest.post(BASE_URL + "/data/person/1/developer/associatedScript/testScriptId/test")
.field("code", """
// output.setMessage(`${input.getName()} is ${input.getAge()} years old.`);
output.setMessage("I am " + input.getName());
return (output);
""")
.field("scriptTypeId", "1")
.field("name", "Tim")
.asString();
JSONObject jsonObject = JsonUtils.toJSONObject(response.getBody());
assertEquals("I am Tim", jsonObject.getJSONObject("outputObject").getString("message"));
assertNotNull(jsonObject.getJSONObject("scriptLog"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testGetAssociatedScriptLogs() throws QException
{
InsertInput insertInput = new InsertInput(TestUtils.defineInstance());
insertInput.setSession(new QSession());
insertInput.setTableName("scriptLog");
insertInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("output", "testOutput").withValue("scriptRevisionId", 100)));
new InsertAction().execute(insertInput);
insertInput.setTableName("scriptLogLine");
insertInput.setRecords(List.of(
new QRecord().withValue("scriptLogId", 1).withValue("text", "line one"),
new QRecord().withValue("scriptLogId", 1).withValue("text", "line two")
));
new InsertAction().execute(insertInput);
HttpResponse<String> response = Unirest.get(BASE_URL + "/data/person/1/developer/associatedScript/testScriptId/100/logs").asString();
assertEquals(200, response.getStatus());
JSONObject jsonObject = JsonUtils.toJSONObject(response.getBody());
assertNotNull(jsonObject.getJSONArray("scriptLogRecords"));
assertEquals(1, jsonObject.getJSONArray("scriptLogRecords").length());
assertNotNull(jsonObject.getJSONArray("scriptLogRecords").getJSONObject(0).getJSONObject("values"));
assertEquals("testOutput", jsonObject.getJSONArray("scriptLogRecords").getJSONObject(0).getJSONObject("values").getString("output"));
assertNotNull(jsonObject.getJSONArray("scriptLogRecords").getJSONObject(0).getJSONObject("values").getJSONArray("scriptLogLine"));
assertEquals(2, jsonObject.getJSONArray("scriptLogRecords").getJSONObject(0).getJSONObject("values").getJSONArray("scriptLogLine").length());
assertEquals("line one", jsonObject.getJSONArray("scriptLogRecords").getJSONObject(0).getJSONObject("values").getJSONArray("scriptLogLine").getJSONObject(0).getJSONObject("values").getString("text"));
assertEquals("line two", jsonObject.getJSONArray("scriptLogRecords").getJSONObject(0).getJSONObject("values").getJSONArray("scriptLogLine").getJSONObject(1).getJSONObject("values").getString("text"));
}
}

View File

@ -0,0 +1,168 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.javalin;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import com.kingsrook.qqq.backend.core.actions.scripts.TestScriptActionInterface;
import com.kingsrook.qqq.backend.core.model.actions.scripts.ExecuteCodeInput;
import com.kingsrook.qqq.backend.core.model.actions.scripts.TestScriptInput;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
/*******************************************************************************
**
*******************************************************************************/
public class TestScriptAction implements TestScriptActionInterface
{
/*******************************************************************************
**
*******************************************************************************/
@Override
public void setupTestScriptInput(TestScriptInput testScriptInput, ExecuteCodeInput executeCodeInput)
{
SampleTestInput sampleTestInput = new SampleTestInput();
sampleTestInput.setName(ValueUtils.getValueAsString(testScriptInput.getInputValues().get("name")));
sampleTestInput.setAge(ValueUtils.getValueAsInteger(testScriptInput.getInputValues().get("age")));
executeCodeInput.setInput(Map.of("input", sampleTestInput));
executeCodeInput.getContext().put("output", new SampleTestOutput());
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public List<QFieldMetaData> getTestInputFields()
{
return List.of(
new QFieldMetaData("name", QFieldType.STRING),
new QFieldMetaData("age", QFieldType.INTEGER)
);
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public List<QFieldMetaData> getTestOutputFields()
{
return List.of(new QFieldMetaData("message", QFieldType.STRING));
}
/*******************************************************************************
**
*******************************************************************************/
public static class SampleTestInput implements Serializable
{
private String name;
private Integer age;
/*******************************************************************************
** Getter for name
**
*******************************************************************************/
public String getName()
{
return name;
}
/*******************************************************************************
** Setter for name
**
*******************************************************************************/
public void setName(String name)
{
this.name = name;
}
/*******************************************************************************
** Getter for age
**
*******************************************************************************/
public Integer getAge()
{
return age;
}
/*******************************************************************************
** Setter for age
**
*******************************************************************************/
public void setAge(Integer age)
{
this.age = age;
}
}
/*******************************************************************************
**
*******************************************************************************/
public static class SampleTestOutput implements Serializable
{
private String message;
/*******************************************************************************
** Getter for message
**
*******************************************************************************/
public String getMessage()
{
return message;
}
/*******************************************************************************
** Setter for message
**
*******************************************************************************/
public void setMessage(String message)
{
this.message = message;
}
}
}

View File

@ -227,7 +227,8 @@ public class TestUtils
.withField(new QFieldMetaData("testScriptId", QFieldType.INTEGER).withBackendName("test_script_id"))
.withAssociatedScript(new AssociatedScript()
.withFieldName("testScriptId")
.withScriptTypeId(1));
.withScriptTypeId(1)
.withScriptTester(new QCodeReference(TestScriptAction.class, QCodeUsage.SCRIPT_TESTER)));
}