More api name & version with scripts (eg, running test scripts)

This commit is contained in:
2023-04-30 19:56:41 -05:00
parent 9ce45934a8
commit 15acaec523
11 changed files with 243 additions and 22 deletions

View File

@ -0,0 +1,41 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.actions.scripts;
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.scripts.ScriptRevision;
/*******************************************************************************
**
*******************************************************************************/
public interface AssociatedScriptContextPrimerInterface
{
/*******************************************************************************
**
*******************************************************************************/
void primeContext(ExecuteCodeInput executeCodeInput, ScriptRevision scriptRevision) throws QException;
}

View File

@ -127,19 +127,21 @@ public class ExecuteCodeAction
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)));
executeCodeInput.setContext(new HashMap<>()); executeCodeInput.setContext(new HashMap<>());
Map<String, Serializable> context = executeCodeInput.getContext();
if(input.getOutputObject() != null) if(input.getOutputObject() != null)
{ {
executeCodeInput.getContext().put("output", input.getOutputObject()); context.put("output", input.getOutputObject());
} }
if(input.getScriptUtils() != null) if(input.getScriptUtils() != null)
{ {
executeCodeInput.getContext().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!! executeCodeInput.setCodeReference(new QCodeReference().withInlineCode(scriptRevision.getContents()).withCodeType(QCodeType.JAVA_SCRIPT)); // todo - code type as attribute of script!!
ExecuteCodeAction.addApiUtilityToContext(executeCodeInput.getContext(), scriptRevision); ExecuteCodeAction.addApiUtilityToContext(context, scriptRevision);
ExecuteCodeAction.setExecutionLoggerInExecuteCodeInput(input, scriptRevision, executeCodeInput); ExecuteCodeAction.setExecutionLoggerInExecuteCodeInput(input, scriptRevision, executeCodeInput);
return (executeCodeInput); return (executeCodeInput);
@ -152,7 +154,7 @@ public class ExecuteCodeAction
** module -- in case the runtime doesn't have that module deployed (e.g, not in ** module -- in case the runtime doesn't have that module deployed (e.g, not in
** the project pom). ** the project pom).
*******************************************************************************/ *******************************************************************************/
private static void addApiUtilityToContext(Map<String, Serializable> context, ScriptRevision scriptRevision) public static void addApiUtilityToContext(Map<String, Serializable> context, ScriptRevision scriptRevision)
{ {
if(!StringUtils.hasContent(scriptRevision.getApiName()) || !StringUtils.hasContent(scriptRevision.getApiVersion())) if(!StringUtils.hasContent(scriptRevision.getApiName()) || !StringUtils.hasContent(scriptRevision.getApiVersion()))
{ {

View File

@ -59,6 +59,11 @@ public class RunAssociatedScriptAction
ScriptRevision scriptRevision = getScriptRevision(input); ScriptRevision scriptRevision = getScriptRevision(input);
ExecuteCodeInput executeCodeInput = ExecuteCodeAction.setupExecuteCodeInput(input, scriptRevision); ExecuteCodeInput executeCodeInput = ExecuteCodeAction.setupExecuteCodeInput(input, scriptRevision);
if(input.getAssociatedScriptContextPrimerInterface() != null)
{
input.getAssociatedScriptContextPrimerInterface().primeContext(executeCodeInput, scriptRevision);
}
ExecuteCodeOutput executeCodeOutput = new ExecuteCodeOutput(); ExecuteCodeOutput executeCodeOutput = new ExecuteCodeOutput();
new ExecuteCodeAction().run(executeCodeInput, executeCodeOutput); new ExecuteCodeAction().run(executeCodeInput, executeCodeOutput);

View File

@ -184,6 +184,8 @@ public class StoreAssociatedScriptAction
QRecord scriptRevision = new QRecord() QRecord scriptRevision = new QRecord()
.withValue("scriptId", script.getValue("id")) .withValue("scriptId", script.getValue("id"))
.withValue("contents", input.getCode()) .withValue("contents", input.getCode())
.withValue("apiName", input.getApiName())
.withValue("apiVersion", input.getApiVersion())
.withValue("commitMessage", commitMessage) .withValue("commitMessage", commitMessage)
.withValue("sequenceNo", nextSequenceNo); .withValue("sequenceNo", nextSequenceNo);

View File

@ -32,6 +32,7 @@ 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.TestScriptInput;
import com.kingsrook.qqq.backend.core.model.actions.scripts.TestScriptOutput; import com.kingsrook.qqq.backend.core.model.actions.scripts.TestScriptOutput;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData; import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.scripts.ScriptRevision;
/******************************************************************************* /*******************************************************************************
@ -47,7 +48,7 @@ public interface TestScriptActionInterface
** Note - such a method may want or need to put an "output" object into the ** Note - such a method may want or need to put an "output" object into the
** executeCodeInput's context map. ** executeCodeInput's context map.
*******************************************************************************/ *******************************************************************************/
void setupTestScriptInput(TestScriptInput testScriptInput, ExecuteCodeInput executeCodeInput); void setupTestScriptInput(TestScriptInput testScriptInput, ExecuteCodeInput executeCodeInput) throws QException;
/******************************************************************************* /*******************************************************************************
@ -87,12 +88,21 @@ public interface TestScriptActionInterface
BuildScriptLogAndScriptLogLineExecutionLogger executionLogger = new BuildScriptLogAndScriptLogLineExecutionLogger(null, null); BuildScriptLogAndScriptLogLineExecutionLogger executionLogger = new BuildScriptLogAndScriptLogLineExecutionLogger(null, null);
executeCodeInput.setExecutionLogger(executionLogger); executeCodeInput.setExecutionLogger(executionLogger);
try
{
setupTestScriptInput(input, executeCodeInput); setupTestScriptInput(input, executeCodeInput);
ScriptRevision scriptRevision = new ScriptRevision().withApiName(input.getApiName()).withApiVersion(input.getApiVersion());
if(this instanceof AssociatedScriptContextPrimerInterface associatedScriptContextPrimerInterface)
{
associatedScriptContextPrimerInterface.primeContext(executeCodeInput, scriptRevision);
}
ExecuteCodeOutput executeCodeOutput = new ExecuteCodeOutput(); ExecuteCodeOutput executeCodeOutput = new ExecuteCodeOutput();
try ExecuteCodeAction.addApiUtilityToContext(executeCodeInput.getContext(), scriptRevision);
{
new ExecuteCodeAction().run(executeCodeInput, executeCodeOutput); new ExecuteCodeAction().run(executeCodeInput, executeCodeOutput);
output.setOutputObject(processTestScriptOutput(executeCodeOutput)); output.setOutputObject(processTestScriptOutput(executeCodeOutput));
} }

View File

@ -22,6 +22,7 @@
package com.kingsrook.qqq.backend.core.model.actions.scripts; package com.kingsrook.qqq.backend.core.model.actions.scripts;
import com.kingsrook.qqq.backend.core.actions.scripts.AssociatedScriptContextPrimerInterface;
import com.kingsrook.qqq.backend.core.model.metadata.code.AssociatedScriptCodeReference; import com.kingsrook.qqq.backend.core.model.metadata.code.AssociatedScriptCodeReference;
@ -30,5 +31,37 @@ import com.kingsrook.qqq.backend.core.model.metadata.code.AssociatedScriptCodeRe
*******************************************************************************/ *******************************************************************************/
public class RunAssociatedScriptInput extends AbstractRunScriptInput<AssociatedScriptCodeReference> public class RunAssociatedScriptInput extends AbstractRunScriptInput<AssociatedScriptCodeReference>
{ {
private AssociatedScriptContextPrimerInterface associatedScriptContextPrimerInterface;
/*******************************************************************************
** Getter for associatedScriptContextPrimerInterface
*******************************************************************************/
public AssociatedScriptContextPrimerInterface getAssociatedScriptContextPrimerInterface()
{
return (this.associatedScriptContextPrimerInterface);
}
/*******************************************************************************
** Setter for associatedScriptContextPrimerInterface
*******************************************************************************/
public void setAssociatedScriptContextPrimerInterface(AssociatedScriptContextPrimerInterface associatedScriptContextPrimerInterface)
{
this.associatedScriptContextPrimerInterface = associatedScriptContextPrimerInterface;
}
/*******************************************************************************
** Fluent setter for associatedScriptContextPrimerInterface
*******************************************************************************/
public RunAssociatedScriptInput withAssociatedScriptContextPrimerInterface(AssociatedScriptContextPrimerInterface associatedScriptContextPrimerInterface)
{
this.associatedScriptContextPrimerInterface = associatedScriptContextPrimerInterface;
return (this);
}
} }

View File

@ -35,6 +35,8 @@ public class StoreAssociatedScriptInput extends AbstractTableActionInput
private Serializable recordPrimaryKey; private Serializable recordPrimaryKey;
private String code; private String code;
private String apiName;
private String apiVersion;
private String commitMessage; private String commitMessage;
@ -183,4 +185,66 @@ public class StoreAssociatedScriptInput extends AbstractTableActionInput
return (this); return (this);
} }
/*******************************************************************************
** Getter for apiName
*******************************************************************************/
public String getApiName()
{
return (this.apiName);
}
/*******************************************************************************
** Setter for apiName
*******************************************************************************/
public void setApiName(String apiName)
{
this.apiName = apiName;
}
/*******************************************************************************
** Fluent setter for apiName
*******************************************************************************/
public StoreAssociatedScriptInput withApiName(String apiName)
{
this.apiName = apiName;
return (this);
}
/*******************************************************************************
** Getter for apiVersion
*******************************************************************************/
public String getApiVersion()
{
return (this.apiVersion);
}
/*******************************************************************************
** Setter for apiVersion
*******************************************************************************/
public void setApiVersion(String apiVersion)
{
this.apiVersion = apiVersion;
}
/*******************************************************************************
** Fluent setter for apiVersion
*******************************************************************************/
public StoreAssociatedScriptInput withApiVersion(String apiVersion)
{
this.apiVersion = apiVersion;
return (this);
}
} }

View File

@ -36,6 +36,9 @@ public class TestScriptInput extends AbstractTableActionInput
private Map<String, Serializable> inputValues; private Map<String, Serializable> inputValues;
private QCodeReference codeReference; private QCodeReference codeReference;
private String apiName;
private String apiVersion;
/******************************************************************************* /*******************************************************************************
@ -113,4 +116,66 @@ public class TestScriptInput extends AbstractTableActionInput
return (this); return (this);
} }
/*******************************************************************************
** Getter for apiName
*******************************************************************************/
public String getApiName()
{
return (this.apiName);
}
/*******************************************************************************
** Setter for apiName
*******************************************************************************/
public void setApiName(String apiName)
{
this.apiName = apiName;
}
/*******************************************************************************
** Fluent setter for apiName
*******************************************************************************/
public TestScriptInput withApiName(String apiName)
{
this.apiName = apiName;
return (this);
}
/*******************************************************************************
** Getter for apiVersion
*******************************************************************************/
public String getApiVersion()
{
return (this.apiVersion);
}
/*******************************************************************************
** Setter for apiVersion
*******************************************************************************/
public void setApiVersion(String apiVersion)
{
this.apiVersion = apiVersion;
}
/*******************************************************************************
** Fluent setter for apiVersion
*******************************************************************************/
public TestScriptInput withApiVersion(String apiVersion)
{
this.apiVersion = apiVersion;
return (this);
}
} }

