Switch to store all script contents in scriptRevisionFile sub-table; make test interface for all scripts work the same

This commit is contained in:
2023-06-26 20:18:57 -05:00
parent 6bc543fff7
commit b53d1823df
11 changed files with 456 additions and 137 deletions

View File

@ -23,22 +23,33 @@ package com.kingsrook.qqq.backend.core.actions.scripts;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import com.kingsrook.qqq.backend.core.actions.scripts.logging.Log4jCodeExecutionLogger; import com.kingsrook.qqq.backend.core.actions.scripts.logging.Log4jCodeExecutionLogger;
import com.kingsrook.qqq.backend.core.actions.scripts.logging.QCodeExecutionLoggerInterface; import com.kingsrook.qqq.backend.core.actions.scripts.logging.QCodeExecutionLoggerInterface;
import com.kingsrook.qqq.backend.core.actions.scripts.logging.ScriptExecutionLoggerInterface; import com.kingsrook.qqq.backend.core.actions.scripts.logging.ScriptExecutionLoggerInterface;
import com.kingsrook.qqq.backend.core.actions.scripts.logging.StoreScriptLogAndScriptLogLineExecutionLogger; import com.kingsrook.qqq.backend.core.actions.scripts.logging.StoreScriptLogAndScriptLogLineExecutionLogger;
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
import com.kingsrook.qqq.backend.core.exceptions.QCodeException; import com.kingsrook.qqq.backend.core.exceptions.QCodeException;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.logging.QLogger; import com.kingsrook.qqq.backend.core.logging.QLogger;
import com.kingsrook.qqq.backend.core.model.actions.scripts.AbstractRunScriptInput; import com.kingsrook.qqq.backend.core.model.actions.scripts.AbstractRunScriptInput;
import com.kingsrook.qqq.backend.core.model.actions.scripts.ExecuteCodeInput; 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.ExecuteCodeOutput;
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.QCodeReference; 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.code.QCodeType;
import com.kingsrook.qqq.backend.core.model.scripts.ScriptRevision; import com.kingsrook.qqq.backend.core.model.scripts.ScriptRevision;
import com.kingsrook.qqq.backend.core.model.scripts.ScriptRevisionFile;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.StringUtils; import com.kingsrook.qqq.backend.core.utils.StringUtils;
@ -133,7 +144,17 @@ public class ExecuteCodeAction
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public static ExecuteCodeInput setupExecuteCodeInput(AbstractRunScriptInput<?> input, ScriptRevision scriptRevision) public static ExecuteCodeInput setupExecuteCodeInput(AbstractRunScriptInput<?> input, ScriptRevision scriptRevision) throws QException
{
return setupExecuteCodeInput(input, scriptRevision, null);
}
/*******************************************************************************
**
*******************************************************************************/
public static ExecuteCodeInput setupExecuteCodeInput(AbstractRunScriptInput<?> input, ScriptRevision scriptRevision, String fileName) throws QException
{ {
ExecuteCodeInput executeCodeInput = new ExecuteCodeInput(); ExecuteCodeInput executeCodeInput = new ExecuteCodeInput();
executeCodeInput.setInput(new HashMap<>(Objects.requireNonNullElseGet(input.getInputValues(), HashMap::new))); executeCodeInput.setInput(new HashMap<>(Objects.requireNonNullElseGet(input.getInputValues(), HashMap::new)));
@ -150,7 +171,49 @@ public class ExecuteCodeAction
context.put("scriptUtils", input.getScriptUtils()); context.put("scriptUtils", input.getScriptUtils());
} }
executeCodeInput.setCodeReference(new QCodeReference().withInlineCode(scriptRevision.getContents()).withCodeType(QCodeType.JAVA_SCRIPT)); // todo - code type as attribute of script!! if(CollectionUtils.nullSafeIsEmpty(scriptRevision.getFiles()))
{
QueryInput queryInput = new QueryInput();
queryInput.setTableName(ScriptRevisionFile.TABLE_NAME);
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("scriptRevisionId", QCriteriaOperator.EQUALS, scriptRevision.getId())));
QueryOutput queryOutput = new QueryAction().execute(queryInput);
scriptRevision.setFiles(new ArrayList<>());
for(QRecord record : queryOutput.getRecords())
{
scriptRevision.getFiles().add(new ScriptRevisionFile(record));
}
}
List<ScriptRevisionFile> files = scriptRevision.getFiles();
if(files == null || files.isEmpty())
{
throw (new QException("Script Revision " + scriptRevision.getId() + " had more than 1 associated ScriptRevisionFile (and the name to use was not specified)."));
}
else
{
String contents = null;
if(fileName == null || files.size() == 1)
{
contents = files.get(0).getContents();
}
else
{
for(ScriptRevisionFile file : files)
{
if(file.getFileName().equals(fileName))
{
contents = file.getContents();
}
}
if(contents == null)
{
throw (new QException("Could not find file named " + fileName + " for Script Revision " + scriptRevision.getId()));
}
}
executeCodeInput.setCodeReference(new QCodeReference().withInlineCode(contents).withCodeType(QCodeType.JAVA_SCRIPT)); // todo - code type as attribute of script!!
}
ExecuteCodeAction.addApiUtilityToContext(context, scriptRevision); ExecuteCodeAction.addApiUtilityToContext(context, scriptRevision);
context.put("qqq", new QqqScriptUtils()); context.put("qqq", new QqqScriptUtils());

