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 a35ae4f5..8ee69e23 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 @@ -25,6 +25,7 @@ package com.kingsrook.qqq.backend.core.model.metadata.fields; import java.io.Serializable; import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.PossibleValueEnum; import com.kingsrook.qqq.backend.core.utils.Pair; +import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder; /******************************************************************************* @@ -105,14 +106,20 @@ public enum AdornmentType /******************************************************************************* ** *******************************************************************************/ - public interface SizeValues + public enum Size { - String WIDTH = "width"; - String XSMALL = "xsmall"; - String SMALL = "small"; - String MEDIUM = "medium"; - String LARGE = "large"; - String XLARGE = "xlarge"; + XSMALL, + SMALL, + MEDIUM, + LARGE, + XLARGE; + + + + public FieldAdornment toAdornment() + { + return (new FieldAdornment(AdornmentType.SIZE, MapBuilder.of("width", name().toLowerCase()))); + } } 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 8b7cefed..b2d51a95 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 @@ -62,6 +62,7 @@ import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwith import com.kingsrook.qqq.backend.core.processes.implementations.scripts.RunRecordScriptExtractStep; import com.kingsrook.qqq.backend.core.processes.implementations.scripts.RunRecordScriptLoadStep; import com.kingsrook.qqq.backend.core.processes.implementations.scripts.StoreScriptRevisionProcessStep; +import com.kingsrook.qqq.backend.core.processes.implementations.scripts.TestScriptProcessStep; /******************************************************************************* @@ -71,6 +72,7 @@ public class ScriptsMetaDataProvider { public static final String RUN_RECORD_SCRIPT_PROCESS_NAME = "runRecordScript"; public static final String STORE_SCRIPT_REVISION_PROCESS_NAME = "storeScriptRevision"; + public static final String TEST_SCRIPT_PROCESS_NAME = "testScript"; public static final String SCRIPT_TYPE_NAME_RECORD = "Record Script"; @@ -87,6 +89,7 @@ public class ScriptsMetaDataProvider defineStandardScriptsWidgets(instance); instance.addPossibleValueSource(TablesPossibleValueSourceMetaDataProvider.defineTablesPossibleValueSource(instance)); instance.addProcess(defineStoreScriptRevisionProcess()); + instance.addProcess(defineTestScriptProcess()); instance.addProcess(defineRunRecordScriptProcess()); } @@ -108,6 +111,22 @@ public class ScriptsMetaDataProvider + /******************************************************************************* + ** + *******************************************************************************/ + private QProcessMetaData defineTestScriptProcess() + { + return (new QProcessMetaData() + .withName(TEST_SCRIPT_PROCESS_NAME) + .withStepList(List.of( + new QBackendStepMetaData() + .withName("main") + .withCode(new QCodeReference(TestScriptProcessStep.class)) + ))); + } + + + /******************************************************************************* ** *******************************************************************************/ @@ -303,10 +322,15 @@ public class ScriptsMetaDataProvider *******************************************************************************/ private QTableMetaData defineScriptTable(String backendName) throws QException { - return (defineStandardTable(backendName, Script.TABLE_NAME, Script.class) + QTableMetaData tableMetaData = defineStandardTable(backendName, Script.TABLE_NAME, Script.class) .withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "name", "scriptTypeId", "tableName", "currentScriptRevisionId"))) .withSection(new QFieldSection("contents", new QIcon().withName("data_object"), Tier.T2).withWidgetName("scriptViewer")) - .withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate")))); + .withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate"))); + + tableMetaData.getField("name").withFieldAdornment(AdornmentType.Size.LARGE.toAdornment()); + tableMetaData.getField("currentScriptRevisionId").withFieldAdornment(AdornmentType.Size.LARGE.toAdornment()); + + return (tableMetaData); } @@ -342,6 +366,8 @@ public class ScriptsMetaDataProvider .withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate"))); tableMetaData.getField("contents").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR)); + tableMetaData.getField("scriptId").withFieldAdornment(AdornmentType.Size.LARGE.toAdornment()); + return (tableMetaData); } @@ -352,14 +378,21 @@ public class ScriptsMetaDataProvider *******************************************************************************/ private QTableMetaData defineScriptLogTable(String backendName) throws QException { - return (defineStandardTable(backendName, ScriptLog.TABLE_NAME, ScriptLog.class) + QTableMetaData tableMetaData = defineStandardTable(backendName, ScriptLog.TABLE_NAME, ScriptLog.class) .withRecordLabelFields(List.of("id")) .withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id"))) .withSection(new QFieldSection("script", new QIcon().withName("data_object"), Tier.T2, List.of("scriptId", "scriptRevisionId"))) .withSection(new QFieldSection("timing", new QIcon().withName("schedule"), Tier.T2, List.of("startTimestamp", "endTimestamp", "runTimeMillis", "createDate", "modifyDate"))) .withSection(new QFieldSection("error", "Error", new QIcon().withName("error_outline"), Tier.T2, List.of("hadError", "error"))) .withSection(new QFieldSection("inputOutput", "Input/Output", new QIcon().withName("chat"), Tier.T2, List.of("input", "output"))) - .withSection(new QFieldSection("lines", new QIcon().withName("horizontal_rule"), Tier.T2).withWidgetName(QJoinMetaData.makeInferredJoinName(ScriptLog.TABLE_NAME, ScriptLogLine.TABLE_NAME)))); + .withSection(new QFieldSection("lines", new QIcon().withName("horizontal_rule"), Tier.T2).withWidgetName(QJoinMetaData.makeInferredJoinName(ScriptLog.TABLE_NAME, ScriptLogLine.TABLE_NAME))); + + tableMetaData.getField("scriptId").withFieldAdornment(AdornmentType.Size.LARGE.toAdornment()); + tableMetaData.getField("scriptRevisionId").withFieldAdornment(AdornmentType.Size.LARGE.toAdornment()); + tableMetaData.getField("input").withFieldAdornment(AdornmentType.Size.LARGE.toAdornment()); + tableMetaData.getField("output").withFieldAdornment(AdornmentType.Size.LARGE.toAdornment()); + + return (tableMetaData); } @@ -369,8 +402,12 @@ public class ScriptsMetaDataProvider *******************************************************************************/ private QTableMetaData defineScriptLogLineTable(String backendName) throws QException { - return (defineStandardTable(backendName, ScriptLogLine.TABLE_NAME, ScriptLogLine.class) - .withRecordLabelFields(List.of("id"))); + QTableMetaData tableMetaData = defineStandardTable(backendName, ScriptLogLine.TABLE_NAME, ScriptLogLine.class) + .withRecordLabelFields(List.of("id")); + + tableMetaData.getField("text").withFieldAdornment(AdornmentType.Size.XLARGE.toAdornment()); + + return (tableMetaData); } } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/scripts/TestScriptProcessStep.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/scripts/TestScriptProcessStep.java new file mode 100644 index 00000000..98a32e96 --- /dev/null +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/scripts/TestScriptProcessStep.java @@ -0,0 +1,176 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2023. 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.processes.implementations.scripts; + + +import java.util.ArrayList; +import com.kingsrook.qqq.backend.core.actions.ActionHelper; +import com.kingsrook.qqq.backend.core.actions.processes.BackendStep; +import com.kingsrook.qqq.backend.core.actions.scripts.RunAdHocRecordScriptAction; +import com.kingsrook.qqq.backend.core.actions.scripts.logging.BuildScriptLogAndScriptLogLineExecutionLogger; +import com.kingsrook.qqq.backend.core.actions.tables.GetAction; +import com.kingsrook.qqq.backend.core.actions.tables.QueryAction; +import com.kingsrook.qqq.backend.core.context.QContext; +import com.kingsrook.qqq.backend.core.exceptions.QException; +import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput; +import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput; +import com.kingsrook.qqq.backend.core.model.actions.scripts.RunAdHocRecordScriptInput; +import com.kingsrook.qqq.backend.core.model.actions.scripts.RunAdHocRecordScriptOutput; +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.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.AdHocScriptCodeReference; +import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; +import com.kingsrook.qqq.backend.core.model.scripts.Script; +import com.kingsrook.qqq.backend.core.model.scripts.ScriptRevision; +import com.kingsrook.qqq.backend.core.model.scripts.ScriptType; +import com.kingsrook.qqq.backend.core.model.scripts.ScriptsMetaDataProvider; +import com.kingsrook.qqq.backend.core.utils.CollectionUtils; +import com.kingsrook.qqq.backend.core.utils.StringUtils; + + +/******************************************************************************* + ** Action to test a script! + ** + *******************************************************************************/ +public class TestScriptProcessStep implements BackendStep +{ + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public void run(RunBackendStepInput input, RunBackendStepOutput output) throws QException + { + try + { + ActionHelper.validateSession(input); + + //////////////// + // get inputs // + //////////////// + Integer scriptId = input.getValueInteger("scriptId"); + String code = input.getValueString("code"); + + ScriptRevision scriptRevision = new ScriptRevision(); + scriptRevision.setScriptId(scriptId); + scriptRevision.setContents(code); + + BuildScriptLogAndScriptLogLineExecutionLogger executionLogger = new BuildScriptLogAndScriptLogLineExecutionLogger(null, null); + + ///////////////////////////////////////////////////////////////// + // lookup the script - figure out how to proceed based on type // + ///////////////////////////////////////////////////////////////// + QRecord script = getScript(scriptId); + String scriptTypeName = getScriptTypeName(script); + + if(ScriptsMetaDataProvider.SCRIPT_TYPE_NAME_RECORD.equals(scriptTypeName)) + { + String tableName = script.getValueString("tableName"); + QTableMetaData table = QContext.getQInstance().getTable(tableName); + if(table == null) + { + throw (new QException("Could not find table [" + tableName + "] for script")); + } + + String recordPrimaryKeyList = input.getValueString("recordPrimaryKeyList"); + if(!StringUtils.hasContent(recordPrimaryKeyList)) + { + throw (new QException("Record primary key list was not given.")); + } + + QueryInput queryInput = new QueryInput(); + queryInput.setTableName(tableName); + queryInput.setFilter(new QQueryFilter(new QFilterCriteria(table.getPrimaryKeyField(), QCriteriaOperator.IN, recordPrimaryKeyList.split(",")))); + QueryOutput queryOutput = new QueryAction().execute(queryInput); + if(CollectionUtils.nullSafeIsEmpty(queryOutput.getRecords())) + { + throw (new QException("No records were found by the given primary keys.")); + } + + RunAdHocRecordScriptInput runAdHocRecordScriptInput = new RunAdHocRecordScriptInput(); + runAdHocRecordScriptInput.setRecordList(queryOutput.getRecords()); + runAdHocRecordScriptInput.setLogger(executionLogger); + runAdHocRecordScriptInput.setCodeReference(new AdHocScriptCodeReference().withScriptRevisionRecord(scriptRevision.toQRecord())); + RunAdHocRecordScriptOutput runAdHocRecordScriptOutput = new RunAdHocRecordScriptOutput(); + new RunAdHocRecordScriptAction().run(runAdHocRecordScriptInput, runAdHocRecordScriptOutput); + } + else + { + throw new QException("This process does not know how to test a script of type: " + scriptTypeName); + } + + output.addValue("scriptLogLines", new ArrayList<>(executionLogger.getScriptLogLines())); + } + catch(Exception e) + { + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // is this the kind of exception meant here? or is it more for one thrown by the script execution? or are those the same?? // + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + output.addValue("exception", e); + } + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private QRecord getScript(Integer scriptId) throws QException + { + GetInput getScriptInput = new GetInput(); + getScriptInput.setTableName(Script.TABLE_NAME); + getScriptInput.setPrimaryKey(scriptId); + GetOutput getScriptOutput = new GetAction().execute(getScriptInput); + if(getScriptOutput.getRecord() == null) + { + throw (new QException("Script was not found by id " + scriptId)); + } + + return (getScriptOutput.getRecord()); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private String getScriptTypeName(QRecord script) throws QException + { + GetInput getScriptTypeInput = new GetInput(); + getScriptTypeInput.setTableName(ScriptType.TABLE_NAME); + getScriptTypeInput.setPrimaryKey(script.getValueInteger("scriptTypeId")); + GetOutput getScriptTypeOutput = new GetAction().execute(getScriptTypeInput); + if(getScriptTypeOutput.getRecord() == null) + { + throw (new QException("Script Type was not found for script " + script.getValue("id"))); + } + + return (getScriptTypeOutput.getRecord().getValueString("name")); + } + +}