View File

@ -150,12 +150,11 @@ public class ApiScriptUtils implements Serializable
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public Map<String, Serializable> query(String urlPart) throws QException public Map<String, Serializable> query(String tableName, String queryString) throws QException
{ {
validateApiNameAndVersion("query(" + urlPart + ")"); validateApiNameAndVersion("query(" + tableName + ")");
String[] urlParts = urlPart.split("\\?", 2); Map<String, List<String>> paramMap = parseQueryString(queryString);
Map<String, List<String>> paramMap = parseQueryString(urlParts.length > 1 ? urlParts[1] : null); return (ApiImplementation.query(getApiInstanceMetaData(), apiVersion, tableName, paramMap));
return (ApiImplementation.query(getApiInstanceMetaData(), apiVersion, urlParts[0], paramMap));
} }

View File

@ -102,13 +102,13 @@ class ApiScriptUtilsTest extends BaseTest
{ {
ApiScriptUtils apiScriptUtils = newDefaultApiScriptUtils(); ApiScriptUtils apiScriptUtils = newDefaultApiScriptUtils();
assertThatThrownBy(() -> apiScriptUtils.query(TestUtils.TABLE_NAME_PERSON + "?foo=bar")) assertThatThrownBy(() -> apiScriptUtils.query(TestUtils.TABLE_NAME_PERSON, "foo=bar"))
.isInstanceOf(QBadRequestException.class) .isInstanceOf(QBadRequestException.class)
.hasMessageContaining("Unrecognized filter criteria field: foo"); .hasMessageContaining("Unrecognized filter criteria field: foo");
insertSimpsons(); insertSimpsons();
Map<String, Serializable> result = apiScriptUtils.query(TestUtils.TABLE_NAME_PERSON + "?id=2"); Map<String, Serializable> result = apiScriptUtils.query(TestUtils.TABLE_NAME_PERSON, "id=2");
assertEquals(1, result.get("count")); assertEquals(1, result.get("count"));
assertEquals(1, ((List<?>) result.get("records")).size()); assertEquals(1, ((List<?>) result.get("records")).size());
assertEquals("Marge", ((Map<?, ?>) ((List<?>) result.get("records")).get(0)).get("firstName")); assertEquals("Marge", ((Map<?, ?>) ((List<?>) result.get("records")).get(0)).get("firstName"));

View File

@ -395,18 +395,18 @@ public class QJavalinScriptsHandler
String key = entry.getKey(); String key = entry.getKey();
String value = entry.getValue().get(0); String value = entry.getValue().get(0);
if(key.equals("code")) switch(key)
{ {
input.setCodeReference(new QCodeReference().withInlineCode(value).withCodeType(QCodeType.JAVA_SCRIPT)); case "code" -> input.setCodeReference(new QCodeReference().withInlineCode(value).withCodeType(QCodeType.JAVA_SCRIPT));
} case "apiName" -> input.setApiName(value);
else case "apiVersion" -> input.setApiVersion(value);
{ default -> inputValues.put(key, value);
inputValues.put(key, value);
} }
} }
TestScriptActionInterface scriptTester = QCodeLoader.getAdHoc(TestScriptActionInterface.class, scriptTesterCodeRef); TestScriptActionInterface scriptTester = QCodeLoader.getAdHoc(TestScriptActionInterface.class, scriptTesterCodeRef);
TestScriptOutput output = new TestScriptOutput(); TestScriptOutput output = new TestScriptOutput();
scriptTester.execute(input, output); scriptTester.execute(input, output);
QJavalinAccessLogger.logEndSuccess(); QJavalinAccessLogger.logEndSuccess();