View File

@ -0,0 +1,71 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.actions.scripts;
import java.util.Collections;
import java.util.List;
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.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
/*******************************************************************************
**
*******************************************************************************/
public class RecordScriptTestInterface implements TestScriptActionInterface
{
/*******************************************************************************
**
*******************************************************************************/
@Override
public void setupTestScriptInput(TestScriptInput testScriptInput, ExecuteCodeInput executeCodeInput) throws QException
{
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public List<QFieldMetaData> getTestInputFields()
{
return (List.of(new QFieldMetaData("recordPrimaryKeyList", QFieldType.STRING).withLabel("Record Primary Key List")));
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public List<QFieldMetaData> getTestOutputFields()
{
return (Collections.emptyList());
}
}

View File

@ -110,6 +110,7 @@ public class RunAssociatedScriptAction
GetInput getInput = new GetInput(); GetInput getInput = new GetInput();
getInput.setTableName("scriptRevision"); getInput.setTableName("scriptRevision");
getInput.setPrimaryKey(scriptRevisionId); getInput.setPrimaryKey(scriptRevisionId);
getInput.setIncludeAssociations(true);
GetOutput getOutput = new GetAction().execute(getInput); GetOutput getOutput = new GetAction().execute(getInput);
if(getOutput.getRecord() == null) if(getOutput.getRecord() == null)
{ {

View File

@ -23,7 +23,9 @@ package com.kingsrook.qqq.backend.core.model.scripts;
import java.time.Instant; import java.time.Instant;
import java.util.List;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.data.QAssociation;
import com.kingsrook.qqq.backend.core.model.data.QField; import com.kingsrook.qqq.backend.core.model.data.QField;
import com.kingsrook.qqq.backend.core.model.data.QRecord; import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity; import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
@ -55,9 +57,6 @@ public class ScriptRevision extends QRecordEntity
@QField(possibleValueSourceName = "apiName", label = "API Name") @QField(possibleValueSourceName = "apiName", label = "API Name")
private String apiName; private String apiName;
@QField()
private String contents;
@QField() @QField()
private Integer sequenceNo; private Integer sequenceNo;
@ -67,6 +66,9 @@ public class ScriptRevision extends QRecordEntity
@QField(maxLength = 100, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS) @QField(maxLength = 100, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
private String author; private String author;
@QAssociation(name = "files")
private List<ScriptRevisionFile> files;
/******************************************************************************* /*******************************************************************************
@ -226,40 +228,6 @@ public class ScriptRevision extends QRecordEntity
/*******************************************************************************
** Getter for contents
**
*******************************************************************************/
public String getContents()
{
return contents;
}
/*******************************************************************************
** Setter for contents
**
*******************************************************************************/
public void setContents(String contents)
{
this.contents = contents;
}
/*******************************************************************************
** Fluent setter for contents
**
*******************************************************************************/
public ScriptRevision withContents(String contents)
{
this.contents = contents;
return (this);
}
/******************************************************************************* /*******************************************************************************
** Getter for sequenceNo ** Getter for sequenceNo
** **
@ -422,4 +390,35 @@ public class ScriptRevision extends QRecordEntity
return (this); return (this);
} }
/*******************************************************************************
** Getter for files
*******************************************************************************/
public List<ScriptRevisionFile> getFiles()
{
return (this.files);
}
/*******************************************************************************
** Setter for files
*******************************************************************************/
public void setFiles(List<ScriptRevisionFile> files)
{
this.files = files;
}
/*******************************************************************************
** Fluent setter for files
*******************************************************************************/
public ScriptRevision withFiles(List<ScriptRevisionFile> files)
{
this.files = files;
return (this);
}
} }

View File

@ -24,6 +24,7 @@ package com.kingsrook.qqq.backend.core.model.scripts;
import java.time.Instant; import java.time.Instant;
import com.kingsrook.qqq.backend.core.model.data.QField; import com.kingsrook.qqq.backend.core.model.data.QField;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity; import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
@ -55,6 +56,30 @@ public class ScriptType extends QRecordEntity
@QField(possibleValueSourceName = ScriptTypeFileMode.NAME) @QField(possibleValueSourceName = ScriptTypeFileMode.NAME)
private Integer fileMode; private Integer fileMode;
@QField()
private String testScriptInterfaceName;
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public ScriptType()
{
}
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public ScriptType(QRecord qRecord)
{
populateFromQRecord(qRecord);
}
/******************************************************************************* /*******************************************************************************
@ -290,4 +315,35 @@ public class ScriptType extends QRecordEntity
return (this); return (this);
} }
/*******************************************************************************
** Getter for testScriptInterfaceName
*******************************************************************************/
public String getTestScriptInterfaceName()
{
return (this.testScriptInterfaceName);
}
/*******************************************************************************
** Setter for testScriptInterfaceName
*******************************************************************************/
public void setTestScriptInterfaceName(String testScriptInterfaceName)
{
this.testScriptInterfaceName = testScriptInterfaceName;
}
/*******************************************************************************
** Fluent setter for testScriptInterfaceName
*******************************************************************************/
public ScriptType withTestScriptInterfaceName(String testScriptInterfaceName)
{
this.testScriptInterfaceName = testScriptInterfaceName;
return (this);
}
} }

View File

@ -54,12 +54,14 @@ import com.kingsrook.qqq.backend.core.model.metadata.processes.QComponentType;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendComponentMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendComponentMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.Association;
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability; import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection; import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.TablesPossibleValueSourceMetaDataProvider; import com.kingsrook.qqq.backend.core.model.metadata.tables.TablesPossibleValueSourceMetaDataProvider;
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier; import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.StreamedETLWithFrontendProcess; import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.StreamedETLWithFrontendProcess;
import com.kingsrook.qqq.backend.core.processes.implementations.scripts.LoadScriptTestDetailsProcessStep;
import com.kingsrook.qqq.backend.core.processes.implementations.scripts.RunRecordScriptExtractStep; 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.RunRecordScriptLoadStep;
import com.kingsrook.qqq.backend.core.processes.implementations.scripts.RunRecordScriptTransformStep; import com.kingsrook.qqq.backend.core.processes.implementations.scripts.RunRecordScriptTransformStep;
@ -75,6 +77,7 @@ public class ScriptsMetaDataProvider
public static final String RUN_RECORD_SCRIPT_PROCESS_NAME = "runRecordScript"; public static final String RUN_RECORD_SCRIPT_PROCESS_NAME = "runRecordScript";
public static final String STORE_SCRIPT_REVISION_PROCESS_NAME = "storeScriptRevision"; public static final String STORE_SCRIPT_REVISION_PROCESS_NAME = "storeScriptRevision";
public static final String TEST_SCRIPT_PROCESS_NAME = "testScript"; public static final String TEST_SCRIPT_PROCESS_NAME = "testScript";
public static final String LOAD_SCRIPT_TEST_DETAILS_PROCESS_NAME = "loadScriptTestDetails";
public static final String SCRIPT_TYPE_NAME_RECORD = "Record Script"; public static final String SCRIPT_TYPE_NAME_RECORD = "Record Script";
@ -94,11 +97,30 @@ public class ScriptsMetaDataProvider
instance.addPossibleValueSource(TablesPossibleValueSourceMetaDataProvider.defineTablesPossibleValueSource(instance)); instance.addPossibleValueSource(TablesPossibleValueSourceMetaDataProvider.defineTablesPossibleValueSource(instance));
instance.addProcess(defineStoreScriptRevisionProcess()); instance.addProcess(defineStoreScriptRevisionProcess());
instance.addProcess(defineTestScriptProcess()); instance.addProcess(defineTestScriptProcess());
instance.addProcess(defineLoadScriptTestDetailsProcess());
instance.addProcess(defineRunRecordScriptProcess()); instance.addProcess(defineRunRecordScriptProcess());
} }
/*******************************************************************************
**
*******************************************************************************/
private QProcessMetaData defineLoadScriptTestDetailsProcess()
{
return (new QProcessMetaData()
.withName(LOAD_SCRIPT_TEST_DETAILS_PROCESS_NAME)
.withTableName(Script.TABLE_NAME)
.withIsHidden(true)
//? .withPermissionRules(new QPermissionRules().withLevel(PermissionLevel.NOT_PROTECTED))
.withStepList(List.of(
new QBackendStepMetaData()
.withName("main")
.withCode(new QCodeReference(LoadScriptTestDetailsProcessStep.class)))));
}
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
@ -404,7 +426,7 @@ public class ScriptsMetaDataProvider
{ {
QTableMetaData tableMetaData = defineStandardTable(backendName, ScriptType.TABLE_NAME, ScriptType.class) QTableMetaData tableMetaData = defineStandardTable(backendName, ScriptType.TABLE_NAME, ScriptType.class)
.withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "name"))) .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", "fileMode"))) .withSection(new QFieldSection("details", new QIcon().withName("dataset"), Tier.T2, List.of("helpText", "sampleCode", "fileMode", "testScriptInterfaceName")))
.withSection(new QFieldSection("files", new QIcon().withName("description"), Tier.T2).withWidgetName(QJoinMetaData.makeInferredJoinName(ScriptType.TABLE_NAME, ScriptTypeFileSchema.TABLE_NAME))) .withSection(new QFieldSection("files", new QIcon().withName("description"), Tier.T2).withWidgetName(QJoinMetaData.makeInferredJoinName(ScriptType.TABLE_NAME, ScriptTypeFileSchema.TABLE_NAME)))
.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("sampleCode").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR).withValue(AdornmentType.CodeEditorValues.languageMode("javascript"))); tableMetaData.getField("sampleCode").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR).withValue(AdornmentType.CodeEditorValues.languageMode("javascript")));
@ -440,12 +462,15 @@ public class ScriptsMetaDataProvider
.withRecordLabelFormat("%s v%s") .withRecordLabelFormat("%s v%s")
.withRecordLabelFields(List.of("scriptId", "sequenceNo")) .withRecordLabelFields(List.of("scriptId", "sequenceNo"))
.withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "scriptId", "sequenceNo"))) .withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "scriptId", "sequenceNo")))
.withSection(new QFieldSection("code", new QIcon().withName("data_object"), Tier.T2, List.of("contents")))
.withSection(new QFieldSection("files", new QIcon().withName("description"), Tier.T2).withWidgetName(QJoinMetaData.makeInferredJoinName(ScriptRevision.TABLE_NAME, ScriptRevisionFile.TABLE_NAME))) .withSection(new QFieldSection("files", new QIcon().withName("description"), Tier.T2).withWidgetName(QJoinMetaData.makeInferredJoinName(ScriptRevision.TABLE_NAME, ScriptRevisionFile.TABLE_NAME)))
.withSection(new QFieldSection("changeManagement", new QIcon().withName("history"), Tier.T2, List.of("commitMessage", "author"))) .withSection(new QFieldSection("changeManagement", new QIcon().withName("history"), Tier.T2, List.of("commitMessage", "author")))
.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")))
.withAssociation(new Association()
.withName("files")
.withAssociatedTableName(ScriptRevisionFile.TABLE_NAME)
.withJoinName(QJoinMetaData.makeInferredJoinName(ScriptRevision.TABLE_NAME, ScriptRevisionFile.TABLE_NAME)));
tableMetaData.getField("contents").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR).withValue(AdornmentType.CodeEditorValues.languageMode("javascript")));
tableMetaData.getField("scriptId").withFieldAdornment(AdornmentType.Size.LARGE.toAdornment()); tableMetaData.getField("scriptId").withFieldAdornment(AdornmentType.Size.LARGE.toAdornment());
try try

