From a5ec33b51b880c6c18cafe153958338c719ca929 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Thu, 10 Nov 2022 14:21:31 -0600 Subject: [PATCH] Script Tests and further enhancements --- .../actions/scripts/TestScriptAction.java | 65 ---- .../scripts/TestScriptActionInterface.java | 109 ++++++ .../core/instances/QInstanceEnricher.java | 2 +- .../actions/scripts/TestScriptInput.java | 99 +---- .../actions/scripts/TestScriptOutput.java | 102 ++++++ .../core/model/metadata/code/QCodeUsage.java | 3 +- .../model/metadata/fields/AdornmentType.java | 16 + .../metadata/tables/AssociatedScript.java | 40 +- .../backend/core/model/scripts/Script.java | 6 +- .../backend/core/model/scripts/ScriptLog.java | 6 +- .../core/model/scripts/ScriptLogLine.java | 6 +- .../core/model/scripts/ScriptRevision.java | 6 +- .../core/model/scripts/ScriptType.java | 6 +- .../scripts/ScriptsMetaDataProvider.java | 3 +- .../etl/basic/BasicETLExtractFunction.java | 6 +- .../etl/basic/BasicETLLoadFunction.java | 3 +- .../etl/basic/BasicETLTransformFunction.java | 6 +- .../TestScriptActionInterfaceTest.java | 222 ++++++++++++ .../actions/scripts/TestScriptActionTest.java | 52 --- .../basic/BasicETLCleanupSourceFilesStep.java | 4 +- qqq-middleware-javalin/pom.xml | 6 + .../javalin/QJavalinImplementation.java | 224 +----------- .../javalin/QJavalinProcessHandler.java | 4 +- .../javalin/QJavalinScriptsHandler.java | 341 ++++++++++++++++++ .../javalin/QJavalinImplementationTest.java | 145 -------- .../javalin/QJavalinScriptsHandlerTest.java | 187 ++++++++++ .../qqq/backend/javalin/TestScriptAction.java | 168 +++++++++ .../qqq/backend/javalin/TestUtils.java | 3 +- 28 files changed, 1239 insertions(+), 601 deletions(-) delete mode 100644 qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/scripts/TestScriptAction.java create mode 100644 qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/scripts/TestScriptActionInterface.java create mode 100644 qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/scripts/TestScriptActionInterfaceTest.java delete mode 100644 qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/scripts/TestScriptActionTest.java create mode 100644 qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinScriptsHandler.java create mode 100644 qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/backend/javalin/QJavalinScriptsHandlerTest.java create mode 100644 qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/backend/javalin/TestScriptAction.java diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/scripts/TestScriptAction.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/scripts/TestScriptAction.java deleted file mode 100644 index 7e9e1e5d..00000000 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/scripts/TestScriptAction.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 . - */ - -package com.kingsrook.qqq.backend.core.actions.scripts; - - -import java.util.HashMap; -import com.kingsrook.qqq.backend.core.actions.scripts.logging.BuildScriptLogAndScriptLogLineExecutionLogger; -import com.kingsrook.qqq.backend.core.exceptions.QException; -import com.kingsrook.qqq.backend.core.model.actions.scripts.ExecuteCodeInput; -import com.kingsrook.qqq.backend.core.model.actions.scripts.ExecuteCodeOutput; -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.metadata.code.QCodeReference; -import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType; -import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; - - -/******************************************************************************* - ** Class for running a test of a script - e.g., maybe before it is saved. - *******************************************************************************/ -public class TestScriptAction -{ - - /******************************************************************************* - ** - *******************************************************************************/ - public void run(TestScriptInput input, TestScriptOutput output) throws QException - { - QTableMetaData table = input.getTable(); - - ExecuteCodeInput executeCodeInput = new ExecuteCodeInput(input.getInstance()); - executeCodeInput.setSession(input.getSession()); - executeCodeInput.setInput(new HashMap<>(input.getInputValues())); - executeCodeInput.setContext(new HashMap<>()); - // todo! if(input.getOutputObject() != null) - // todo! { - // todo! executeCodeInput.getContext().put("output", input.getOutputObject()); - // todo! } - executeCodeInput.setCodeReference(new QCodeReference().withInlineCode(input.getCode()).withCodeType(QCodeType.JAVA_SCRIPT)); // todo - code type as attribute of script!! - executeCodeInput.setExecutionLogger(new BuildScriptLogAndScriptLogLineExecutionLogger()); - ExecuteCodeOutput executeCodeOutput = new ExecuteCodeOutput(); - new ExecuteCodeAction().run(executeCodeInput, executeCodeOutput); - - // todo! output.setOutput(executeCodeOutput.getOutput()); - } -} diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/scripts/TestScriptActionInterface.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/scripts/TestScriptActionInterface.java new file mode 100644 index 00000000..d2cf509f --- /dev/null +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/scripts/TestScriptActionInterface.java @@ -0,0 +1,109 @@ +/* + * 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 . + */ + +package com.kingsrook.qqq.backend.core.actions.scripts; + + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import com.kingsrook.qqq.backend.core.actions.scripts.logging.BuildScriptLogAndScriptLogLineExecutionLogger; +import com.kingsrook.qqq.backend.core.exceptions.QException; +import com.kingsrook.qqq.backend.core.model.actions.scripts.ExecuteCodeInput; +import com.kingsrook.qqq.backend.core.model.actions.scripts.ExecuteCodeOutput; +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.metadata.fields.QFieldMetaData; + + +/******************************************************************************* + ** Interface to be implemented by script-running actions, if they want to allow + ** themselves to be used for user-testing of their script. + *******************************************************************************/ +public interface TestScriptActionInterface +{ + /******************************************************************************* + ** Called to adapt or translate data from the TestScriptInput (which would just + ** have a map of name-value pairs) to the actual input object(s) used by the script. + ** + ** Note - such a method may want or need to put an "output" object into the + ** executeCodeInput's context map. + *******************************************************************************/ + void setupTestScriptInput(TestScriptInput testScriptInput, ExecuteCodeInput executeCodeInput); + + + /******************************************************************************* + ** Called to adapt or translate the output object of the script execution to + ** something suitable for returning to the caller. + ** + ** Default implementation may always be suitable? + *******************************************************************************/ + default Serializable processTestScriptOutput(ExecuteCodeOutput executeCodeOutput) + { + return (executeCodeOutput.getOutput()); + } + + + /******************************************************************************* + ** Define the list of input fields for testing the script. The names of these + ** fields will end up as keys in the setupTestScriptInput method's testScriptInput object. + *******************************************************************************/ + List getTestInputFields(); + + + /******************************************************************************* + ** Define the list of output fields when testing the script. The output object + ** returned from processTestScriptOutput should have keys that match these field names. + *******************************************************************************/ + List getTestOutputFields(); + + /******************************************************************************* + ** Execute a test script. + *******************************************************************************/ + default void execute(TestScriptInput input, TestScriptOutput output) throws QException + { + ExecuteCodeInput executeCodeInput = new ExecuteCodeInput(input.getInstance()); + executeCodeInput.setSession(input.getSession()); + executeCodeInput.setContext(new HashMap<>()); + + executeCodeInput.setCodeReference(input.getCodeReference()); + BuildScriptLogAndScriptLogLineExecutionLogger executionLogger = new BuildScriptLogAndScriptLogLineExecutionLogger(null, null); + executeCodeInput.setExecutionLogger(executionLogger); + + setupTestScriptInput(input, executeCodeInput); + + ExecuteCodeOutput executeCodeOutput = new ExecuteCodeOutput(); + + try + { + new ExecuteCodeAction().run(executeCodeInput, executeCodeOutput); + output.setOutputObject(processTestScriptOutput(executeCodeOutput)); + } + catch(Exception e) + { + output.setException(e); + } + + output.setScriptLog(executionLogger.getScriptLog()); + output.setScriptLogLines(executionLogger.getScriptLogLines()); + } + +} diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceEnricher.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceEnricher.java index 078c7843..98631e8b 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceEnricher.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceEnricher.java @@ -230,7 +230,7 @@ public class QInstanceEnricher /******************************************************************************* ** *******************************************************************************/ - private void enrichField(QFieldMetaData field) + public void enrichField(QFieldMetaData field) { if(!StringUtils.hasContent(field.getLabel())) { diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/scripts/TestScriptInput.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/scripts/TestScriptInput.java index 33871f38..4021e27b 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/scripts/TestScriptInput.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/scripts/TestScriptInput.java @@ -26,6 +26,7 @@ import java.io.Serializable; import java.util.Map; import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput; import com.kingsrook.qqq.backend.core.model.metadata.QInstance; +import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference; /******************************************************************************* @@ -33,10 +34,8 @@ import com.kingsrook.qqq.backend.core.model.metadata.QInstance; *******************************************************************************/ public class TestScriptInput extends AbstractTableActionInput { - private Serializable recordPrimaryKey; - private String code; - private Serializable scriptTypeId; - private Map inputValues; + private Map inputValues; + private QCodeReference codeReference; @@ -50,45 +49,11 @@ public class TestScriptInput extends AbstractTableActionInput - /******************************************************************************* - ** Getter for recordPrimaryKey - ** - *******************************************************************************/ - public Serializable getRecordPrimaryKey() - { - return recordPrimaryKey; - } - - - - /******************************************************************************* - ** Setter for recordPrimaryKey - ** - *******************************************************************************/ - public void setRecordPrimaryKey(Serializable recordPrimaryKey) - { - this.recordPrimaryKey = recordPrimaryKey; - } - - - - /******************************************************************************* - ** Fluent setter for recordPrimaryKey - ** - *******************************************************************************/ - public TestScriptInput withRecordPrimaryKey(Serializable recordPrimaryKey) - { - this.recordPrimaryKey = recordPrimaryKey; - return (this); - } - - - /******************************************************************************* ** Getter for inputValues ** *******************************************************************************/ - public Map getInputValues() + public Map getInputValues() { return inputValues; } @@ -99,7 +64,7 @@ public class TestScriptInput extends AbstractTableActionInput ** Setter for inputValues ** *******************************************************************************/ - public void setInputValues(Map inputValues) + public void setInputValues(Map inputValues) { this.inputValues = inputValues; } @@ -110,7 +75,7 @@ public class TestScriptInput extends AbstractTableActionInput ** Fluent setter for inputValues ** *******************************************************************************/ - public TestScriptInput withInputValues(Map inputValues) + public TestScriptInput withInputValues(Map inputValues) { this.inputValues = inputValues; return (this); @@ -119,68 +84,34 @@ public class TestScriptInput extends AbstractTableActionInput /******************************************************************************* - ** Getter for code + ** Getter for codeReference ** *******************************************************************************/ - public String getCode() + public QCodeReference getCodeReference() { - return code; + return codeReference; } /******************************************************************************* - ** Setter for code + ** Setter for codeReference ** *******************************************************************************/ - public void setCode(String code) + public void setCodeReference(QCodeReference codeReference) { - this.code = code; + this.codeReference = codeReference; } /******************************************************************************* - ** Fluent setter for code + ** Fluent setter for codeReference ** *******************************************************************************/ - public TestScriptInput withCode(String code) + public TestScriptInput withCodeReference(QCodeReference codeReference) { - this.code = code; - return (this); - } - - - - /******************************************************************************* - ** Getter for scriptTypeId - ** - *******************************************************************************/ - public Serializable getScriptTypeId() - { - return scriptTypeId; - } - - - - /******************************************************************************* - ** Setter for scriptTypeId - ** - *******************************************************************************/ - public void setScriptTypeId(Serializable scriptTypeId) - { - this.scriptTypeId = scriptTypeId; - } - - - - /******************************************************************************* - ** Fluent setter for scriptTypeId - ** - *******************************************************************************/ - public TestScriptInput withScriptTypeId(Serializable scriptTypeId) - { - this.scriptTypeId = scriptTypeId; + this.codeReference = codeReference; return (this); } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/scripts/TestScriptOutput.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/scripts/TestScriptOutput.java index d6da9222..e9641dc6 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/scripts/TestScriptOutput.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/scripts/TestScriptOutput.java @@ -22,7 +22,10 @@ package com.kingsrook.qqq.backend.core.model.actions.scripts; +import java.io.Serializable; +import java.util.List; import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput; +import com.kingsrook.qqq.backend.core.model.data.QRecord; /******************************************************************************* @@ -30,4 +33,103 @@ import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput; *******************************************************************************/ public class TestScriptOutput extends AbstractActionOutput { + private Serializable outputObject; + private Exception exception; + private QRecord scriptLog; + private List scriptLogLines; + + + + /******************************************************************************* + ** Getter for outputObject + ** + *******************************************************************************/ + public Serializable getOutputObject() + { + return outputObject; + } + + + + /******************************************************************************* + ** Setter for outputObject + ** + *******************************************************************************/ + public void setOutputObject(Serializable outputObject) + { + this.outputObject = outputObject; + } + + + + /******************************************************************************* + ** Fluent setter for outputObject + ** + *******************************************************************************/ + public TestScriptOutput withOutputObject(Serializable outputObject) + { + this.outputObject = outputObject; + return (this); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public void setException(Exception exception) + { + this.exception = exception; + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public Exception getException() + { + return exception; + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public void setScriptLog(QRecord scriptLog) + { + this.scriptLog = scriptLog; + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public QRecord getScriptLog() + { + return scriptLog; + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public void setScriptLogLines(List scriptLogLines) + { + this.scriptLogLines = scriptLogLines; + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public List getScriptLogLines() + { + return scriptLogLines; + } + } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/code/QCodeUsage.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/code/QCodeUsage.java index ce4509be..1020dedb 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/code/QCodeUsage.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/code/QCodeUsage.java @@ -32,5 +32,6 @@ public enum QCodeUsage CUSTOMIZER, // a function to customize part of a QQQ table's behavior POSSIBLE_VALUE_PROVIDER, // code that drives a custom possibleValueSource RECORD_AUTOMATION_HANDLER, // code that executes record automations - REPORT_STATIC_DATA_SUPPLIER // code that supplies static data to a report + REPORT_STATIC_DATA_SUPPLIER, // code that supplies static data to a report + SCRIPT_TESTER // class that is used to test scripts. } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/fields/AdornmentType.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/fields/AdornmentType.java index ad5f3c53..00a3c5d8 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/fields/AdornmentType.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/fields/AdornmentType.java @@ -98,4 +98,20 @@ public enum AdornmentType String XLARGE = "xlarge"; } + + + /******************************************************************************* + ** + *******************************************************************************/ + public interface CodeEditorValues + { + /******************************************************************************* + ** + *******************************************************************************/ + static Pair languageMode(String languageMode) + { + return (new Pair<>("languageMode", languageMode)); + } + } + } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/tables/AssociatedScript.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/tables/AssociatedScript.java index 1645e470..3a8766c3 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/tables/AssociatedScript.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/tables/AssociatedScript.java @@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.core.model.metadata.tables; import java.io.Serializable; +import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference; /******************************************************************************* @@ -30,8 +31,9 @@ import java.io.Serializable; *******************************************************************************/ public class AssociatedScript implements Serializable { - private String fieldName; - private Serializable scriptTypeId; + private String fieldName; + private Serializable scriptTypeId; + private QCodeReference scriptTester; @@ -101,4 +103,38 @@ public class AssociatedScript implements Serializable return (this); } + + + /******************************************************************************* + ** Getter for scriptTester + ** + *******************************************************************************/ + public QCodeReference getScriptTester() + { + return scriptTester; + } + + + + /******************************************************************************* + ** Setter for scriptTester + ** + *******************************************************************************/ + public void setScriptTester(QCodeReference scriptTester) + { + this.scriptTester = scriptTester; + } + + + + /******************************************************************************* + ** Fluent setter for scriptTester + ** + *******************************************************************************/ + public AssociatedScript withScriptTester(QCodeReference scriptTester) + { + this.scriptTester = scriptTester; + return (this); + } + } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/Script.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/Script.java index 42fb6fe9..232938d3 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/Script.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/Script.java @@ -36,13 +36,13 @@ public class Script extends QRecordEntity { public static final String TABLE_NAME = "script"; - @QField() + @QField(isEditable = false) private Integer id; - @QField() + @QField(isEditable = false) private Instant createDate; - @QField() + @QField(isEditable = false) private Instant modifyDate; @QField() diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptLog.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptLog.java index 5949f326..71cbf7e5 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptLog.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptLog.java @@ -35,13 +35,13 @@ public class ScriptLog extends QRecordEntity { public static final String TABLE_NAME = "scriptLog"; - @QField() + @QField(isEditable = false) private Integer id; - @QField() + @QField(isEditable = false) private Instant createDate; - @QField() + @QField(isEditable = false) private Instant modifyDate; @QField(possibleValueSourceName = "script") diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptLogLine.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptLogLine.java index f878e91b..d9d7f19b 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptLogLine.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptLogLine.java @@ -34,13 +34,13 @@ public class ScriptLogLine extends QRecordEntity { public static final String TABLE_NAME = "scriptLogLine"; - @QField() + @QField(isEditable = false) private Integer id; - @QField() + @QField(isEditable = false) private Instant createDate; - @QField() + @QField(isEditable = false) private Instant modifyDate; @QField(possibleValueSourceName = "scriptLog") diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptRevision.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptRevision.java index ff8f0d26..0169c561 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptRevision.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptRevision.java @@ -36,13 +36,13 @@ public class ScriptRevision extends QRecordEntity { public static final String TABLE_NAME = "scriptRevision"; - @QField() + @QField(isEditable = false) private Integer id; - @QField() + @QField(isEditable = false) private Instant createDate; - @QField() + @QField(isEditable = false) private Instant modifyDate; @QField(possibleValueSourceName = "script") diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptType.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptType.java index 6b50d26e..e2fe2354 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptType.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptType.java @@ -34,13 +34,13 @@ public class ScriptType extends QRecordEntity { public static final String TABLE_NAME = "scriptType"; - @QField() + @QField(isEditable = false) private Integer id; - @QField() + @QField(isEditable = false) private Instant createDate; - @QField() + @QField(isEditable = false) private Instant modifyDate; @QField() diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptsMetaDataProvider.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptsMetaDataProvider.java index 39c2249d..ac671f61 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptsMetaDataProvider.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptsMetaDataProvider.java @@ -163,7 +163,8 @@ public class ScriptsMetaDataProvider .withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "name"))) .withSection(new QFieldSection("details", new QIcon().withName("dataset"), Tier.T2, List.of("helpText", "sampleCode"))) .withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate"))); - tableMetaData.getField("sampleCode").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR)); + tableMetaData.getField("sampleCode").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR).withValue(AdornmentType.CodeEditorValues.languageMode("javascript"))); + tableMetaData.getField("helpText").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR).withValue(AdornmentType.CodeEditorValues.languageMode("text"))); return (tableMetaData); } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/etl/basic/BasicETLExtractFunction.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/etl/basic/BasicETLExtractFunction.java index 9b0d0687..70317044 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/etl/basic/BasicETLExtractFunction.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/etl/basic/BasicETLExtractFunction.java @@ -52,7 +52,7 @@ public class BasicETLExtractFunction implements BackendStep public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException { String tableName = runBackendStepInput.getValueString(BasicETLProcess.FIELD_SOURCE_TABLE); - LOG.info("Start query on table: " + tableName); + LOG.debug("Start query on table: " + tableName); QueryInput queryInput = new QueryInput(runBackendStepInput.getInstance()); queryInput.setSession(runBackendStepInput.getSession()); @@ -70,7 +70,7 @@ public class BasicETLExtractFunction implements BackendStep ////////////////////////////////////////////////////////////////////// // if the caller gave us a record pipe, pass it to the query action // ////////////////////////////////////////////////////////////////////// - if (recordPipe != null) + if(recordPipe != null) { queryInput.setRecordPipe(recordPipe); } @@ -78,7 +78,7 @@ public class BasicETLExtractFunction implements BackendStep QueryAction queryAction = new QueryAction(); QueryOutput queryOutput = queryAction.execute(queryInput); - if (recordPipe == null) + if(recordPipe == null) { //////////////////////////////////////////////////////////////////////////// // only return the records (and log about them) if there's no record pipe // diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/etl/basic/BasicETLLoadFunction.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/etl/basic/BasicETLLoadFunction.java index d0a6c77b..68dbb512 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/etl/basic/BasicETLLoadFunction.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/etl/basic/BasicETLLoadFunction.java @@ -60,13 +60,14 @@ public class BasicETLLoadFunction implements BackendStep // exit early with no-op if no records made it here // ////////////////////////////////////////////////////// List inputRecords = runBackendStepInput.getRecords(); - LOG.info("Received [" + inputRecords.size() + "] records to load"); if(CollectionUtils.nullSafeIsEmpty(inputRecords)) { runBackendStepOutput.addValue(BasicETLProcess.FIELD_RECORD_COUNT, 0); return; } + LOG.info("Received [" + inputRecords.size() + "] records to load"); + ////////////////////////////////////////////////////////////////// // put the destination table name in all records being inserted // ////////////////////////////////////////////////////////////////// diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/etl/basic/BasicETLTransformFunction.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/etl/basic/BasicETLTransformFunction.java index 42e90231..6ef8f44d 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/etl/basic/BasicETLTransformFunction.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/etl/basic/BasicETLTransformFunction.java @@ -59,21 +59,21 @@ public class BasicETLTransformFunction implements BackendStep public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException { String tableName = runBackendStepInput.getValueString(BasicETLProcess.FIELD_DESTINATION_TABLE); - LOG.info("Start transform for destination table: " + tableName); + LOG.debug("Start transform for destination table: " + tableName); //////////////////////////////////////////////////////////////////////////////////////////// // exit early with no-op if no records made it here, or if we don't have a mapping to use // //////////////////////////////////////////////////////////////////////////////////////////// if(CollectionUtils.nullSafeIsEmpty(runBackendStepInput.getRecords())) { - LOG.info("Exiting early with no-op for empty input record list."); + LOG.debug("Exiting early with no-op for empty input record list."); return; } String mappingJSON = runBackendStepInput.getValueString(BasicETLProcess.FIELD_MAPPING_JSON); if(!StringUtils.hasContent(mappingJSON)) { - LOG.info("Exiting early with no-op for empty mappingJSON."); + LOG.debug("Exiting early with no-op for empty mappingJSON."); return; } diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/scripts/TestScriptActionInterfaceTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/scripts/TestScriptActionInterfaceTest.java new file mode 100644 index 00000000..28627695 --- /dev/null +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/scripts/TestScriptActionInterfaceTest.java @@ -0,0 +1,222 @@ +/* + * 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 . + */ + +package com.kingsrook.qqq.backend.core.actions.scripts; + + +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import com.kingsrook.qqq.backend.core.exceptions.QException; +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.actions.scripts.TestScriptOutput; +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.QCodeUsage; +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.model.session.QSession; +import com.kingsrook.qqq.backend.core.utils.TestUtils; +import com.kingsrook.qqq.backend.core.utils.ValueUtils; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + + +/******************************************************************************* + ** Unit test for TestScriptActionInterface + *******************************************************************************/ +class TestScriptActionInterfaceTest +{ + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void test() throws QException + { + QInstance qInstance = TestUtils.defineInstance(); + + TestScriptInput testScriptInput = new TestScriptInput(qInstance); + testScriptInput.setSession(new QSession()); + testScriptInput.setInputValues(Map.of("name", "Darin", "age", 42)); + testScriptInput.setCodeReference(new QCodeReference(SampleScript.class, QCodeUsage.CUSTOMIZER)); + TestScriptOutput testScriptOutput = new TestScriptOutput(); + new SampleTestAction().execute(testScriptInput, testScriptOutput); + + assertNull(testScriptOutput.getException()); + assertTrue(testScriptOutput.getOutputObject() instanceof SampleTestOutput); + SampleTestOutput sampleTestOutput = (SampleTestOutput) testScriptOutput.getOutputObject(); + assertEquals("Darin is 42 years old!", sampleTestOutput.getMessage()); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public static class SampleScript implements Function, Serializable> + { + @Override + public Serializable apply(Map context) + { + SampleTestInput input = (SampleTestInput) context.get("input"); + SampleTestOutput output = (SampleTestOutput) context.get("output"); + output.setMessage(input.getName() + " is " + input.getAge() + " years old!"); + return (output); + } + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public static class SampleTestAction 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 getTestInputFields() + { + return List.of( + new QFieldMetaData("name", QFieldType.STRING), + new QFieldMetaData("age", QFieldType.INTEGER) + ); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public List 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; + } + } + +} \ No newline at end of file diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/scripts/TestScriptActionTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/scripts/TestScriptActionTest.java deleted file mode 100644 index 7afad2de..00000000 --- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/scripts/TestScriptActionTest.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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 . - */ - -package com.kingsrook.qqq.backend.core.actions.scripts; - - -import com.kingsrook.qqq.backend.core.exceptions.QException; -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.metadata.QInstance; -import com.kingsrook.qqq.backend.core.utils.TestUtils; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - - -/******************************************************************************* - ** Unit test for TestScriptAction - *******************************************************************************/ -class TestScriptActionTest -{ - /******************************************************************************* - ** - *******************************************************************************/ - @Test - @Disabled("Not yet done.") - void test() throws QException - { - QInstance instance = TestUtils.defineInstance(); - TestScriptInput input = new TestScriptInput(instance); - TestScriptOutput output = new TestScriptOutput(); - new TestScriptAction().run(input, output); - } - -} \ No newline at end of file diff --git a/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/processes/implementations/etl/basic/BasicETLCleanupSourceFilesStep.java b/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/processes/implementations/etl/basic/BasicETLCleanupSourceFilesStep.java index 7add637a..c6fea88a 100644 --- a/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/processes/implementations/etl/basic/BasicETLCleanupSourceFilesStep.java +++ b/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/processes/implementations/etl/basic/BasicETLCleanupSourceFilesStep.java @@ -57,7 +57,7 @@ public class BasicETLCleanupSourceFilesStep implements BackendStep public static final String FIELD_MOVE_OR_DELETE = "moveOrDelete"; public static final String FIELD_DESTINATION_FOR_MOVES = "destinationForMoves"; - public static final String VALUE_MOVE = "move"; + public static final String VALUE_MOVE = "move"; public static final String VALUE_DELETE = "delete"; public static final String STEP_NAME = "cleanupSourceFiles"; @@ -84,7 +84,7 @@ public class BasicETLCleanupSourceFilesStep implements BackendStep String sourceFilePaths = runBackendStepInput.getValueString(BasicETLCollectSourceFileNamesStep.FIELD_SOURCE_FILE_PATHS); if(!StringUtils.hasContent(sourceFilePaths)) { - LOG.info("No source file paths were specified in field [" + BasicETLCollectSourceFileNamesStep.FIELD_SOURCE_FILE_PATHS + "]"); + LOG.debug("No source file paths were specified in field [" + BasicETLCollectSourceFileNamesStep.FIELD_SOURCE_FILE_PATHS + "]"); return; } diff --git a/qqq-middleware-javalin/pom.xml b/qqq-middleware-javalin/pom.xml index 9de49afb..efef7d1e 100644 --- a/qqq-middleware-javalin/pom.xml +++ b/qqq-middleware-javalin/pom.xml @@ -49,6 +49,12 @@ ${revision} test + + com.kingsrook.qqq + qqq-language-support-javascript + ${revision} + test + diff --git a/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementation.java b/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementation.java index fd520a25..4fe8bef2 100644 --- a/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementation.java +++ b/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementation.java @@ -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 rs = new HashMap<>(); - rs.put("record", record); - - ArrayList> associatedScripts = new ArrayList<>(); - rs.put("associatedScripts", associatedScripts); - - /////////////////////////////////////////////////////// - // process each associated script type for the table // - /////////////////////////////////////////////////////// - for(AssociatedScript associatedScript : CollectionUtils.nonNullList(table.getAssociatedScripts())) - { - HashMap 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 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 inputValues = new HashMap<>(); - input.setInputValues(inputValues); - - for(Map.Entry> 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); - } - } - - - /******************************************************************************* ** *******************************************************************************/ diff --git a/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinProcessHandler.java b/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinProcessHandler.java index 165455a3..4bb8f71b 100644 --- a/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinProcessHandler.java +++ b/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinProcessHandler.java @@ -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); diff --git a/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinScriptsHandler.java b/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinScriptsHandler.java new file mode 100644 index 00000000..d8a32404 --- /dev/null +++ b/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinScriptsHandler.java @@ -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 . + */ + +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 rs = new HashMap<>(); + rs.put("record", record); + + ArrayList> associatedScripts = new ArrayList<>(); + rs.put("associatedScripts", associatedScripts); + + Map 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 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 fields) + { + ArrayList 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 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 inputValues = new HashMap<>(); + input.setInputValues(inputValues); + + String tableName = context.pathParam("table"); + String fieldName = context.pathParam("fieldName"); + QTableMetaData table = QJavalinImplementation.qInstance.getTable(tableName); + + Optional 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> 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); + } + } +} diff --git a/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementationTest.java b/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementationTest.java index df3bb744..19d971f8 100644 --- a/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementationTest.java +++ b/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementationTest.java @@ -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 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 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 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 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")); - } - } diff --git a/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/backend/javalin/QJavalinScriptsHandlerTest.java b/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/backend/javalin/QJavalinScriptsHandlerTest.java new file mode 100644 index 00000000..5f11b2e3 --- /dev/null +++ b/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/backend/javalin/QJavalinScriptsHandlerTest.java @@ -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 . + */ + +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 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 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 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 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")); + } + +} \ No newline at end of file diff --git a/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/backend/javalin/TestScriptAction.java b/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/backend/javalin/TestScriptAction.java new file mode 100644 index 00000000..75925675 --- /dev/null +++ b/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/backend/javalin/TestScriptAction.java @@ -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 . + */ + +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 getTestInputFields() + { + return List.of( + new QFieldMetaData("name", QFieldType.STRING), + new QFieldMetaData("age", QFieldType.INTEGER) + ); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public List 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; + } + } + +} diff --git a/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/backend/javalin/TestUtils.java b/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/backend/javalin/TestUtils.java index 89da7217..9a313b9d 100644 --- a/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/backend/javalin/TestUtils.java +++ b/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/backend/javalin/TestUtils.java @@ -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))); }