mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 13:10:44 +00:00
Cleanup from code review
This commit is contained in:
@ -22,6 +22,7 @@
|
||||
package com.kingsrook.qqq.backend.core.actions.dashboard;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetOutput;
|
||||
@ -51,6 +52,8 @@ public class QuickSightChartRenderer extends AbstractWidgetRenderer
|
||||
@Override
|
||||
public RenderWidgetOutput render(RenderWidgetInput input) throws QException
|
||||
{
|
||||
ActionHelper.validateSession(input);
|
||||
|
||||
try
|
||||
{
|
||||
QuickSightChartMetaData quickSightMetaData = (QuickSightChartMetaData) input.getWidgetMetaData();
|
||||
|
@ -30,6 +30,7 @@ import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.frontend.AppTreeNode;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendAppMetaData;
|
||||
@ -69,8 +70,10 @@ public class MetaDataAction
|
||||
Map<String, QFrontendTableMetaData> tables = new LinkedHashMap<>();
|
||||
for(Map.Entry<String, QTableMetaData> entry : metaDataInput.getInstance().getTables().entrySet())
|
||||
{
|
||||
tables.put(entry.getKey(), new QFrontendTableMetaData(entry.getValue(), false));
|
||||
treeNodes.put(entry.getKey(), new AppTreeNode(entry.getValue()));
|
||||
String tableName = entry.getKey();
|
||||
QBackendMetaData backendForTable = metaDataInput.getInstance().getBackendForTable(tableName);
|
||||
tables.put(tableName, new QFrontendTableMetaData(backendForTable, entry.getValue(), false));
|
||||
treeNodes.put(tableName, new AppTreeNode(entry.getValue()));
|
||||
}
|
||||
metaDataOutput.setTables(tables);
|
||||
|
||||
|
@ -27,6 +27,7 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException;
|
||||
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.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
|
||||
@ -52,7 +53,8 @@ public class TableMetaDataAction
|
||||
{
|
||||
throw (new QNotFoundException("Table [" + tableMetaDataInput.getTableName() + "] was not found."));
|
||||
}
|
||||
tableMetaDataOutput.setTable(new QFrontendTableMetaData(table, true));
|
||||
QBackendMetaData backendForTable = tableMetaDataInput.getInstance().getBackendForTable(table.getName());
|
||||
tableMetaDataOutput.setTable(new QFrontendTableMetaData(backendForTable, table, true));
|
||||
|
||||
// todo post-customization - can do whatever w/ the result if you want
|
||||
|
||||
|
@ -23,6 +23,8 @@ package com.kingsrook.qqq.backend.core.actions.scripts;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
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.exceptions.QCodeException;
|
||||
@ -33,7 +35,17 @@ import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Action to execute user/runtime defined code.
|
||||
**
|
||||
** This action is designed to support code in multiple languages, by using
|
||||
** executors, e.g., provided by additional runtime qqq dependencies. Initially
|
||||
** we are building qqq-language-support-javascript.
|
||||
**
|
||||
** We also have a Java executor, to provide at least a little bit of testability
|
||||
** within qqq-backend-core. This executor is a candidate to be replaced in the
|
||||
** future with something that would do actual dynamic java (whether that's compiled
|
||||
** at runtime, or loaded from a plugin jar at runtime). In other words, the java
|
||||
** executor in place today is just meant to be a placeholder.
|
||||
*******************************************************************************/
|
||||
public class ExecuteCodeAction
|
||||
{
|
||||
@ -65,7 +77,20 @@ public class ExecuteCodeAction
|
||||
Class<? extends QCodeExecutor> executorClass = (Class<? extends QCodeExecutor>) Class.forName(languageExecutor);
|
||||
QCodeExecutor qCodeExecutor = executorClass.getConstructor().newInstance();
|
||||
|
||||
Serializable codeOutput = qCodeExecutor.execute(codeReference, input.getContext(), executionLogger);
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// merge all of the input context, plus the input... input - into a context for the code executor //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
Map<String, Serializable> context = new HashMap<>();
|
||||
if(input.getContext() != null)
|
||||
{
|
||||
context.putAll(input.getContext());
|
||||
}
|
||||
if(input.getInput() != null)
|
||||
{
|
||||
context.putAll(input.getInput());
|
||||
}
|
||||
|
||||
Serializable codeOutput = qCodeExecutor.execute(codeReference, context, executionLogger);
|
||||
output.setOutput(codeOutput);
|
||||
executionLogger.acceptExecutionEnd(codeOutput);
|
||||
}
|
||||
|
@ -30,7 +30,8 @@ import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Interface to be implemented by language-specific code executors, e.g., in
|
||||
** qqq-language-support-${languageName} maven modules.
|
||||
*******************************************************************************/
|
||||
public interface QCodeExecutor
|
||||
{
|
||||
@ -38,6 +39,6 @@ public interface QCodeExecutor
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
Serializable execute(QCodeReference codeReference, Map<String, Serializable> context, QCodeExecutionLoggerInterface executionLogger) throws QCodeException;
|
||||
Serializable execute(QCodeReference codeReference, Map<String, Serializable> inputContext, QCodeExecutionLoggerInterface executionLogger) throws QCodeException;
|
||||
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Java
|
||||
*******************************************************************************/
|
||||
public class QJavaExecutor implements QCodeExecutor
|
||||
{
|
||||
@ -42,9 +42,9 @@ public class QJavaExecutor implements QCodeExecutor
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public Serializable execute(QCodeReference codeReference, Map<String, Serializable> input, QCodeExecutionLoggerInterface executionLogger) throws QCodeException
|
||||
public Serializable execute(QCodeReference codeReference, Map<String, Serializable> inputContext, QCodeExecutionLoggerInterface executionLogger) throws QCodeException
|
||||
{
|
||||
Map<String, Object> context = new HashMap<>(input);
|
||||
Map<String, Object> context = new HashMap<>(inputContext);
|
||||
if(!context.containsKey("logger"))
|
||||
{
|
||||
context.put("logger", executionLogger);
|
||||
|
@ -24,9 +24,11 @@ package com.kingsrook.qqq.backend.core.actions.scripts;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.HeaderAndDetailTableCodeExecutionLogger;
|
||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.StoreScriptLogAndScriptLogLineExecutionLogger;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException;
|
||||
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.RunAssociatedScriptInput;
|
||||
@ -50,15 +52,34 @@ public class RunAssociatedScriptAction
|
||||
*******************************************************************************/
|
||||
public void run(RunAssociatedScriptInput input, RunAssociatedScriptOutput output) throws QException
|
||||
{
|
||||
Serializable scriptId = getScriptId(input);
|
||||
Script script = getScript(input, scriptId);
|
||||
ActionHelper.validateSession(input);
|
||||
|
||||
Serializable scriptId = getScriptId(input);
|
||||
if(scriptId == null)
|
||||
{
|
||||
throw (new QNotFoundException("The input record [" + input.getCodeReference().getRecordTable() + "][" + input.getCodeReference().getRecordPrimaryKey()
|
||||
+ "] does not have a script specified for [" + input.getCodeReference().getFieldName() + "]"));
|
||||
}
|
||||
|
||||
Script script = getScript(input, scriptId);
|
||||
if(script.getCurrentScriptRevisionId() == null)
|
||||
{
|
||||
throw (new QNotFoundException("The script for record [" + input.getCodeReference().getRecordTable() + "][" + input.getCodeReference().getRecordPrimaryKey()
|
||||
+ "] (scriptId=" + scriptId + ") does not have a current version."));
|
||||
}
|
||||
|
||||
ScriptRevision scriptRevision = getCurrentScriptRevision(input, script.getCurrentScriptRevisionId());
|
||||
|
||||
ExecuteCodeInput executeCodeInput = new ExecuteCodeInput(input.getInstance());
|
||||
executeCodeInput.setSession(input.getSession());
|
||||
executeCodeInput.setContext(new HashMap<>(input.getInputValues()));
|
||||
executeCodeInput.setCodeReference(new QCodeReference().withInlineCode(scriptRevision.getContents()).withCodeType(QCodeType.JAVA_SCRIPT));
|
||||
executeCodeInput.setExecutionLogger(new HeaderAndDetailTableCodeExecutionLogger(scriptRevision.getScriptId(), scriptRevision.getId()));
|
||||
executeCodeInput.setInput(new HashMap<>(input.getInputValues()));
|
||||
executeCodeInput.setContext(new HashMap<>());
|
||||
if(input.getOutputObject() != null)
|
||||
{
|
||||
executeCodeInput.getContext().put("output", input.getOutputObject());
|
||||
}
|
||||
executeCodeInput.setCodeReference(new QCodeReference().withInlineCode(scriptRevision.getContents()).withCodeType(QCodeType.JAVA_SCRIPT)); // todo - code type as attribute of script!!
|
||||
executeCodeInput.setExecutionLogger(new StoreScriptLogAndScriptLogLineExecutionLogger(scriptRevision.getScriptId(), scriptRevision.getId()));
|
||||
ExecuteCodeOutput executeCodeOutput = new ExecuteCodeOutput();
|
||||
new ExecuteCodeAction().run(executeCodeInput, executeCodeOutput);
|
||||
|
||||
@ -77,6 +98,12 @@ public class RunAssociatedScriptAction
|
||||
getInput.setTableName("scriptRevision");
|
||||
getInput.setPrimaryKey(scriptRevisionId);
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
if(getOutput.getRecord() == null)
|
||||
{
|
||||
throw (new QNotFoundException("The current revision of the script for record [" + input.getCodeReference().getRecordTable() + "][" + input.getCodeReference().getRecordPrimaryKey() + "]["
|
||||
+ input.getCodeReference().getFieldName() + "] (scriptRevisionId=" + scriptRevisionId + ") was not found."));
|
||||
}
|
||||
|
||||
return (new ScriptRevision(getOutput.getRecord()));
|
||||
}
|
||||
|
||||
@ -92,6 +119,13 @@ public class RunAssociatedScriptAction
|
||||
getInput.setTableName("script");
|
||||
getInput.setPrimaryKey(scriptId);
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
|
||||
if(getOutput.getRecord() == null)
|
||||
{
|
||||
throw (new QNotFoundException("The script for record [" + input.getCodeReference().getRecordTable() + "][" + input.getCodeReference().getRecordPrimaryKey() + "]["
|
||||
+ input.getCodeReference().getFieldName() + "] (script id=" + scriptId + ") was not found."));
|
||||
}
|
||||
|
||||
return (new Script(getOutput.getRecord()));
|
||||
}
|
||||
|
||||
@ -107,6 +141,11 @@ public class RunAssociatedScriptAction
|
||||
getInput.setTableName(input.getCodeReference().getRecordTable());
|
||||
getInput.setPrimaryKey(input.getCodeReference().getRecordPrimaryKey());
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
if(getOutput.getRecord() == null)
|
||||
{
|
||||
throw (new QNotFoundException("The requested record [" + input.getCodeReference().getRecordTable() + "][" + input.getCodeReference().getRecordPrimaryKey() + "] was not found."));
|
||||
}
|
||||
|
||||
return (getOutput.getRecord().getValue(input.getCodeReference().getFieldName()));
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@ package com.kingsrook.qqq.backend.core.actions.scripts;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||
@ -50,6 +51,13 @@ import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Action to store a new version of a script, associated with a record.
|
||||
**
|
||||
** If there's never been a script assigned to the record (for the specified field),
|
||||
** then a new Script record is first inserted.
|
||||
**
|
||||
** The script referenced by the record is always updated to point at the new
|
||||
** scriptRevision record that is inserted.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class StoreAssociatedScriptAction
|
||||
@ -60,6 +68,8 @@ public class StoreAssociatedScriptAction
|
||||
*******************************************************************************/
|
||||
public void run(StoreAssociatedScriptInput input, StoreAssociatedScriptOutput output) throws QException
|
||||
{
|
||||
ActionHelper.validateSession(input);
|
||||
|
||||
QTableMetaData table = input.getTable();
|
||||
Optional<AssociatedScript> optAssociatedScript = table.getAssociatedScripts().stream().filter(as -> as.getFieldName().equals(input.getFieldName())).findFirst();
|
||||
if(optAssociatedScript.isEmpty())
|
||||
@ -77,6 +87,7 @@ public class StoreAssociatedScriptAction
|
||||
getInput.setSession(input.getSession());
|
||||
getInput.setTableName(input.getTableName());
|
||||
getInput.setPrimaryKey(input.getRecordPrimaryKey());
|
||||
getInput.setShouldGenerateDisplayValues(true);
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
associatedRecord = getOutput.getRecord();
|
||||
}
|
||||
@ -100,8 +111,13 @@ public class StoreAssociatedScriptAction
|
||||
getInput.setSession(input.getSession());
|
||||
getInput.setTableName("scriptType");
|
||||
getInput.setPrimaryKey(associatedScript.getScriptTypeId());
|
||||
getInput.setShouldGenerateDisplayValues(true);
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
QRecord scriptType = getOutput.getRecord();
|
||||
if(scriptType == null)
|
||||
{
|
||||
throw (new QException("Script type [" + associatedScript.getScriptTypeId() + "] was not found."));
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// insert a new script //
|
||||
|
@ -22,14 +22,20 @@
|
||||
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
|
||||
{
|
||||
@ -40,5 +46,20 @@ 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());
|
||||
}
|
||||
}
|
||||
|
@ -27,30 +27,26 @@ import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.ExecuteCodeInput;
|
||||
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.update.UpdateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Implementation of a code execution logger that logs into a header and child
|
||||
** table
|
||||
** Implementation of a code execution logger that builds a scriptLog and 0 or more
|
||||
** scriptLogLine records - but doesn't insert them. e.g., useful for testing
|
||||
** (both in junit, and for users in-app).
|
||||
*******************************************************************************/
|
||||
public class HeaderAndDetailTableCodeExecutionLogger implements QCodeExecutionLoggerInterface
|
||||
public class BuildScriptLogAndScriptLogLineExecutionLogger implements QCodeExecutionLoggerInterface
|
||||
{
|
||||
private static final Logger LOG = LogManager.getLogger(HeaderAndDetailTableCodeExecutionLogger.class);
|
||||
private static final Logger LOG = LogManager.getLogger(BuildScriptLogAndScriptLogLineExecutionLogger.class);
|
||||
|
||||
private QRecord header;
|
||||
private List<QRecord> children = new ArrayList<>();
|
||||
private ExecuteCodeInput executeCodeInput;
|
||||
private QRecord scriptLog;
|
||||
private List<QRecord> scriptLogLines = new ArrayList<>();
|
||||
protected ExecuteCodeInput executeCodeInput;
|
||||
|
||||
private Serializable scriptId;
|
||||
private Serializable scriptRevisionId;
|
||||
@ -61,7 +57,17 @@ public class HeaderAndDetailTableCodeExecutionLogger implements QCodeExecutionLo
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public HeaderAndDetailTableCodeExecutionLogger(Serializable scriptId, Serializable scriptRevisionId)
|
||||
public BuildScriptLogAndScriptLogLineExecutionLogger()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public BuildScriptLogAndScriptLogLineExecutionLogger(Serializable scriptId, Serializable scriptRevisionId)
|
||||
{
|
||||
this.scriptId = scriptId;
|
||||
this.scriptRevisionId = scriptRevisionId;
|
||||
@ -78,7 +84,7 @@ public class HeaderAndDetailTableCodeExecutionLogger implements QCodeExecutionLo
|
||||
.withValue("scriptId", scriptId)
|
||||
.withValue("scriptRevisionId", scriptRevisionId)
|
||||
.withValue("startTimestamp", Instant.now())
|
||||
.withValue("input", truncate(executeCodeInput.getContext().toString())));
|
||||
.withValue("input", truncate(executeCodeInput.getInput())));
|
||||
}
|
||||
|
||||
|
||||
@ -89,7 +95,7 @@ public class HeaderAndDetailTableCodeExecutionLogger implements QCodeExecutionLo
|
||||
protected QRecord buildDetailLogRecord(String logLine)
|
||||
{
|
||||
return (new QRecord()
|
||||
.withValue("scriptLogId", header.getValue("id"))
|
||||
.withValue("scriptLogId", scriptLog.getValue("id"))
|
||||
.withValue("timestamp", Instant.now())
|
||||
.withValue("text", truncate(logLine)));
|
||||
}
|
||||
@ -99,9 +105,9 @@ public class HeaderAndDetailTableCodeExecutionLogger implements QCodeExecutionLo
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private String truncate(String logLine)
|
||||
private String truncate(Object o)
|
||||
{
|
||||
return StringUtils.safeTruncate(logLine, 1000, "...");
|
||||
return StringUtils.safeTruncate(ValueUtils.getValueAsString(o), 1000, "...");
|
||||
}
|
||||
|
||||
|
||||
@ -109,22 +115,22 @@ public class HeaderAndDetailTableCodeExecutionLogger implements QCodeExecutionLo
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void updateHeaderAtEnd(Serializable output, Exception exception)
|
||||
protected void updateHeaderAtEnd(Serializable output, Exception exception)
|
||||
{
|
||||
Instant startTimestamp = (Instant) header.getValue("startTimestamp");
|
||||
Instant startTimestamp = (Instant) scriptLog.getValue("startTimestamp");
|
||||
Instant endTimestamp = Instant.now();
|
||||
header.setValue("endTimestamp", endTimestamp);
|
||||
header.setValue("runTimeMillis", startTimestamp.until(endTimestamp, ChronoUnit.MILLIS));
|
||||
scriptLog.setValue("endTimestamp", endTimestamp);
|
||||
scriptLog.setValue("runTimeMillis", startTimestamp.until(endTimestamp, ChronoUnit.MILLIS));
|
||||
|
||||
if(exception != null)
|
||||
{
|
||||
header.setValue("hadError", true);
|
||||
header.setValue("error", exception.getMessage());
|
||||
scriptLog.setValue("hadError", true);
|
||||
scriptLog.setValue("error", exception.getMessage());
|
||||
}
|
||||
else
|
||||
{
|
||||
header.setValue("hadError", false);
|
||||
header.setValue("output", truncate(String.valueOf(output)));
|
||||
scriptLog.setValue("hadError", false);
|
||||
scriptLog.setValue("output", truncate(output));
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,14 +145,7 @@ public class HeaderAndDetailTableCodeExecutionLogger implements QCodeExecutionLo
|
||||
try
|
||||
{
|
||||
this.executeCodeInput = executeCodeInput;
|
||||
QRecord scriptLog = buildHeaderRecord(executeCodeInput);
|
||||
|
||||
InsertInput insertInput = new InsertInput(executeCodeInput.getInstance());
|
||||
insertInput.setSession(executeCodeInput.getSession());
|
||||
insertInput.setTableName("scriptLog");
|
||||
insertInput.setRecords(List.of(scriptLog));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
this.header = insertOutput.getRecords().get(0);
|
||||
this.scriptLog = buildHeaderRecord(executeCodeInput);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
@ -162,7 +161,7 @@ public class HeaderAndDetailTableCodeExecutionLogger implements QCodeExecutionLo
|
||||
@Override
|
||||
public void acceptLogLine(String logLine)
|
||||
{
|
||||
children.add(buildDetailLogRecord(logLine));
|
||||
scriptLogLines.add(buildDetailLogRecord(logLine));
|
||||
}
|
||||
|
||||
|
||||
@ -173,7 +172,7 @@ public class HeaderAndDetailTableCodeExecutionLogger implements QCodeExecutionLo
|
||||
@Override
|
||||
public void acceptException(Exception exception)
|
||||
{
|
||||
store(null, exception);
|
||||
updateHeaderAtEnd(null, exception);
|
||||
}
|
||||
|
||||
|
||||
@ -184,38 +183,39 @@ public class HeaderAndDetailTableCodeExecutionLogger implements QCodeExecutionLo
|
||||
@Override
|
||||
public void acceptExecutionEnd(Serializable output)
|
||||
{
|
||||
store(output, null);
|
||||
updateHeaderAtEnd(output, null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for scriptLog
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void store(Serializable output, Exception exception)
|
||||
public QRecord getScriptLog()
|
||||
{
|
||||
try
|
||||
{
|
||||
updateHeaderAtEnd(output, exception);
|
||||
UpdateInput updateInput = new UpdateInput(executeCodeInput.getInstance());
|
||||
updateInput.setSession(executeCodeInput.getSession());
|
||||
updateInput.setTableName("scriptLog");
|
||||
updateInput.setRecords(List.of(header));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
if(CollectionUtils.nullSafeHasContents(children))
|
||||
{
|
||||
InsertInput insertInput = new InsertInput(executeCodeInput.getInstance());
|
||||
insertInput.setSession(executeCodeInput.getSession());
|
||||
insertInput.setTableName("scriptLogLine");
|
||||
insertInput.setRecords(children);
|
||||
new InsertAction().execute(insertInput);
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn("Error storing script log", e);
|
||||
}
|
||||
return scriptLog;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for scriptLogLines
|
||||
**
|
||||
*******************************************************************************/
|
||||
public List<QRecord> getScriptLogLines()
|
||||
{
|
||||
return scriptLogLines;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for scriptLog
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected void setScriptLog(QRecord scriptLog)
|
||||
{
|
||||
this.scriptLog = scriptLog;
|
||||
}
|
||||
}
|
@ -33,7 +33,7 @@ import org.apache.logging.log4j.Logger;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Implementation of a code execution logger that just logs to LOG 4j
|
||||
** Implementation of a code execution logger that logs to LOG 4j
|
||||
*******************************************************************************/
|
||||
public class Log4jCodeExecutionLogger implements QCodeExecutionLoggerInterface
|
||||
{
|
||||
@ -52,8 +52,8 @@ public class Log4jCodeExecutionLogger implements QCodeExecutionLoggerInterface
|
||||
{
|
||||
this.qCodeReference = executeCodeInput.getCodeReference();
|
||||
|
||||
String contextString = StringUtils.safeTruncate(ValueUtils.getValueAsString(executeCodeInput.getContext()), 250, "...");
|
||||
LOG.info("Starting script execution: " + qCodeReference.getName() + ", uuid: " + uuid + ", with context: " + contextString);
|
||||
String inputString = StringUtils.safeTruncate(ValueUtils.getValueAsString(executeCodeInput.getInput()), 250, "...");
|
||||
LOG.info("Starting script execution: " + qCodeReference.getName() + ", uuid: " + uuid + ", with input: " + inputString);
|
||||
}
|
||||
|
||||
|
||||
|
@ -27,23 +27,24 @@ import com.kingsrook.qqq.backend.core.model.actions.scripts.ExecuteCodeInput;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Interface to provide logging functionality to QCodeExecution (e.g., scripts)
|
||||
*******************************************************************************/
|
||||
public interface QCodeExecutionLoggerInterface
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Called when the execution starts - takes the execution's input object.
|
||||
*******************************************************************************/
|
||||
void acceptExecutionStart(ExecuteCodeInput executeCodeInput);
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Called to log a line, a message.
|
||||
*******************************************************************************/
|
||||
void acceptLogLine(String logLine);
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** In case the loggerInterface object is provided to the script as context,
|
||||
** this method gives a clean interface for the script to log a line.
|
||||
*******************************************************************************/
|
||||
default void log(String message)
|
||||
{
|
||||
@ -51,12 +52,12 @@ public interface QCodeExecutionLoggerInterface
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Called if the script fails with an exception.
|
||||
*******************************************************************************/
|
||||
void acceptException(Exception exception);
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Called if the script completes without exception.
|
||||
*******************************************************************************/
|
||||
void acceptExecutionEnd(Serializable output);
|
||||
|
||||
|
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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.logging;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.ExecuteCodeInput;
|
||||
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.update.UpdateInput;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Implementation of a code execution logger that logs into scriptLog and scriptLogLine
|
||||
** tables - e.g., as defined in ScriptMetaDataProvider.
|
||||
*******************************************************************************/
|
||||
public class StoreScriptLogAndScriptLogLineExecutionLogger extends BuildScriptLogAndScriptLogLineExecutionLogger
|
||||
{
|
||||
private static final Logger LOG = LogManager.getLogger(StoreScriptLogAndScriptLogLineExecutionLogger.class);
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public StoreScriptLogAndScriptLogLineExecutionLogger(Serializable scriptId, Serializable scriptRevisionId)
|
||||
{
|
||||
super(scriptId, scriptRevisionId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void acceptExecutionStart(ExecuteCodeInput executeCodeInput)
|
||||
{
|
||||
try
|
||||
{
|
||||
super.acceptExecutionStart(executeCodeInput);
|
||||
|
||||
InsertInput insertInput = new InsertInput(executeCodeInput.getInstance());
|
||||
insertInput.setSession(executeCodeInput.getSession());
|
||||
insertInput.setTableName("scriptLog");
|
||||
insertInput.setRecords(List.of(getScriptLog()));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
|
||||
setScriptLog(insertOutput.getRecords().get(0));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn("Error starting storage of script log", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void acceptException(Exception exception)
|
||||
{
|
||||
store(null, exception);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void acceptExecutionEnd(Serializable output)
|
||||
{
|
||||
store(output, null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void store(Serializable output, Exception exception)
|
||||
{
|
||||
try
|
||||
{
|
||||
updateHeaderAtEnd(output, exception);
|
||||
UpdateInput updateInput = new UpdateInput(executeCodeInput.getInstance());
|
||||
updateInput.setSession(executeCodeInput.getSession());
|
||||
updateInput.setTableName("scriptLog");
|
||||
updateInput.setRecords(List.of(getScriptLog()));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
if(CollectionUtils.nullSafeHasContents(getScriptLogLines()))
|
||||
{
|
||||
InsertInput insertInput = new InsertInput(executeCodeInput.getInstance());
|
||||
insertInput.setSession(executeCodeInput.getSession());
|
||||
insertInput.setTableName("scriptLogLine");
|
||||
insertInput.setRecords(getScriptLogLines());
|
||||
new InsertAction().execute(insertInput);
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn("Error storing script log", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -406,8 +406,11 @@ public class QPossibleValueTranslator
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
// this is needed to get record labels, which are what we use here... unclear if best! //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
queryInput.setShouldTranslatePossibleValues(true);
|
||||
queryInput.setShouldGenerateDisplayValues(true);
|
||||
if(notTooDeep())
|
||||
{
|
||||
queryInput.setShouldTranslatePossibleValues(true);
|
||||
queryInput.setShouldGenerateDisplayValues(true);
|
||||
}
|
||||
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
|
||||
@ -428,4 +431,24 @@ public class QPossibleValueTranslator
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Avoid infinite recursion, for where one field's PVS depends on another's...
|
||||
** not too smart, just breaks at 5...
|
||||
*******************************************************************************/
|
||||
private boolean notTooDeep()
|
||||
{
|
||||
int count = 0;
|
||||
for(StackTraceElement stackTraceElement : new Throwable().getStackTrace())
|
||||
{
|
||||
if(stackTraceElement.getMethodName().equals("translatePossibleValuesInRecords"))
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return (count < 5);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -184,6 +184,7 @@ public class QValueFormatter
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.debug("Error formatting record label", e);
|
||||
return (formatRecordLabelExceptionalCases(table, record));
|
||||
}
|
||||
}
|
||||
|
@ -23,8 +23,10 @@ package com.kingsrook.qqq.backend.core.exceptions;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* Exception thrown while generating reports
|
||||
*
|
||||
** Exception thrown while executing custom code in QQQ.
|
||||
**
|
||||
** Context field is meant to give the user "context" for where the error occurred
|
||||
** - e.g., a line number or word that was bad.
|
||||
*******************************************************************************/
|
||||
public class QCodeException extends QException
|
||||
{
|
||||
|
@ -37,6 +37,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
public class ExecuteCodeInput extends AbstractActionInput
|
||||
{
|
||||
private QCodeReference codeReference;
|
||||
private Map<String, Serializable> input;
|
||||
private Map<String, Serializable> context;
|
||||
private QCodeExecutionLoggerInterface executionLogger;
|
||||
|
||||
@ -87,6 +88,56 @@ public class ExecuteCodeInput extends AbstractActionInput
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for input
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Map<String, Serializable> getInput()
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for input
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setInput(Map<String, Serializable> input)
|
||||
{
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for input
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ExecuteCodeInput withInput(Map<String, Serializable> input)
|
||||
{
|
||||
this.input = input;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for input
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ExecuteCodeInput withInput(String key, Serializable value)
|
||||
{
|
||||
if(this.input == null)
|
||||
{
|
||||
input = new HashMap<>();
|
||||
}
|
||||
this.input.put(key, value);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for context
|
||||
**
|
||||
|
@ -22,6 +22,7 @@
|
||||
package com.kingsrook.qqq.backend.core.model.actions.scripts;
|
||||
|
||||
|
||||
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;
|
||||
@ -34,7 +35,9 @@ import com.kingsrook.qqq.backend.core.model.metadata.code.AssociatedScriptCodeRe
|
||||
public class RunAssociatedScriptInput extends AbstractTableActionInput
|
||||
{
|
||||
private AssociatedScriptCodeReference codeReference;
|
||||
private Map<String, String> inputValues;
|
||||
private Map<String, Serializable> inputValues;
|
||||
|
||||
private Serializable outputObject;
|
||||
|
||||
|
||||
|
||||
@ -86,7 +89,7 @@ public class RunAssociatedScriptInput extends AbstractTableActionInput
|
||||
** Getter for inputValues
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Map<String, String> getInputValues()
|
||||
public Map<String, Serializable> getInputValues()
|
||||
{
|
||||
return inputValues;
|
||||
}
|
||||
@ -97,7 +100,7 @@ public class RunAssociatedScriptInput extends AbstractTableActionInput
|
||||
** Setter for inputValues
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setInputValues(Map<String, String> inputValues)
|
||||
public void setInputValues(Map<String, Serializable> inputValues)
|
||||
{
|
||||
this.inputValues = inputValues;
|
||||
}
|
||||
@ -108,10 +111,44 @@ public class RunAssociatedScriptInput extends AbstractTableActionInput
|
||||
** Fluent setter for inputValues
|
||||
**
|
||||
*******************************************************************************/
|
||||
public RunAssociatedScriptInput withInputValues(Map<String, String> inputValues)
|
||||
public RunAssociatedScriptInput withInputValues(Map<String, Serializable> inputValues)
|
||||
{
|
||||
this.inputValues = inputValues;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for outputObject
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Serializable getOutputObject()
|
||||
{
|
||||
return outputObject;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for outputObject
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setOutputObject(Serializable outputObject)
|
||||
{
|
||||
this.outputObject = outputObject;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for outputObject
|
||||
**
|
||||
*******************************************************************************/
|
||||
public RunAssociatedScriptInput withOutputObject(Serializable outputObject)
|
||||
{
|
||||
this.outputObject = outputObject;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,8 +22,12 @@
|
||||
package com.kingsrook.qqq.backend.core.model.metadata;
|
||||
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.serialization.QBackendMetaDataDeserializer;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
||||
|
||||
|
||||
@ -38,6 +42,9 @@ public class QBackendMetaData
|
||||
private String name;
|
||||
private String backendType;
|
||||
|
||||
private Set<Capability> enabledCapabilities = new HashSet<>();
|
||||
private Set<Capability> disabledCapabilities = new HashSet<>();
|
||||
|
||||
// todo - at some point, we may want to apply this to secret properties on subclasses?
|
||||
// @JsonFilter("secretsFilter")
|
||||
|
||||
@ -157,4 +164,161 @@ public class QBackendMetaData
|
||||
// noop in base class //
|
||||
////////////////////////
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for enabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Set<Capability> getEnabledCapabilities()
|
||||
{
|
||||
return enabledCapabilities;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for enabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setEnabledCapabilities(Set<Capability> enabledCapabilities)
|
||||
{
|
||||
this.enabledCapabilities = enabledCapabilities;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for enabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withEnabledCapabilities(Set<Capability> enabledCapabilities)
|
||||
{
|
||||
this.enabledCapabilities = enabledCapabilities;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Alternative fluent setter for enabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withCapabilities(Set<Capability> enabledCapabilities)
|
||||
{
|
||||
this.enabledCapabilities = enabledCapabilities;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Alternative fluent setter for a single enabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withCapability(Capability capability)
|
||||
{
|
||||
if(this.enabledCapabilities == null)
|
||||
{
|
||||
this.enabledCapabilities = new HashSet<>();
|
||||
}
|
||||
this.enabledCapabilities.add(capability);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for enabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withCapabilities(Capability... enabledCapabilities)
|
||||
{
|
||||
if(this.enabledCapabilities == null)
|
||||
{
|
||||
this.enabledCapabilities = new HashSet<>();
|
||||
}
|
||||
this.enabledCapabilities.addAll(Arrays.stream(enabledCapabilities).toList());
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for disabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Set<Capability> getDisabledCapabilities()
|
||||
{
|
||||
return disabledCapabilities;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for disabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setDisabledCapabilities(Set<Capability> disabledCapabilities)
|
||||
{
|
||||
this.disabledCapabilities = disabledCapabilities;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for disabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withDisabledCapabilities(Set<Capability> disabledCapabilities)
|
||||
{
|
||||
this.disabledCapabilities = disabledCapabilities;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for disabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withoutCapabilities(Capability... disabledCapabilities)
|
||||
{
|
||||
if(this.disabledCapabilities == null)
|
||||
{
|
||||
this.disabledCapabilities = new HashSet<>();
|
||||
}
|
||||
this.disabledCapabilities.addAll(Arrays.stream(disabledCapabilities).toList());
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Alternative fluent setter for disabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withoutCapabilities(Set<Capability> disabledCapabilities)
|
||||
{
|
||||
this.disabledCapabilities = disabledCapabilities;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Alternative fluent setter for a single disabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withoutCapability(Capability capability)
|
||||
{
|
||||
if(this.disabledCapabilities == null)
|
||||
{
|
||||
this.disabledCapabilities = new HashSet<>();
|
||||
}
|
||||
this.disabledCapabilities.add(capability);
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,6 +22,9 @@
|
||||
package com.kingsrook.qqq.backend.core.model.metadata.dashboard;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
|
||||
|
||||
@ -38,6 +41,8 @@ public class QWidgetMetaData implements QWidgetMetaDataInterface
|
||||
protected Integer gridColumns;
|
||||
protected QCodeReference codeReference;
|
||||
|
||||
protected Map<String, Serializable> defaultValues = new LinkedHashMap<>();
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -242,4 +247,56 @@ public class QWidgetMetaData implements QWidgetMetaDataInterface
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for defaultValues
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Map<String, Serializable> getDefaultValues()
|
||||
{
|
||||
return defaultValues;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for defaultValues
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setDefaultValues(Map<String, Serializable> defaultValues)
|
||||
{
|
||||
this.defaultValues = defaultValues;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for defaultValues
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QWidgetMetaData withDefaultValues(Map<String, Serializable> defaultValues)
|
||||
{
|
||||
this.defaultValues = defaultValues;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for a single defaultValue
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QWidgetMetaData withDefaultValue(String key, Serializable value)
|
||||
{
|
||||
if(this.defaultValues == null)
|
||||
{
|
||||
this.defaultValues = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
this.defaultValues.put(key, value);
|
||||
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -34,7 +34,11 @@ public enum AdornmentType
|
||||
{
|
||||
LINK,
|
||||
CHIP,
|
||||
SIZE;
|
||||
SIZE,
|
||||
CODE_EDITOR;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// keep these values in sync with AdornmentType.ts in qqq-frontend-core //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
|
@ -23,11 +23,16 @@ package com.kingsrook.qqq.backend.core.model.metadata.frontend;
|
||||
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
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.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
@ -53,6 +58,8 @@ public class QFrontendTableMetaData
|
||||
|
||||
private List<String> widgets;
|
||||
|
||||
private Set<String> capabilities;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// do not add setters. take values from the source-object in the constructor!! //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
@ -62,7 +69,7 @@ public class QFrontendTableMetaData
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QFrontendTableMetaData(QTableMetaData tableMetaData, boolean includeFields)
|
||||
public QFrontendTableMetaData(QBackendMetaData backendForTable, QTableMetaData tableMetaData, boolean includeFields)
|
||||
{
|
||||
this.name = tableMetaData.getName();
|
||||
this.label = tableMetaData.getLabel();
|
||||
@ -89,6 +96,62 @@ public class QFrontendTableMetaData
|
||||
{
|
||||
this.widgets = tableMetaData.getWidgets();
|
||||
}
|
||||
|
||||
setCapabilities(backendForTable, tableMetaData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void setCapabilities(QBackendMetaData backend, QTableMetaData table)
|
||||
{
|
||||
Set<Capability> enabledCapabilities = new HashSet<>();
|
||||
for(Capability capability : Capability.values())
|
||||
{
|
||||
///////////////////////////////////////////////
|
||||
// by default, every table can do everything //
|
||||
///////////////////////////////////////////////
|
||||
boolean hasCapability = true;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if the table's backend says the capability is disabled, then by default, then the capability is disabled... //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if(backend.getDisabledCapabilities().contains(capability))
|
||||
{
|
||||
hasCapability = false;
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// unless the table overrides that and says that it IS enabled //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
if(table.getEnabledCapabilities().contains(capability))
|
||||
{
|
||||
hasCapability = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if the backend doesn't specify the capability, then disable it if the table says so //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
if(table.getDisabledCapabilities().contains(capability))
|
||||
{
|
||||
hasCapability = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(hasCapability)
|
||||
{
|
||||
///////////////////////////////////////
|
||||
// todo - check if user is allowed!! //
|
||||
///////////////////////////////////////
|
||||
|
||||
enabledCapabilities.add(capability);
|
||||
}
|
||||
}
|
||||
|
||||
this.capabilities = enabledCapabilities.stream().map(Enum::name).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
|
||||
@ -178,4 +241,16 @@ public class QFrontendTableMetaData
|
||||
{
|
||||
return widgets;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for capabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Set<String> getCapabilities()
|
||||
{
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,6 +24,9 @@ package com.kingsrook.qqq.backend.core.model.metadata.layout;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
|
||||
|
||||
@ -340,4 +343,38 @@ public class QAppMetaData implements QAppChildMetaData
|
||||
this.addSection(section);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QAppMetaData withSectionOfChildren(QAppSection section, QAppChildMetaData... children)
|
||||
{
|
||||
this.addSection(section);
|
||||
|
||||
for(QAppChildMetaData child : children)
|
||||
{
|
||||
withChild(child);
|
||||
if(child instanceof QTableMetaData)
|
||||
{
|
||||
section.withTable(child.getName());
|
||||
}
|
||||
else if(child instanceof QProcessMetaData)
|
||||
{
|
||||
section.withProcess(child.getName());
|
||||
}
|
||||
else if(child instanceof QReportMetaData)
|
||||
{
|
||||
section.withReport(child.getName());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException("Unrecognized child type: " + child.getName());
|
||||
}
|
||||
}
|
||||
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
package com.kingsrook.qqq.backend.core.model.metadata.layout;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@ -166,6 +167,22 @@ public class QAppSection
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for tables
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QAppSection withTable(String tableName)
|
||||
{
|
||||
if(this.tables == null)
|
||||
{
|
||||
this.tables = new ArrayList<>();
|
||||
}
|
||||
this.tables.add(tableName);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for processes
|
||||
**
|
||||
@ -200,6 +217,22 @@ public class QAppSection
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for processes
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QAppSection withProcess(String processName)
|
||||
{
|
||||
if(this.processes == null)
|
||||
{
|
||||
this.processes = new ArrayList<>();
|
||||
}
|
||||
this.processes.add(processName);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for reports
|
||||
**
|
||||
@ -234,6 +267,22 @@ public class QAppSection
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for reports
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QAppSection withReport(String reportName)
|
||||
{
|
||||
if(this.reports == null)
|
||||
{
|
||||
this.reports = new ArrayList<>();
|
||||
}
|
||||
this.reports.add(reportName);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for icon
|
||||
**
|
||||
|
@ -289,6 +289,7 @@ public class QPossibleValueSource
|
||||
*******************************************************************************/
|
||||
public void setTableName(String tableName)
|
||||
{
|
||||
this.type = QPossibleValueSourceType.TABLE;
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
@ -300,7 +301,7 @@ public class QPossibleValueSource
|
||||
*******************************************************************************/
|
||||
public QPossibleValueSource withTableName(String tableName)
|
||||
{
|
||||
this.tableName = tableName;
|
||||
setTableName(tableName);
|
||||
return (this);
|
||||
}
|
||||
|
||||
@ -446,6 +447,7 @@ public class QPossibleValueSource
|
||||
public void setEnumValues(List<QPossibleValue<?>> enumValues)
|
||||
{
|
||||
this.enumValues = enumValues;
|
||||
setType(QPossibleValueSourceType.ENUM);
|
||||
}
|
||||
|
||||
|
||||
@ -456,7 +458,7 @@ public class QPossibleValueSource
|
||||
*******************************************************************************/
|
||||
public QPossibleValueSource withEnumValues(List<QPossibleValue<?>> enumValues)
|
||||
{
|
||||
this.enumValues = enumValues;
|
||||
setEnumValues(enumValues);
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -472,6 +474,7 @@ public class QPossibleValueSource
|
||||
this.enumValues = new ArrayList<>();
|
||||
}
|
||||
this.enumValues.add(possibleValue);
|
||||
setType(QPossibleValueSourceType.ENUM);
|
||||
}
|
||||
|
||||
|
||||
@ -512,6 +515,7 @@ public class QPossibleValueSource
|
||||
public void setCustomCodeReference(QCodeReference customCodeReference)
|
||||
{
|
||||
this.customCodeReference = customCodeReference;
|
||||
setType(QPossibleValueSourceType.CUSTOM);
|
||||
}
|
||||
|
||||
|
||||
@ -522,7 +526,7 @@ public class QPossibleValueSource
|
||||
*******************************************************************************/
|
||||
public QPossibleValueSource withCustomCodeReference(QCodeReference customCodeReference)
|
||||
{
|
||||
this.customCodeReference = customCodeReference;
|
||||
setCustomCodeReference(customCodeReference);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
@ -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.model.metadata.tables;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Things that can be done to tables, fields.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public enum Capability
|
||||
{
|
||||
TABLE_QUERY,
|
||||
TABLE_GET,
|
||||
TABLE_COUNT,
|
||||
TABLE_INSERT,
|
||||
TABLE_UPDATE,
|
||||
TABLE_DELETE
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// keep these values in sync with AdornmentType.ts in qqq-frontend-core //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
}
|
@ -38,6 +38,8 @@ public class QFieldSection
|
||||
private List<String> fieldNames;
|
||||
private QIcon icon;
|
||||
|
||||
private boolean isHidden = false;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -244,4 +246,38 @@ public class QFieldSection
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for isHidden
|
||||
**
|
||||
*******************************************************************************/
|
||||
public boolean getIsHidden()
|
||||
{
|
||||
return (isHidden);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for isHidden
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setIsHidden(boolean isHidden)
|
||||
{
|
||||
this.isHidden = isHidden;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent Setter for isHidden
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QFieldSection withIsHidden(boolean isHidden)
|
||||
{
|
||||
this.isHidden = isHidden;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -26,10 +26,12 @@ import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizer;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
@ -81,6 +83,9 @@ public class QTableMetaData implements QAppChildMetaData, Serializable
|
||||
private List<String> widgets;
|
||||
private List<AssociatedScript> associatedScripts;
|
||||
|
||||
private Set<Capability> enabledCapabilities = new HashSet<>();
|
||||
private Set<Capability> disabledCapabilities = new HashSet<>();
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -874,4 +879,160 @@ public class QTableMetaData implements QAppChildMetaData, Serializable
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for enabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Set<Capability> getEnabledCapabilities()
|
||||
{
|
||||
return enabledCapabilities;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for enabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setEnabledCapabilities(Set<Capability> enabledCapabilities)
|
||||
{
|
||||
this.enabledCapabilities = enabledCapabilities;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for enabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withEnabledCapabilities(Set<Capability> enabledCapabilities)
|
||||
{
|
||||
this.enabledCapabilities = enabledCapabilities;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Alternative fluent setter for enabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withCapabilities(Set<Capability> enabledCapabilities)
|
||||
{
|
||||
this.enabledCapabilities = enabledCapabilities;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Alternative fluent setter for a single enabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withCapability(Capability capability)
|
||||
{
|
||||
if(this.enabledCapabilities == null)
|
||||
{
|
||||
this.enabledCapabilities = new HashSet<>();
|
||||
}
|
||||
this.enabledCapabilities.add(capability);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for enabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withCapabilities(Capability... enabledCapabilities)
|
||||
{
|
||||
if(this.enabledCapabilities == null)
|
||||
{
|
||||
this.enabledCapabilities = new HashSet<>();
|
||||
}
|
||||
this.enabledCapabilities.addAll(Arrays.stream(enabledCapabilities).toList());
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for disabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Set<Capability> getDisabledCapabilities()
|
||||
{
|
||||
return disabledCapabilities;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for disabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setDisabledCapabilities(Set<Capability> disabledCapabilities)
|
||||
{
|
||||
this.disabledCapabilities = disabledCapabilities;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for disabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withDisabledCapabilities(Set<Capability> disabledCapabilities)
|
||||
{
|
||||
this.disabledCapabilities = disabledCapabilities;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for disabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withoutCapabilities(Capability... disabledCapabilities)
|
||||
{
|
||||
if(this.disabledCapabilities == null)
|
||||
{
|
||||
this.disabledCapabilities = new HashSet<>();
|
||||
}
|
||||
this.disabledCapabilities.addAll(Arrays.stream(disabledCapabilities).toList());
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Alternative fluent setter for disabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withoutCapabilities(Set<Capability> disabledCapabilities)
|
||||
{
|
||||
this.disabledCapabilities = disabledCapabilities;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Alternative fluent setter for a single disabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withoutCapability(Capability capability)
|
||||
{
|
||||
if(this.disabledCapabilities == null)
|
||||
{
|
||||
this.disabledCapabilities = new HashSet<>();
|
||||
}
|
||||
this.disabledCapabilities.add(capability);
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -48,10 +48,10 @@ public class Script extends QRecordEntity
|
||||
@QField()
|
||||
private String name;
|
||||
|
||||
@QField()
|
||||
@QField(possibleValueSourceName = "scriptType")
|
||||
private Integer scriptTypeId;
|
||||
|
||||
@QField()
|
||||
@QField(possibleValueSourceName = "scriptRevision")
|
||||
private Integer currentScriptRevisionId;
|
||||
|
||||
|
||||
|
@ -25,6 +25,7 @@ package com.kingsrook.qqq.backend.core.model.scripts;
|
||||
import java.time.Instant;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.DisplayFormat;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -43,10 +44,10 @@ public class ScriptLog extends QRecordEntity
|
||||
@QField()
|
||||
private Instant modifyDate;
|
||||
|
||||
@QField()
|
||||
@QField(possibleValueSourceName = "script")
|
||||
private Integer scriptId;
|
||||
|
||||
@QField()
|
||||
@QField(possibleValueSourceName = "scriptRevision")
|
||||
private Integer scriptRevisionId;
|
||||
|
||||
@QField()
|
||||
@ -55,7 +56,7 @@ public class ScriptLog extends QRecordEntity
|
||||
@QField()
|
||||
private Instant endTimestamp;
|
||||
|
||||
@QField()
|
||||
@QField(displayFormat = DisplayFormat.COMMAS)
|
||||
private Integer runTimeMillis;
|
||||
|
||||
@QField()
|
||||
|
@ -43,7 +43,7 @@ public class ScriptLogLine extends QRecordEntity
|
||||
@QField()
|
||||
private Instant modifyDate;
|
||||
|
||||
@QField()
|
||||
@QField(possibleValueSourceName = "scriptLog")
|
||||
private Integer scriptLogId;
|
||||
|
||||
@QField()
|
||||
|
@ -45,7 +45,7 @@ public class ScriptRevision extends QRecordEntity
|
||||
@QField()
|
||||
private Instant modifyDate;
|
||||
|
||||
@QField()
|
||||
@QField(possibleValueSourceName = "script")
|
||||
private Integer scriptId;
|
||||
|
||||
@QField()
|
||||
|
@ -28,7 +28,14 @@ import java.util.function.Consumer;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
||||
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.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -37,6 +44,17 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
public class ScriptsMetaDataProvider
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void defineAll(QInstance instance, String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
||||
{
|
||||
defineStandardScriptsTables(instance, backendName, backendDetailEnricher);
|
||||
defineStandardScriptsPossibleValueSources(instance);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -53,7 +71,35 @@ public class ScriptsMetaDataProvider
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public List<QTableMetaData> defineStandardScriptsTables(String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
||||
public void defineStandardScriptsPossibleValueSources(QInstance instance) throws QException
|
||||
{
|
||||
instance.addPossibleValueSource(new QPossibleValueSource()
|
||||
.withName(Script.TABLE_NAME)
|
||||
.withTableName(Script.TABLE_NAME)
|
||||
);
|
||||
|
||||
instance.addPossibleValueSource(new QPossibleValueSource()
|
||||
.withName(ScriptRevision.TABLE_NAME)
|
||||
.withTableName(ScriptRevision.TABLE_NAME)
|
||||
);
|
||||
|
||||
instance.addPossibleValueSource(new QPossibleValueSource()
|
||||
.withName(ScriptType.TABLE_NAME)
|
||||
.withTableName(ScriptType.TABLE_NAME)
|
||||
);
|
||||
|
||||
instance.addPossibleValueSource(new QPossibleValueSource()
|
||||
.withName(ScriptLog.TABLE_NAME)
|
||||
.withTableName(ScriptLog.TABLE_NAME)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private List<QTableMetaData> defineStandardScriptsTables(String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
||||
{
|
||||
List<QTableMetaData> rs = new ArrayList<>();
|
||||
rs.add(enrich(backendDetailEnricher, defineScriptTypeTable(backendName)));
|
||||
@ -101,7 +147,9 @@ public class ScriptsMetaDataProvider
|
||||
*******************************************************************************/
|
||||
private QTableMetaData defineScriptTable(String backendName) throws QException
|
||||
{
|
||||
return (defineStandardTable(backendName, Script.TABLE_NAME, Script.class));
|
||||
return (defineStandardTable(backendName, Script.TABLE_NAME, Script.class)
|
||||
.withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "name", "scriptTypeId", "currentScriptRevisionId")))
|
||||
.withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate"))));
|
||||
}
|
||||
|
||||
|
||||
@ -111,7 +159,12 @@ public class ScriptsMetaDataProvider
|
||||
*******************************************************************************/
|
||||
private QTableMetaData defineScriptTypeTable(String backendName) throws QException
|
||||
{
|
||||
return (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("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));
|
||||
return (tableMetaData);
|
||||
}
|
||||
|
||||
|
||||
@ -121,8 +174,17 @@ public class ScriptsMetaDataProvider
|
||||
*******************************************************************************/
|
||||
private QTableMetaData defineScriptRevisionTable(String backendName) throws QException
|
||||
{
|
||||
return (defineStandardTable(backendName, ScriptRevision.TABLE_NAME, ScriptRevision.class)
|
||||
.withRecordLabelFields(List.of("id")));
|
||||
QTableMetaData tableMetaData = defineStandardTable(backendName, ScriptRevision.TABLE_NAME, ScriptRevision.class)
|
||||
.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE)
|
||||
.withRecordLabelFormat("%s v%s")
|
||||
.withRecordLabelFields(List.of("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("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")));
|
||||
|
||||
tableMetaData.getField("contents").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR));
|
||||
return (tableMetaData);
|
||||
}
|
||||
|
||||
|
||||
@ -133,7 +195,12 @@ public class ScriptsMetaDataProvider
|
||||
private QTableMetaData defineScriptLogTable(String backendName) throws QException
|
||||
{
|
||||
return (defineStandardTable(backendName, ScriptLog.TABLE_NAME, ScriptLog.class)
|
||||
.withRecordLabelFields(List.of("id")));
|
||||
.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"))));
|
||||
}
|
||||
|
||||
|
||||
|
@ -28,7 +28,11 @@ import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Backend module for a table based on a java enum. So we can expose an enum
|
||||
** as a table (similar to exposing an enum as a possible value source), with multiple
|
||||
** fields in the enum (exposed via getter methods in the enum) as fields in the table.
|
||||
**
|
||||
** Only supports read-operations, as you can't modify an enum.
|
||||
*******************************************************************************/
|
||||
public class EnumerationBackendModule implements QBackendModuleInterface
|
||||
{
|
||||
|
@ -39,12 +39,16 @@ import org.apache.commons.lang.NotImplementedException;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Utility class for backend modules that need to do filter operations.
|
||||
**
|
||||
** e.g., like an in-memory module, or one that's working with files - basically
|
||||
** one that doesn't have filtering provided by the backend (like a database or API).
|
||||
*******************************************************************************/
|
||||
public class BackendQueryFilterUtils
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Test if record matches filter.
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings("checkstyle:indentation")
|
||||
public static boolean doesRecordMatch(QQueryFilter filter, QRecord qRecord)
|
||||
@ -407,7 +411,7 @@ public class BackendQueryFilterUtils
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Sort list of records based on filter.
|
||||
*******************************************************************************/
|
||||
public static void sortRecordList(QQueryFilter filter, List<QRecord> recordList)
|
||||
{
|
||||
@ -443,7 +447,7 @@ public class BackendQueryFilterUtils
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Apply skip & limit attributes from queryInput to a list of records.
|
||||
*******************************************************************************/
|
||||
public static List<QRecord> applySkipAndLimit(QueryInput queryInput, List<QRecord> recordList)
|
||||
{
|
||||
|
@ -41,13 +41,19 @@ import com.kingsrook.qqq.backend.core.utils.ListingHash;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Utility methods for working with QQQ records and table actions inside user -
|
||||
** defined QQQ processes steps.
|
||||
*******************************************************************************/
|
||||
public class GeneralProcessUtils
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
** For a list of sourceRecords,
|
||||
** lookup records in the foreignTableName,
|
||||
** that have their foreignTablePrimaryKeyName in the sourceTableForeignKeyFieldName on the sourceRecords.
|
||||
**
|
||||
** e.g., for a list of orders (with a clientId field), build a map of client.id => client record
|
||||
** via getForeignRecordMap(input, orderList, "clientId", "client", "id")
|
||||
*******************************************************************************/
|
||||
public static Map<Serializable, QRecord> getForeignRecordMap(AbstractActionInput parentActionInput, List<QRecord> sourceRecords, String sourceTableForeignKeyFieldName, String foreignTableName, String foreignTablePrimaryKeyName) throws QException
|
||||
{
|
||||
@ -69,9 +75,14 @@ public class GeneralProcessUtils
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** For a list of sourceRecords,
|
||||
** lookup records in the foreignTableName,
|
||||
** that have their foreignTableForeignKeyName in the sourceTableForeignKeyFieldName on the sourceRecords.
|
||||
**
|
||||
** e.g., for a list of orders, build a ListingHash of order.id => List(OrderLine records)
|
||||
** via getForeignRecordListingHashMap(input, orderList, "id", "orderLine", "orderId")
|
||||
*******************************************************************************/
|
||||
public static ListingHash<Serializable, QRecord> getForeignRecordListingHashMap(AbstractActionInput parentActionInput, List<QRecord> sourceRecords, String sourceTableForeignKeyFieldName, String foreignTableName, String foreignTablePrimaryKeyName) throws QException
|
||||
public static ListingHash<Serializable, QRecord> getForeignRecordListingHashMap(AbstractActionInput parentActionInput, List<QRecord> sourceRecords, String sourceTableForeignKeyFieldName, String foreignTableName, String foreignTableForeignKeyName) throws QException
|
||||
{
|
||||
ListingHash<Serializable, QRecord> foreignRecordMap = new ListingHash<>();
|
||||
QueryInput queryInput = new QueryInput(parentActionInput.getInstance());
|
||||
@ -79,11 +90,11 @@ public class GeneralProcessUtils
|
||||
queryInput.setTableName(foreignTableName);
|
||||
List<Serializable> foreignIds = new ArrayList<>(sourceRecords.stream().map(r -> r.getValue(sourceTableForeignKeyFieldName)).toList());
|
||||
|
||||
queryInput.setFilter(new QQueryFilter().withCriteria(new QFilterCriteria(foreignTablePrimaryKeyName, QCriteriaOperator.IN, foreignIds)));
|
||||
queryInput.setFilter(new QQueryFilter().withCriteria(new QFilterCriteria(foreignTableForeignKeyName, QCriteriaOperator.IN, foreignIds)));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
for(QRecord foreignRecord : queryOutput.getRecords())
|
||||
{
|
||||
foreignRecordMap.add(foreignRecord.getValue(foreignTablePrimaryKeyName), foreignRecord);
|
||||
foreignRecordMap.add(foreignRecord.getValue(foreignTableForeignKeyName), foreignRecord);
|
||||
}
|
||||
return foreignRecordMap;
|
||||
}
|
||||
@ -91,7 +102,13 @@ public class GeneralProcessUtils
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** For a list of sourceRecords,
|
||||
** lookup records in the foreignTableName,
|
||||
** that have their foreignTablePrimaryKeyName in the sourceTableForeignKeyFieldName on the sourceRecords.
|
||||
** and set those foreign records as a value in the sourceRecords.
|
||||
**
|
||||
** e.g., for a list of orders (with a clientId field), setValue("client", QRecord(client));
|
||||
** via addForeignRecordsToRecordList(input, orderList, "clientId", "client", "id")
|
||||
*******************************************************************************/
|
||||
public static void addForeignRecordsToRecordList(AbstractActionInput parentActionInput, List<QRecord> sourceRecords, String sourceTableForeignKeyFieldName, String foreignTableName, String foreignTablePrimaryKeyName) throws QException
|
||||
{
|
||||
@ -106,11 +123,16 @@ public class GeneralProcessUtils
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** For a list of sourceRecords,
|
||||
** lookup records in the foreignTableName,
|
||||
** that have their foreignTableForeignKeyName in the sourceTableForeignKeyFieldName on the sourceRecords.
|
||||
**
|
||||
** e.g., for a list of orders, setValue("orderLine", List(QRecord(orderLine)))
|
||||
** via addForeignRecordsListToRecordList(input, orderList, "id", "orderLine", "orderId")
|
||||
*******************************************************************************/
|
||||
public static void addForeignRecordsListToRecordList(AbstractActionInput parentActionInput, List<QRecord> sourceRecords, String sourceTableForeignKeyFieldName, String foreignTableName, String foreignTablePrimaryKeyName) throws QException
|
||||
public static void addForeignRecordsListToRecordList(AbstractActionInput parentActionInput, List<QRecord> sourceRecords, String sourceTableForeignKeyFieldName, String foreignTableName, String foreignTableForeignKeyName) throws QException
|
||||
{
|
||||
ListingHash<Serializable, QRecord> foreignRecordMap = getForeignRecordListingHashMap(parentActionInput, sourceRecords, sourceTableForeignKeyFieldName, foreignTableName, foreignTablePrimaryKeyName);
|
||||
ListingHash<Serializable, QRecord> foreignRecordMap = getForeignRecordListingHashMap(parentActionInput, sourceRecords, sourceTableForeignKeyFieldName, foreignTableName, foreignTableForeignKeyName);
|
||||
for(QRecord sourceRecord : sourceRecords)
|
||||
{
|
||||
List<QRecord> foreignRecordList = foreignRecordMap.get(sourceRecord.getValue(sourceTableForeignKeyFieldName));
|
||||
@ -131,7 +153,8 @@ public class GeneralProcessUtils
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Run a query on tableName, for where fieldName equals fieldValue, and return
|
||||
** the list of QRecords.
|
||||
*******************************************************************************/
|
||||
public static List<QRecord> getRecordListByField(AbstractActionInput parentActionInput, String tableName, String fieldName, Serializable fieldValue) throws QException
|
||||
{
|
||||
@ -146,7 +169,9 @@ public class GeneralProcessUtils
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Query to get one record by a unique key value. That field can be the primary
|
||||
** key, or any other field on the table. Note, if multiple rows do match the value,
|
||||
** only 1 (determined in an unspecified way) is returned.
|
||||
*******************************************************************************/
|
||||
public static Optional<QRecord> getRecordById(AbstractActionInput parentActionInput, String tableName, String fieldName, Serializable fieldValue) throws QException
|
||||
{
|
||||
@ -162,7 +187,10 @@ public class GeneralProcessUtils
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Load all rows from a table.
|
||||
**
|
||||
** Note, this is inherently unsafe, if you were to call it on a table with
|
||||
** too many rows... Caveat emptor.
|
||||
*******************************************************************************/
|
||||
public static List<QRecord> loadTable(AbstractActionInput parentActionInput, String tableName) throws QException
|
||||
{
|
||||
@ -176,7 +204,15 @@ public class GeneralProcessUtils
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Load all rows from a table, into a map, keyed by the keyFieldName.
|
||||
**
|
||||
** Note - null values from the key field are NOT put in the map.
|
||||
**
|
||||
** If multiple values are found for the key, they'll squash each other, and only
|
||||
** one random value will appear.
|
||||
**
|
||||
** Also, note, this is inherently unsafe, if you were to call it on a table with
|
||||
** too many rows... Caveat emptor.
|
||||
*******************************************************************************/
|
||||
public static Map<Serializable, QRecord> loadTableToMap(AbstractActionInput parentActionInput, String tableName, String keyFieldName) throws QException
|
||||
{
|
||||
@ -201,7 +237,14 @@ public class GeneralProcessUtils
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Load all rows from a table, into a ListingHash, keyed by the keyFieldName.
|
||||
**
|
||||
** Note - null values from the key field are NOT put in the map.
|
||||
**
|
||||
** The ordering of the records is not specified.
|
||||
**
|
||||
** Also, note, this is inherently unsafe, if you were to call it on a table with
|
||||
** too many rows... Caveat emptor.
|
||||
*******************************************************************************/
|
||||
public static ListingHash<Serializable, QRecord> loadTableToListingHash(AbstractActionInput parentActionInput, String tableName, String keyFieldName) throws QException
|
||||
{
|
||||
|
@ -26,10 +26,10 @@ import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.HeaderAndDetailTableCodeExecutionLogger;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.Log4jCodeExecutionLogger;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.NoopCodeExecutionLogger;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.QCodeExecutionLoggerInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.StoreScriptLogAndScriptLogLineExecutionLogger;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QCodeException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.ExecuteCodeInput;
|
||||
@ -120,9 +120,9 @@ class ExecuteCodeActionTest
|
||||
void testTableLogger() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
new ScriptsMetaDataProvider().defineStandardScriptsTables(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
new ScriptsMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
|
||||
ExecuteCodeInput executeCodeInput = setupInput(qInstance, Map.of("x", 4), new HeaderAndDetailTableCodeExecutionLogger(1701, 1702));
|
||||
ExecuteCodeInput executeCodeInput = setupInput(qInstance, Map.of("x", 4), new StoreScriptLogAndScriptLogLineExecutionLogger(1701, 1702));
|
||||
ExecuteCodeOutput executeCodeOutput = new ExecuteCodeOutput();
|
||||
new ExecuteCodeAction().run(executeCodeInput, executeCodeOutput);
|
||||
assertEquals(16, executeCodeOutput.getOutput());
|
||||
|
@ -25,11 +25,17 @@ package com.kingsrook.qqq.backend.core.actions.scripts;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.RunAssociatedScriptInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.RunAssociatedScriptOutput;
|
||||
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.tables.get.GetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
||||
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.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.AssociatedScriptCodeReference;
|
||||
@ -56,24 +62,7 @@ class RunAssociatedScriptActionTest
|
||||
@Test
|
||||
void test() throws QException
|
||||
{
|
||||
QInstance instance = TestUtils.defineInstance();
|
||||
QTableMetaData table = instance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withField(new QFieldMetaData("testScriptId", QFieldType.INTEGER))
|
||||
.withAssociatedScript(new AssociatedScript()
|
||||
.withScriptTypeId(1)
|
||||
.withFieldName("testScriptId")
|
||||
);
|
||||
|
||||
new ScriptsMetaDataProvider().defineStandardScriptsTables(instance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
|
||||
TestUtils.insertRecords(instance, table, List.of(
|
||||
new QRecord().withValue("id", 1),
|
||||
new QRecord().withValue("id", 2)
|
||||
));
|
||||
|
||||
TestUtils.insertRecords(instance, instance.getTable("scriptType"), List.of(
|
||||
new QRecord().withValue("id", 1).withValue("name", "Test Script Type")
|
||||
));
|
||||
QInstance instance = setupInstance();
|
||||
|
||||
insertScript(instance, 1, """
|
||||
return "Hello";
|
||||
@ -90,6 +79,9 @@ class RunAssociatedScriptActionTest
|
||||
);
|
||||
RunAssociatedScriptOutput runAssociatedScriptOutput = new RunAssociatedScriptOutput();
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ok - since the core module doesn't have the javascript language support module as a dep, this action will fail - but at least we can confirm it fails with this specific exception! //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
assertThatThrownBy(() -> new RunAssociatedScriptAction().run(runAssociatedScriptInput, runAssociatedScriptOutput))
|
||||
.isInstanceOf(QException.class)
|
||||
.hasRootCauseInstanceOf(ClassNotFoundException.class)
|
||||
@ -98,6 +90,204 @@ class RunAssociatedScriptActionTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private QInstance setupInstance() throws QException
|
||||
{
|
||||
QInstance instance = TestUtils.defineInstance();
|
||||
QTableMetaData table = instance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withField(new QFieldMetaData("testScriptId", QFieldType.INTEGER))
|
||||
.withAssociatedScript(new AssociatedScript()
|
||||
.withScriptTypeId(1)
|
||||
.withFieldName("testScriptId")
|
||||
);
|
||||
|
||||
new ScriptsMetaDataProvider().defineAll(instance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
|
||||
TestUtils.insertRecords(instance, table, List.of(
|
||||
new QRecord().withValue("id", 1),
|
||||
new QRecord().withValue("id", 2)
|
||||
));
|
||||
|
||||
TestUtils.insertRecords(instance, instance.getTable("scriptType"), List.of(
|
||||
new QRecord().withValue("id", 1).withValue("name", "Test Script Type")
|
||||
));
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testRecordNotFound() throws QException
|
||||
{
|
||||
QInstance instance = setupInstance();
|
||||
|
||||
RunAssociatedScriptInput runAssociatedScriptInput = new RunAssociatedScriptInput(instance);
|
||||
runAssociatedScriptInput.setSession(new QSession());
|
||||
runAssociatedScriptInput.setInputValues(Map.of());
|
||||
runAssociatedScriptInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
runAssociatedScriptInput.setCodeReference(new AssociatedScriptCodeReference()
|
||||
.withRecordTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withRecordPrimaryKey(-9999)
|
||||
.withFieldName("testScriptId")
|
||||
);
|
||||
RunAssociatedScriptOutput runAssociatedScriptOutput = new RunAssociatedScriptOutput();
|
||||
|
||||
assertThatThrownBy(() -> new RunAssociatedScriptAction().run(runAssociatedScriptInput, runAssociatedScriptOutput))
|
||||
.isInstanceOf(QNotFoundException.class)
|
||||
.hasMessageMatching("The requested record.*was not found.*");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testNoScriptInRecord() throws QException
|
||||
{
|
||||
QInstance instance = setupInstance();
|
||||
|
||||
RunAssociatedScriptInput runAssociatedScriptInput = new RunAssociatedScriptInput(instance);
|
||||
runAssociatedScriptInput.setSession(new QSession());
|
||||
runAssociatedScriptInput.setInputValues(Map.of());
|
||||
runAssociatedScriptInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
runAssociatedScriptInput.setCodeReference(new AssociatedScriptCodeReference()
|
||||
.withRecordTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withRecordPrimaryKey(1)
|
||||
.withFieldName("testScriptId")
|
||||
);
|
||||
RunAssociatedScriptOutput runAssociatedScriptOutput = new RunAssociatedScriptOutput();
|
||||
|
||||
assertThatThrownBy(() -> new RunAssociatedScriptAction().run(runAssociatedScriptInput, runAssociatedScriptOutput))
|
||||
.isInstanceOf(QNotFoundException.class)
|
||||
.hasMessageMatching("The input record.*does not have a script specified for.*");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testBadScriptIdInRecord() throws QException
|
||||
{
|
||||
QInstance instance = setupInstance();
|
||||
|
||||
UpdateInput updateInput = new UpdateInput(instance);
|
||||
updateInput.setSession(new QSession());
|
||||
updateInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
updateInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("testScriptId", -9998)));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
RunAssociatedScriptInput runAssociatedScriptInput = new RunAssociatedScriptInput(instance);
|
||||
runAssociatedScriptInput.setSession(new QSession());
|
||||
runAssociatedScriptInput.setInputValues(Map.of());
|
||||
runAssociatedScriptInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
runAssociatedScriptInput.setCodeReference(new AssociatedScriptCodeReference()
|
||||
.withRecordTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withRecordPrimaryKey(1)
|
||||
.withFieldName("testScriptId")
|
||||
);
|
||||
RunAssociatedScriptOutput runAssociatedScriptOutput = new RunAssociatedScriptOutput();
|
||||
|
||||
assertThatThrownBy(() -> new RunAssociatedScriptAction().run(runAssociatedScriptInput, runAssociatedScriptOutput))
|
||||
.isInstanceOf(QNotFoundException.class)
|
||||
.hasMessageMatching("The script for record .* was not found.*");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testNoCurrentScriptRevisionOnScript() throws QException
|
||||
{
|
||||
QInstance instance = setupInstance();
|
||||
|
||||
insertScript(instance, 1, """
|
||||
return "Hello";
|
||||
""");
|
||||
|
||||
GetInput getInput = new GetInput(instance);
|
||||
getInput.setSession(new QSession());
|
||||
getInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
getInput.setPrimaryKey(1);
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
Integer scriptId = getOutput.getRecord().getValueInteger("testScriptId");
|
||||
|
||||
UpdateInput updateInput = new UpdateInput(instance);
|
||||
updateInput.setSession(new QSession());
|
||||
updateInput.setTableName("script");
|
||||
updateInput.setRecords(List.of(new QRecord().withValue("id", scriptId).withValue("currentScriptRevisionId", null)));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
RunAssociatedScriptInput runAssociatedScriptInput = new RunAssociatedScriptInput(instance);
|
||||
runAssociatedScriptInput.setSession(new QSession());
|
||||
runAssociatedScriptInput.setInputValues(Map.of());
|
||||
runAssociatedScriptInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
runAssociatedScriptInput.setCodeReference(new AssociatedScriptCodeReference()
|
||||
.withRecordTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withRecordPrimaryKey(1)
|
||||
.withFieldName("testScriptId")
|
||||
);
|
||||
RunAssociatedScriptOutput runAssociatedScriptOutput = new RunAssociatedScriptOutput();
|
||||
|
||||
assertThatThrownBy(() -> new RunAssociatedScriptAction().run(runAssociatedScriptInput, runAssociatedScriptOutput))
|
||||
.isInstanceOf(QNotFoundException.class)
|
||||
.hasMessageMatching("The script for record .* does not have a current version.*");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testBadCurrentScriptRevisionOnScript() throws QException
|
||||
{
|
||||
QInstance instance = setupInstance();
|
||||
|
||||
insertScript(instance, 1, """
|
||||
return "Hello";
|
||||
""");
|
||||
|
||||
GetInput getInput = new GetInput(instance);
|
||||
getInput.setSession(new QSession());
|
||||
getInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
getInput.setPrimaryKey(1);
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
Integer scriptId = getOutput.getRecord().getValueInteger("testScriptId");
|
||||
|
||||
UpdateInput updateInput = new UpdateInput(instance);
|
||||
updateInput.setSession(new QSession());
|
||||
updateInput.setTableName("script");
|
||||
updateInput.setRecords(List.of(new QRecord().withValue("id", scriptId).withValue("currentScriptRevisionId", 9997)));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
RunAssociatedScriptInput runAssociatedScriptInput = new RunAssociatedScriptInput(instance);
|
||||
runAssociatedScriptInput.setSession(new QSession());
|
||||
runAssociatedScriptInput.setInputValues(Map.of());
|
||||
runAssociatedScriptInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
runAssociatedScriptInput.setCodeReference(new AssociatedScriptCodeReference()
|
||||
.withRecordTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withRecordPrimaryKey(1)
|
||||
.withFieldName("testScriptId")
|
||||
);
|
||||
RunAssociatedScriptOutput runAssociatedScriptOutput = new RunAssociatedScriptOutput();
|
||||
|
||||
assertThatThrownBy(() -> new RunAssociatedScriptAction().run(runAssociatedScriptInput, runAssociatedScriptOutput))
|
||||
.isInstanceOf(QNotFoundException.class)
|
||||
.hasMessageMatching("The current revision of the script for record .* was not found.*");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -24,15 +24,12 @@ package com.kingsrook.qqq.backend.core.actions.scripts;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
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.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.get.GetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
||||
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;
|
||||
@ -47,6 +44,7 @@ import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -85,7 +83,7 @@ class StoreAssociatedScriptActionTest
|
||||
.withFieldName("otherScriptId")
|
||||
);
|
||||
|
||||
new ScriptsMetaDataProvider().defineStandardScriptsTables(instance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
new ScriptsMetaDataProvider().defineAll(instance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
|
||||
TestUtils.insertRecords(instance, table, List.of(
|
||||
new QRecord().withValue("id", 1),
|
||||
@ -149,7 +147,6 @@ class StoreAssociatedScriptActionTest
|
||||
assertValueInField(instance, TestUtils.TABLE_NAME_PERSON_MEMORY, 1, "testScriptId", 1);
|
||||
assertValueInField(instance, TestUtils.TABLE_NAME_PERSON_MEMORY, 1, "otherScriptId", 3);
|
||||
assertValueInField(instance, "script", 3, "currentScriptRevisionId", 4);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -157,16 +154,19 @@ class StoreAssociatedScriptActionTest
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private Serializable assertValueInField(QInstance instance, String tableName, Serializable recordId, String fieldName, Serializable value) throws QException
|
||||
private void assertValueInField(QInstance instance, String tableName, Serializable recordId, String fieldName, Serializable value) throws QException
|
||||
{
|
||||
QueryInput queryInput = new QueryInput(instance);
|
||||
queryInput.setSession(new QSession());
|
||||
queryInput.setTableName(tableName);
|
||||
queryInput.setFilter(new QQueryFilter().withCriteria(new QFilterCriteria("id", QCriteriaOperator.EQUALS, List.of(recordId))));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
Serializable actual = queryOutput.getRecords().get(0).getValue(fieldName);
|
||||
assertEquals(value, actual);
|
||||
return (actual);
|
||||
GetInput getInput = new GetInput(instance);
|
||||
getInput.setSession(new QSession());
|
||||
getInput.setTableName(tableName);
|
||||
getInput.setPrimaryKey(recordId);
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
if(getOutput.getRecord() == null)
|
||||
{
|
||||
fail("Expected value [" + value + "] in field [" + fieldName + "], record [" + tableName + "][" + recordId + "], but the record wasn't found...");
|
||||
}
|
||||
Serializable actual = getOutput.getRecord().getValue(fieldName);
|
||||
assertEquals(value, actual, "Expected value in field [" + fieldName + "], record [" + tableName + "][" + recordId + "]");
|
||||
}
|
||||
|
||||
}
|
@ -27,6 +27,7 @@ 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;
|
||||
|
||||
|
||||
@ -39,6 +40,7 @@ class TestScriptActionTest
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
@Disabled("Not yet done.")
|
||||
void test() throws QException
|
||||
{
|
||||
QInstance instance = TestUtils.defineInstance();
|
||||
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.logging;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
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.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.ScriptsMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for BuildScriptLogAndScriptLogLineExecutionLogger
|
||||
*******************************************************************************/
|
||||
class BuildScriptLogAndScriptLogLineExecutionLoggerTest
|
||||
{
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void test() throws QException
|
||||
{
|
||||
QInstance instance = TestUtils.defineInstance();
|
||||
new ScriptsMetaDataProvider().defineAll(instance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
ExecuteCodeInput executeCodeInput = new ExecuteCodeInput(instance);
|
||||
executeCodeInput.setSession(new QSession());
|
||||
executeCodeInput.setInput(Map.of("a", 1));
|
||||
|
||||
BuildScriptLogAndScriptLogLineExecutionLogger logger = new BuildScriptLogAndScriptLogLineExecutionLogger(9999, 8888);
|
||||
logger.acceptExecutionStart(executeCodeInput);
|
||||
logger.acceptLogLine("This is a log");
|
||||
logger.acceptLogLine("This is also a log");
|
||||
logger.acceptExecutionEnd(true);
|
||||
|
||||
QRecord scriptLog = logger.getScriptLog();
|
||||
assertNull(scriptLog.getValueInteger("id"));
|
||||
assertNotNull(scriptLog.getValue("startTimestamp"));
|
||||
assertNotNull(scriptLog.getValue("endTimestamp"));
|
||||
assertNotNull(scriptLog.getValue("runTimeMillis"));
|
||||
assertEquals(9999, scriptLog.getValueInteger("scriptId"));
|
||||
assertEquals(8888, scriptLog.getValueInteger("scriptRevisionId"));
|
||||
assertEquals("{a=1}", scriptLog.getValueString("input"));
|
||||
assertEquals("true", scriptLog.getValueString("output"));
|
||||
assertNull(scriptLog.getValueString("exception"));
|
||||
assertFalse(scriptLog.getValueBoolean("hadError"));
|
||||
|
||||
List<QRecord> scriptLogLineRecords = logger.getScriptLogLines();
|
||||
assertEquals(2, scriptLogLineRecords.size());
|
||||
QRecord scriptLogLine = scriptLogLineRecords.get(0);
|
||||
assertNull(scriptLogLine.getValueInteger("scriptLogId"));
|
||||
assertNotNull(scriptLogLine.getValue("timestamp"));
|
||||
assertEquals("This is a log", scriptLogLine.getValueString("text"));
|
||||
scriptLogLine = scriptLogLineRecords.get(1);
|
||||
assertEquals("This is also a log", scriptLogLine.getValueString("text"));
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.logging;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
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.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.ScriptsMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryRecordStore;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for StoreScriptLogAndScriptLogLineExecutionLogger
|
||||
*******************************************************************************/
|
||||
class StoreScriptLogAndScriptLogLineExecutionLoggerTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@BeforeEach
|
||||
@AfterEach
|
||||
void beforeAndAfterEach()
|
||||
{
|
||||
MemoryRecordStore.getInstance().reset();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void test() throws QException
|
||||
{
|
||||
QInstance instance = TestUtils.defineInstance();
|
||||
new ScriptsMetaDataProvider().defineAll(instance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
ExecuteCodeInput executeCodeInput = new ExecuteCodeInput(instance);
|
||||
executeCodeInput.setSession(new QSession());
|
||||
executeCodeInput.setInput(Map.of("a", 1));
|
||||
|
||||
StoreScriptLogAndScriptLogLineExecutionLogger logger = new StoreScriptLogAndScriptLogLineExecutionLogger(9999, 8888);
|
||||
logger.acceptExecutionStart(executeCodeInput);
|
||||
logger.acceptLogLine("This is a log");
|
||||
logger.acceptLogLine("This is also a log");
|
||||
logger.acceptExecutionEnd(true);
|
||||
|
||||
List<QRecord> scriptLogRecords = TestUtils.queryTable(instance, "scriptLog");
|
||||
assertEquals(1, scriptLogRecords.size());
|
||||
QRecord scriptLog = scriptLogRecords.get(0);
|
||||
assertNotNull(scriptLog.getValueInteger("id"));
|
||||
assertNotNull(scriptLog.getValue("startTimestamp"));
|
||||
assertNotNull(scriptLog.getValue("endTimestamp"));
|
||||
assertNotNull(scriptLog.getValue("runTimeMillis"));
|
||||
assertEquals(9999, scriptLog.getValueInteger("scriptId"));
|
||||
assertEquals(8888, scriptLog.getValueInteger("scriptRevisionId"));
|
||||
assertEquals("{a=1}", scriptLog.getValueString("input"));
|
||||
assertEquals("true", scriptLog.getValueString("output"));
|
||||
assertNull(scriptLog.getValueString("exception"));
|
||||
assertFalse(scriptLog.getValueBoolean("hadError"));
|
||||
|
||||
List<QRecord> scriptLogLineRecords = TestUtils.queryTable(instance, "scriptLogLine");
|
||||
assertEquals(2, scriptLogLineRecords.size());
|
||||
QRecord scriptLogLine = scriptLogLineRecords.get(0);
|
||||
assertEquals(scriptLog.getValueInteger("id"), scriptLogLine.getValueInteger("scriptLogId"));
|
||||
assertNotNull(scriptLogLine.getValue("timestamp"));
|
||||
assertEquals("This is a log", scriptLogLine.getValueString("text"));
|
||||
scriptLogLine = scriptLogLineRecords.get(1);
|
||||
assertEquals("This is also a log", scriptLogLine.getValueString("text"));
|
||||
}
|
||||
}
|
@ -51,6 +51,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppSection;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValue;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.queues.SQSQueueProviderMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection;
|
||||
@ -805,6 +806,7 @@ class QInstanceValidatorTest
|
||||
possibleValueSource.setOrderByFields(List.of(new QFilterOrderBy("id")));
|
||||
possibleValueSource.setCustomCodeReference(new QCodeReference());
|
||||
possibleValueSource.setEnumValues(null);
|
||||
possibleValueSource.setType(QPossibleValueSourceType.ENUM);
|
||||
},
|
||||
"should not have a tableName",
|
||||
"should not have searchFields",
|
||||
@ -831,6 +833,7 @@ class QInstanceValidatorTest
|
||||
possibleValueSource.setOrderByFields(new ArrayList<>());
|
||||
possibleValueSource.setCustomCodeReference(new QCodeReference());
|
||||
possibleValueSource.setEnumValues(List.of(new QPossibleValue<>("test")));
|
||||
possibleValueSource.setType(QPossibleValueSourceType.TABLE);
|
||||
},
|
||||
"should not have enum values",
|
||||
"should not have a customCodeReference",
|
||||
@ -860,6 +863,7 @@ class QInstanceValidatorTest
|
||||
possibleValueSource.setOrderByFields(List.of(new QFilterOrderBy("id")));
|
||||
possibleValueSource.setCustomCodeReference(null);
|
||||
possibleValueSource.setEnumValues(List.of(new QPossibleValue<>("test")));
|
||||
possibleValueSource.setType(QPossibleValueSourceType.CUSTOM);
|
||||
},
|
||||
"should not have enum values",
|
||||
"should not have a tableName",
|
||||
|
Reference in New Issue
Block a user