View File

@ -0,0 +1,96 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
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.customizers.QCodeLoader;
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
import com.kingsrook.qqq.backend.core.actions.scripts.TestScriptActionInterface;
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.instances.QInstanceEnricher;
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.tables.get.GetInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.scripts.ScriptType;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
/*******************************************************************************
** Action to load the details necessary to test a script.
**
*******************************************************************************/
public class LoadScriptTestDetailsProcessStep implements BackendStep
{
/*******************************************************************************
**
*******************************************************************************/
@Override
public void run(RunBackendStepInput input, RunBackendStepOutput output) throws QException
{
try
{
ActionHelper.validateSession(input);
Integer scriptTypeId = input.getValueInteger("scriptTypeId");
GetInput getInput = new GetInput();
getInput.setTableName(ScriptType.TABLE_NAME);
getInput.setPrimaryKey(scriptTypeId);
GetOutput getOutput = new GetAction().execute(getInput);
ScriptType scriptType = new ScriptType(getOutput.getRecord());
TestScriptActionInterface testScriptActionInterface = QCodeLoader.getAdHoc(TestScriptActionInterface.class, new QCodeReference(scriptType.getTestScriptInterfaceName(), QCodeType.JAVA));
QInstanceEnricher qInstanceEnricher = new QInstanceEnricher(new QInstance());
ArrayList<QFieldMetaData> inputFields = new ArrayList<>();
for(QFieldMetaData testInputField : CollectionUtils.nonNullList(testScriptActionInterface.getTestInputFields()))
{
qInstanceEnricher.enrichField(testInputField);
inputFields.add(testInputField);
}
ArrayList<QFieldMetaData> outputFields = new ArrayList<>();
for(QFieldMetaData testOutputField : CollectionUtils.nonNullList(testScriptActionInterface.getTestOutputFields()))
{
qInstanceEnricher.enrichField(testOutputField);
outputFields.add(testOutputField);
}
output.addValue("testInputFields", inputFields);
output.addValue("testOutputFields", outputFields);
}
catch(Exception e)
{
output.addValue("exception", e);
}
}
}

View File

@ -22,6 +22,7 @@
package com.kingsrook.qqq.backend.core.processes.implementations.scripts; package com.kingsrook.qqq.backend.core.processes.implementations.scripts;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.kingsrook.qqq.backend.core.actions.ActionHelper; import com.kingsrook.qqq.backend.core.actions.ActionHelper;
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction; import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
@ -128,11 +129,6 @@ public class StoreScriptRevisionProcessStep implements BackendStep
.withValue("commitMessage", commitMessage) .withValue("commitMessage", commitMessage)
.withValue("sequenceNo", nextSequenceNo); .withValue("sequenceNo", nextSequenceNo);
if(input.getValue("contents") != null)
{
scriptRevision.withValue("contents", input.getValueString("contents"));
}
try try
{ {
scriptRevision.setValue("author", input.getSession().getUser().getFullName()); scriptRevision.setValue("author", input.getSession().getUser().getFullName());
@ -147,22 +143,28 @@ public class StoreScriptRevisionProcessStep implements BackendStep
scriptRevision = insertOutput.getRecords().get(0); scriptRevision = insertOutput.getRecords().get(0);
Integer scriptRevisionId = scriptRevision.getValueInteger("id"); Integer scriptRevisionId = scriptRevision.getValueInteger("id");
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////
// if there's a list of file contents (instead of just a single string), store them all // // Store the file(s) under the revision //
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////
@SuppressWarnings("unchecked") List<QRecord> scriptRevisionFileRecords = null;
List<QRecord> fileContents = (List<QRecord>) input.getValue("fileContents"); if(StringUtils.hasContent(input.getValueString("fileNames")))
if(CollectionUtils.nullSafeHasContents(fileContents))
{ {
List<QRecord> scriptRevisionRecords = fileContents.stream().map(r -> new ScriptRevisionFile() scriptRevisionFileRecords = new ArrayList<>();
for(String fileName : input.getValueString("fileNames").split(","))
{
scriptRevisionFileRecords.add(new ScriptRevisionFile()
.withScriptRevisionId(scriptRevisionId) .withScriptRevisionId(scriptRevisionId)
.withFileName(r.getValueString("fileName")) .withFileName(fileName)
.withContents(r.getValueString("contents")) .withContents(input.getValueString("fileContents:" + fileName))
.toQRecord()).toList(); .toQRecord());
}
}
if(CollectionUtils.nullSafeHasContents(scriptRevisionFileRecords))
{
InsertInput scriptRevisionFileInsertInput = new InsertInput(); InsertInput scriptRevisionFileInsertInput = new InsertInput();
scriptRevisionFileInsertInput.setTableName(ScriptRevisionFile.TABLE_NAME); scriptRevisionFileInsertInput.setTableName(ScriptRevisionFile.TABLE_NAME);
scriptRevisionFileInsertInput.setRecords(scriptRevisionRecords); scriptRevisionFileInsertInput.setRecords(scriptRevisionFileRecords);
scriptRevisionFileInsertInput.setTransaction(transaction); scriptRevisionFileInsertInput.setTransaction(transaction);
new InsertAction().execute(scriptRevisionFileInsertInput); new InsertAction().execute(scriptRevisionFileInsertInput);
} }

View File

@ -22,35 +22,33 @@
package com.kingsrook.qqq.backend.core.processes.implementations.scripts; package com.kingsrook.qqq.backend.core.processes.implementations.scripts;
import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import com.kingsrook.qqq.backend.core.actions.ActionHelper; import com.kingsrook.qqq.backend.core.actions.ActionHelper;
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep; 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.TestScriptActionInterface;
import com.kingsrook.qqq.backend.core.actions.scripts.logging.BuildScriptLogAndScriptLogLineExecutionLogger; 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.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.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput; 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.processes.RunBackendStepOutput;
import com.kingsrook.qqq.backend.core.model.actions.scripts.RunAdHocRecordScriptInput; import com.kingsrook.qqq.backend.core.model.actions.scripts.TestScriptInput;
import com.kingsrook.qqq.backend.core.model.actions.scripts.RunAdHocRecordScriptOutput; 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.GetInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput; 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.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.code.AdHocScriptCodeReference; 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.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType;
import com.kingsrook.qqq.backend.core.model.scripts.Script; 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.ScriptRevision;
import com.kingsrook.qqq.backend.core.model.scripts.ScriptRevisionFile;
import com.kingsrook.qqq.backend.core.model.scripts.ScriptType; 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; import com.kingsrook.qqq.backend.core.utils.StringUtils;
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
/******************************************************************************* /*******************************************************************************
@ -77,62 +75,60 @@ public class TestScriptProcessStep implements BackendStep
ScriptRevision scriptRevision = new ScriptRevision(); ScriptRevision scriptRevision = new ScriptRevision();
scriptRevision.setScriptId(scriptId); scriptRevision.setScriptId(scriptId);
scriptRevision.setContents(input.getValueString("code"));
ArrayList<ScriptRevisionFile> files = new ArrayList<>();
if(StringUtils.hasContent(input.getValueString("fileNames")))
{
for(String fileName : input.getValueString("fileNames").split(","))
{
files.add(new ScriptRevisionFile()
.withFileName(fileName)
.withContents(input.getValueString("fileContents:" + fileName)));
}
}
scriptRevision.setFiles(files);
scriptRevision.setApiName(input.getValueString("apiName")); scriptRevision.setApiName(input.getValueString("apiName"));
scriptRevision.setApiVersion(input.getValueString("apiVersion")); scriptRevision.setApiVersion(input.getValueString("apiVersion"));
AdHocScriptCodeReference adHocScriptCodeReference = new AdHocScriptCodeReference().withScriptRevisionRecord(scriptRevision.toQRecord());
adHocScriptCodeReference.setCodeType(QCodeType.JAVA_SCRIPT); // todo - load dynamically?
adHocScriptCodeReference.setInlineCode(scriptRevision.getFiles().get(0).getContents()); // todo - ugh.
BuildScriptLogAndScriptLogLineExecutionLogger executionLogger = new BuildScriptLogAndScriptLogLineExecutionLogger(null, null); BuildScriptLogAndScriptLogLineExecutionLogger executionLogger = new BuildScriptLogAndScriptLogLineExecutionLogger(null, null);
///////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////
// lookup the script - figure out how to proceed based on type // // load the script & its type & its test interface. //
///////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////
QRecord script = getScript(scriptId); QRecord script = getScript(scriptId);
String scriptTypeName = getScriptTypeName(script); Integer scriptTypeId = script.getValueInteger("scriptTypeId");
GetInput getInput = new GetInput();
getInput.setTableName(ScriptType.TABLE_NAME);
getInput.setPrimaryKey(scriptTypeId);
GetOutput getOutput = new GetAction().execute(getInput);
ScriptType scriptType = new ScriptType(getOutput.getRecord());
if(ScriptsMetaDataProvider.SCRIPT_TYPE_NAME_RECORD.equals(scriptTypeName)) TestScriptActionInterface testScriptActionInterface = QCodeLoader.getAdHoc(TestScriptActionInterface.class, new QCodeReference(scriptType.getTestScriptInterfaceName(), QCodeType.JAVA));
TestScriptInput testScriptInput = new TestScriptInput();
testScriptInput.setApiName(input.getValueString("apiName"));
testScriptInput.setApiVersion(input.getValueString("apiVersion"));
testScriptInput.setCodeReference(adHocScriptCodeReference);
Map<String, Serializable> inputValues = new HashMap<>();
testScriptInput.setInputValues(inputValues);
for(Map.Entry<String, Serializable> entry : input.getValues().entrySet())
{ {
String tableName = script.getValueString("tableName"); String key = entry.getKey();
QTableMetaData table = QContext.getQInstance().getTable(tableName); String value = ValueUtils.getValueAsString(entry.getValue());
if(table == null) inputValues.put(key, value);
{
throw (new QException("Could not find table [" + tableName + "] for script"));
} }
String recordPrimaryKeyList = input.getValueString("recordPrimaryKeyList"); TestScriptOutput testScriptOutput = new TestScriptOutput();
if(!StringUtils.hasContent(recordPrimaryKeyList)) testScriptActionInterface.execute(testScriptInput, testScriptOutput);
{
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(","))));
queryInput.setIncludeAssociations(true);
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.setTableName(tableName);
runAdHocRecordScriptInput.setCodeReference(new AdHocScriptCodeReference().withScriptRevisionRecord(scriptRevision.toQRecord()));
RunAdHocRecordScriptOutput runAdHocRecordScriptOutput = new RunAdHocRecordScriptOutput();
new RunAdHocRecordScriptAction().run(runAdHocRecordScriptInput, runAdHocRecordScriptOutput);
/////////////////////////////////////////////
// if there was an exception, send it back //
/////////////////////////////////////////////
runAdHocRecordScriptOutput.getException().ifPresent(e -> output.addValue("exception", e));
}
else
{
throw new QException("This process does not know how to test a script of type: " + scriptTypeName);
}
output.addValue("scriptLogLines", new ArrayList<>(executionLogger.getScriptLogLines())); output.addValue("scriptLogLines", new ArrayList<>(executionLogger.getScriptLogLines()));
output.addValue("outputObject", testScriptOutput.getOutputObject());
} }
catch(Exception e) catch(Exception e)
{ {

View File

@ -22,7 +22,6 @@
package com.kingsrook.qqq.backend.core.processes.implementations.scripts; package com.kingsrook.qqq.backend.core.processes.implementations.scripts;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.kingsrook.qqq.backend.core.BaseTest; import com.kingsrook.qqq.backend.core.BaseTest;
@ -68,7 +67,8 @@ class StoreScriptRevisionProcessStepTest extends BaseTest
new StoreScriptRevisionProcessStep().run(new RunBackendStepInput().withValues(MapBuilder.of( new StoreScriptRevisionProcessStep().run(new RunBackendStepInput().withValues(MapBuilder.of(
"scriptId", scriptId, "scriptId", scriptId,
"contents", scriptContents "fileNames", "script",
"fileContents:script", scriptContents
)), new RunBackendStepOutput()); )), new RunBackendStepOutput());
scripts = TestUtils.queryTable(Script.TABLE_NAME); scripts = TestUtils.queryTable(Script.TABLE_NAME);
@ -79,11 +79,16 @@ class StoreScriptRevisionProcessStepTest extends BaseTest
assertEquals(scriptId, scriptRevision.getValueInteger("scriptId")); assertEquals(scriptId, scriptRevision.getValueInteger("scriptId"));
assertEquals(1, scriptRevision.getValueInteger("sequenceNo")); assertEquals(1, scriptRevision.getValueInteger("sequenceNo"));
assertEquals("Initial version", scriptRevision.getValueString("commitMessage")); assertEquals("Initial version", scriptRevision.getValueString("commitMessage"));
assertEquals(scriptContents, scriptRevision.getValueString("contents"));
List<QRecord> scriptRevisionFiles = TestUtils.queryTable(ScriptRevisionFile.TABLE_NAME);
QRecord scriptRevisionFile = scriptRevisionFiles.get(0);
assertEquals(scriptContents, scriptRevisionFile.getValueString("contents"));
String updatedScriptContents = "logger.log('Really, Hi');";
new StoreScriptRevisionProcessStep().run(new RunBackendStepInput().withValues(MapBuilder.of( new StoreScriptRevisionProcessStep().run(new RunBackendStepInput().withValues(MapBuilder.of(
"scriptId", scriptId, "scriptId", scriptId,
"contents", scriptContents "fileNames", "script",
"fileContents:script", updatedScriptContents
)), new RunBackendStepOutput()); )), new RunBackendStepOutput());
scripts = TestUtils.queryTable(Script.TABLE_NAME); scripts = TestUtils.queryTable(Script.TABLE_NAME);
@ -91,10 +96,14 @@ class StoreScriptRevisionProcessStepTest extends BaseTest
scriptRevisions = TestUtils.queryTable(ScriptRevision.TABLE_NAME).stream().filter(r -> r.getValueInteger("id").equals(2)).collect(Collectors.toList()); scriptRevisions = TestUtils.queryTable(ScriptRevision.TABLE_NAME).stream().filter(r -> r.getValueInteger("id").equals(2)).collect(Collectors.toList());
scriptRevision = scriptRevisions.get(0); scriptRevision = scriptRevisions.get(0);
Integer newScriptRevisionId = scriptRevision.getValueInteger("id");
assertEquals(scriptId, scriptRevision.getValueInteger("scriptId")); assertEquals(scriptId, scriptRevision.getValueInteger("scriptId"));
assertEquals(2, scriptRevision.getValueInteger("sequenceNo")); assertEquals(2, scriptRevision.getValueInteger("sequenceNo"));
assertEquals("No commit message given", scriptRevision.getValueString("commitMessage")); assertEquals("No commit message given", scriptRevision.getValueString("commitMessage"));
assertEquals(scriptContents, scriptRevision.getValueString("contents"));
scriptRevisionFiles = TestUtils.queryTable(ScriptRevisionFile.TABLE_NAME);
scriptRevisionFile = scriptRevisionFiles.stream().filter(r -> r.getValueInteger("scriptRevisionId").equals(newScriptRevisionId)).findFirst().get();
assertEquals(updatedScriptContents, scriptRevisionFile.getValueString("contents"));
} }
@ -116,13 +125,11 @@ class StoreScriptRevisionProcessStepTest extends BaseTest
List<QRecord> scripts = TestUtils.queryTable(Script.TABLE_NAME); List<QRecord> scripts = TestUtils.queryTable(Script.TABLE_NAME);
assertNull(scripts.get(0).getValueInteger("currentScriptRevisionId")); assertNull(scripts.get(0).getValueInteger("currentScriptRevisionId"));
ArrayList<QRecord> fileContents = new ArrayList<>();
fileContents.add(new QRecord().withValue("fileName", "script").withValue("contents", scriptContents));
fileContents.add(new QRecord().withValue("fileName", "template").withValue("contents", templateContents));
RunBackendStepInput runBackendStepInput = new RunBackendStepInput(); RunBackendStepInput runBackendStepInput = new RunBackendStepInput();
runBackendStepInput.addValue("scriptId", scriptId); runBackendStepInput.addValue("scriptId", scriptId);
runBackendStepInput.addValue("fileContents", fileContents); runBackendStepInput.addValue("fileNames", "script,template");
runBackendStepInput.addValue("fileContents:script", scriptContents);
runBackendStepInput.addValue("fileContents:template", templateContents);
new StoreScriptRevisionProcessStep().run(runBackendStepInput, new RunBackendStepOutput()); new StoreScriptRevisionProcessStep().run(runBackendStepInput, new RunBackendStepOutput());
scripts = TestUtils.queryTable(Script.TABLE_NAME); scripts = TestUtils.queryTable(Script.TABLE_NAME);
@ -150,13 +157,11 @@ class StoreScriptRevisionProcessStepTest extends BaseTest
String updatedScriptContents = "logger.log('Really, Hi');"; String updatedScriptContents = "logger.log('Really, Hi');";
String updatedTemplateContents = "<h1>Hey, what's up</h1>"; String updatedTemplateContents = "<h1>Hey, what's up</h1>";
fileContents = new ArrayList<>();
fileContents.add(new QRecord().withValue("fileName", "script").withValue("contents", updatedScriptContents));
fileContents.add(new QRecord().withValue("fileName", "template").withValue("contents", updatedTemplateContents));
runBackendStepInput = new RunBackendStepInput(); runBackendStepInput = new RunBackendStepInput();
runBackendStepInput.addValue("scriptId", scriptId); runBackendStepInput.addValue("scriptId", scriptId);
runBackendStepInput.addValue("fileContents", fileContents); runBackendStepInput.addValue("fileNames", "script,template");
runBackendStepInput.addValue("fileContents:script", updatedScriptContents);
runBackendStepInput.addValue("fileContents:template", updatedTemplateContents);
runBackendStepInput.addValue("commitMessage", "Updated files"); runBackendStepInput.addValue("commitMessage", "Updated files");
new StoreScriptRevisionProcessStep().run(runBackendStepInput, new RunBackendStepOutput()); new StoreScriptRevisionProcessStep().run(runBackendStepInput, new RunBackendStepOutput());

View File

@ -855,6 +855,11 @@ public class QJavalinImplementation
getInput.setShouldTranslatePossibleValues(true); getInput.setShouldTranslatePossibleValues(true);
getInput.setShouldFetchHeavyFields(true); getInput.setShouldFetchHeavyFields(true);
if("true".equals(context.queryParam("includeAssociations")))
{
getInput.setIncludeAssociations(true);
}
PermissionsHelper.checkTablePermissionThrowing(getInput, TablePermissionSubType.READ); PermissionsHelper.checkTablePermissionThrowing(getInput, TablePermissionSubType.READ);
// todo - validate that the primary key is of the proper type (e.g,. not a string for an id field) // todo - validate that the primary key is of the proper type (e.g,. not a string for an id field)