mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 13:10:44 +00:00
Update to cache script and revision ids (could be better); new AccumulatingBuildScriptLogAndScriptLogLineExecutionLogger, to just do 2 inserts across multiple script runs; add child-log-lines to script log table
This commit is contained in:
@ -24,7 +24,11 @@ package com.kingsrook.qqq.backend.core.actions.scripts;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.scripts.logging.QCodeExecutionLoggerInterface;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.scripts.logging.ScriptExecutionLoggerInterface;
|
||||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.StoreScriptLogAndScriptLogLineExecutionLogger;
|
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.actions.tables.GetAction;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
@ -35,6 +39,7 @@ import com.kingsrook.qqq.backend.core.model.actions.scripts.RunAssociatedScriptI
|
|||||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.RunAssociatedScriptOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.scripts.RunAssociatedScriptOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.code.AssociatedScriptCodeReference;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType;
|
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType;
|
||||||
import com.kingsrook.qqq.backend.core.model.scripts.Script;
|
import com.kingsrook.qqq.backend.core.model.scripts.Script;
|
||||||
@ -46,6 +51,8 @@ import com.kingsrook.qqq.backend.core.model.scripts.ScriptRevision;
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class RunAssociatedScriptAction
|
public class RunAssociatedScriptAction
|
||||||
{
|
{
|
||||||
|
private Map<AssociatedScriptCodeReference, ScriptRevision> scriptRevisionCache = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
@ -54,21 +61,7 @@ public class RunAssociatedScriptAction
|
|||||||
{
|
{
|
||||||
ActionHelper.validateSession(input);
|
ActionHelper.validateSession(input);
|
||||||
|
|
||||||
Serializable scriptId = getScriptId(input);
|
ScriptRevision scriptRevision = getScriptRevision(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();
|
ExecuteCodeInput executeCodeInput = new ExecuteCodeInput();
|
||||||
executeCodeInput.setInput(new HashMap<>(input.getInputValues()));
|
executeCodeInput.setInput(new HashMap<>(input.getInputValues()));
|
||||||
@ -78,7 +71,21 @@ public class RunAssociatedScriptAction
|
|||||||
executeCodeInput.getContext().put("output", input.getOutputObject());
|
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.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()));
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// let caller supply a logger, or by default use StoreScriptLogAndScriptLogLineExecutionLogger //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
QCodeExecutionLoggerInterface executionLogger = Objects.requireNonNullElseGet(input.getLogger(), () -> new StoreScriptLogAndScriptLogLineExecutionLogger(scriptRevision.getScriptId(), scriptRevision.getId()));
|
||||||
|
executeCodeInput.setExecutionLogger(executionLogger);
|
||||||
|
if(executionLogger instanceof ScriptExecutionLoggerInterface scriptExecutionLoggerInterface)
|
||||||
|
{
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// if logger is aware of scripts (as opposed to a generic CodeExecution logger), give it the ids. //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
scriptExecutionLoggerInterface.setScriptId(scriptRevision.getScriptId());
|
||||||
|
scriptExecutionLoggerInterface.setScriptRevisionId(scriptRevision.getId());
|
||||||
|
}
|
||||||
|
|
||||||
ExecuteCodeOutput executeCodeOutput = new ExecuteCodeOutput();
|
ExecuteCodeOutput executeCodeOutput = new ExecuteCodeOutput();
|
||||||
new ExecuteCodeAction().run(executeCodeInput, executeCodeOutput);
|
new ExecuteCodeAction().run(executeCodeInput, executeCodeOutput);
|
||||||
|
|
||||||
@ -87,6 +94,36 @@ public class RunAssociatedScriptAction
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private ScriptRevision getScriptRevision(RunAssociatedScriptInput input) throws QException
|
||||||
|
{
|
||||||
|
if(!scriptRevisionCache.containsKey(input.getCodeReference()))
|
||||||
|
{
|
||||||
|
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());
|
||||||
|
scriptRevisionCache.put(input.getCodeReference(), scriptRevision);
|
||||||
|
}
|
||||||
|
|
||||||
|
return scriptRevisionCache.get(input.getCodeReference());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||||
|
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||||
|
* contact@kingsrook.com
|
||||||
|
* https://github.com/Kingsrook/
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.core.actions.scripts.logging;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||||
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
|
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.data.QRecord;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public class AccumulatingBuildScriptLogAndScriptLogLineExecutionLogger extends BuildScriptLogAndScriptLogLineExecutionLogger implements ScriptExecutionLoggerInterface
|
||||||
|
{
|
||||||
|
private static final QLogger LOG = QLogger.getLogger(AccumulatingBuildScriptLogAndScriptLogLineExecutionLogger.class);
|
||||||
|
|
||||||
|
private List<QRecord> scriptLogs = new ArrayList<>();
|
||||||
|
private List<List<QRecord>> scriptLogLines = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public AccumulatingBuildScriptLogAndScriptLogLineExecutionLogger()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public void acceptExecutionStart(ExecuteCodeInput executeCodeInput)
|
||||||
|
{
|
||||||
|
super.acceptExecutionStart(executeCodeInput);
|
||||||
|
super.setScriptLogLines(new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public void acceptException(Exception exception)
|
||||||
|
{
|
||||||
|
accumulate(null, exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public void acceptExecutionEnd(Serializable output)
|
||||||
|
{
|
||||||
|
accumulate(output, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private void accumulate(Serializable output, Exception exception)
|
||||||
|
{
|
||||||
|
super.updateHeaderAtEnd(output, exception);
|
||||||
|
scriptLogs.add(super.getScriptLog());
|
||||||
|
scriptLogLines.add(new ArrayList<>(super.getScriptLogLines()));
|
||||||
|
super.getScriptLogLines().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void storeAndClear()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
InsertInput insertInput = new InsertInput();
|
||||||
|
insertInput.setTableName("scriptLog");
|
||||||
|
insertInput.setRecords(scriptLogs);
|
||||||
|
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||||
|
|
||||||
|
List<QRecord> flatScriptLogLines = new ArrayList<>();
|
||||||
|
for(int i = 0; i < insertOutput.getRecords().size(); i++)
|
||||||
|
{
|
||||||
|
QRecord insertedScriptLog = insertOutput.getRecords().get(i);
|
||||||
|
List<QRecord> subScriptLogLines = scriptLogLines.get(i);
|
||||||
|
subScriptLogLines.forEach(r -> r.setValue("scriptLogId", insertedScriptLog.getValueInteger("id")));
|
||||||
|
flatScriptLogLines.addAll(subScriptLogLines);
|
||||||
|
}
|
||||||
|
|
||||||
|
insertInput = new InsertInput();
|
||||||
|
insertInput.setTableName("scriptLogLine");
|
||||||
|
insertInput.setRecords(flatScriptLogLines);
|
||||||
|
new InsertAction().execute(insertInput);
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
LOG.warn("Error storing script logs", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
scriptLogs.clear();
|
||||||
|
scriptLogLines.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public void setScriptId(Integer scriptId)
|
||||||
|
{
|
||||||
|
super.setScriptId(scriptId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public void setScriptRevisionId(Integer scriptRevisionId)
|
||||||
|
{
|
||||||
|
super.setScriptRevisionId(scriptRevisionId);
|
||||||
|
}
|
||||||
|
}
|
@ -39,7 +39,7 @@ import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
|||||||
** scriptLogLine records - but doesn't insert them. e.g., useful for testing
|
** scriptLogLine records - but doesn't insert them. e.g., useful for testing
|
||||||
** (both in junit, and for users in-app).
|
** (both in junit, and for users in-app).
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class BuildScriptLogAndScriptLogLineExecutionLogger implements QCodeExecutionLoggerInterface
|
public class BuildScriptLogAndScriptLogLineExecutionLogger implements QCodeExecutionLoggerInterface, ScriptExecutionLoggerInterface
|
||||||
{
|
{
|
||||||
private static final QLogger LOG = QLogger.getLogger(BuildScriptLogAndScriptLogLineExecutionLogger.class);
|
private static final QLogger LOG = QLogger.getLogger(BuildScriptLogAndScriptLogLineExecutionLogger.class);
|
||||||
|
|
||||||
@ -217,4 +217,37 @@ public class BuildScriptLogAndScriptLogLineExecutionLogger implements QCodeExecu
|
|||||||
{
|
{
|
||||||
this.scriptLog = scriptLog;
|
this.scriptLog = scriptLog;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for scriptLogLines
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
protected void setScriptLogLines(List<QRecord> scriptLogLines)
|
||||||
|
{
|
||||||
|
this.scriptLogLines = scriptLogLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public void setScriptId(Integer scriptId)
|
||||||
|
{
|
||||||
|
this.scriptId = scriptId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public void setScriptRevisionId(Integer scriptRevisionId)
|
||||||
|
{
|
||||||
|
this.scriptRevisionId = scriptRevisionId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||||
|
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||||
|
* contact@kingsrook.com
|
||||||
|
* https://github.com/Kingsrook/
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.core.actions.scripts.logging;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public interface ScriptExecutionLoggerInterface
|
||||||
|
{
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
void setScriptId(Integer scriptId);
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
void setScriptRevisionId(Integer scriptRevisionId);
|
||||||
|
}
|
@ -24,6 +24,7 @@ package com.kingsrook.qqq.backend.core.model.actions.scripts;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.scripts.logging.QCodeExecutionLoggerInterface;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.code.AssociatedScriptCodeReference;
|
import com.kingsrook.qqq.backend.core.model.metadata.code.AssociatedScriptCodeReference;
|
||||||
|
|
||||||
@ -35,6 +36,7 @@ public class RunAssociatedScriptInput extends AbstractTableActionInput
|
|||||||
{
|
{
|
||||||
private AssociatedScriptCodeReference codeReference;
|
private AssociatedScriptCodeReference codeReference;
|
||||||
private Map<String, Serializable> inputValues;
|
private Map<String, Serializable> inputValues;
|
||||||
|
private QCodeExecutionLoggerInterface logger;
|
||||||
|
|
||||||
private Serializable outputObject;
|
private Serializable outputObject;
|
||||||
|
|
||||||
@ -149,4 +151,35 @@ public class RunAssociatedScriptInput extends AbstractTableActionInput
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for logger
|
||||||
|
*******************************************************************************/
|
||||||
|
public QCodeExecutionLoggerInterface getLogger()
|
||||||
|
{
|
||||||
|
return (this.logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for logger
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setLogger(QCodeExecutionLoggerInterface logger)
|
||||||
|
{
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for logger
|
||||||
|
*******************************************************************************/
|
||||||
|
public RunAssociatedScriptInput withLogger(QCodeExecutionLoggerInterface logger)
|
||||||
|
{
|
||||||
|
this.logger = logger;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.core.model.metadata.code;
|
|||||||
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -136,4 +137,34 @@ public class AssociatedScriptCodeReference extends QCodeReference
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o)
|
||||||
|
{
|
||||||
|
if(this == o)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(o == null || getClass() != o.getClass())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
AssociatedScriptCodeReference that = (AssociatedScriptCodeReference) o;
|
||||||
|
return Objects.equals(recordTable, that.recordTable) && Objects.equals(recordPrimaryKey, that.recordPrimaryKey) && Objects.equals(fieldName, that.fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
return Objects.hash(recordTable, recordPrimaryKey, fieldName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,11 +25,16 @@ package com.kingsrook.qqq.backend.core.model.scripts;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.ChildRecordListRenderer;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
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.QInstance;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType;
|
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.fields.FieldAdornment;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinType;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
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.possiblevalues.QPossibleValueSource;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
||||||
@ -51,6 +56,35 @@ public class ScriptsMetaDataProvider
|
|||||||
{
|
{
|
||||||
defineStandardScriptsTables(instance, backendName, backendDetailEnricher);
|
defineStandardScriptsTables(instance, backendName, backendDetailEnricher);
|
||||||
defineStandardScriptsPossibleValueSources(instance);
|
defineStandardScriptsPossibleValueSources(instance);
|
||||||
|
defineStandardScriptsJoins(instance);
|
||||||
|
defineStandardScriptsWidgets(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void defineStandardScriptsWidgets(QInstance instance)
|
||||||
|
{
|
||||||
|
instance.addWidget(ChildRecordListRenderer.widgetMetaDataBuilder(instance.getJoin(QJoinMetaData.makeInferredJoinName(ScriptLog.TABLE_NAME, ScriptLogLine.TABLE_NAME)))
|
||||||
|
.withLabel("Log Lines")
|
||||||
|
.getWidgetMetaData());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void defineStandardScriptsJoins(QInstance instance)
|
||||||
|
{
|
||||||
|
instance.addJoin(new QJoinMetaData()
|
||||||
|
.withType(JoinType.ONE_TO_MANY)
|
||||||
|
.withLeftTable(ScriptLog.TABLE_NAME)
|
||||||
|
.withRightTable(ScriptLogLine.TABLE_NAME)
|
||||||
|
.withJoinOn(new JoinOn("id", "scriptLogId"))
|
||||||
|
.withOrderBy(new QFilterOrderBy("id"))
|
||||||
|
.withInferredName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -201,7 +235,8 @@ public class ScriptsMetaDataProvider
|
|||||||
.withSection(new QFieldSection("script", new QIcon().withName("data_object"), Tier.T2, List.of("scriptId", "scriptRevisionId")))
|
.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("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("error", "Error", new QIcon().withName("error_outline"), Tier.T2, List.of("hadError", "error")))
|
||||||
.withSection(new QFieldSection("inputOutput", "Input/Output", new QIcon().withName("chat"), Tier.T2, List.of("input", "output"))));
|
.withSection(new QFieldSection("inputOutput", "Input/Output", new QIcon().withName("chat"), Tier.T2, List.of("input", "output")))
|
||||||
|
.withSection(new QFieldSection("lines", new QIcon().withName("horizontal_rule"), Tier.T2).withWidgetName(QJoinMetaData.makeInferredJoinName(ScriptLog.TABLE_NAME, ScriptLogLine.TABLE_NAME))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -56,6 +56,7 @@ public class MemoryRecordStore
|
|||||||
private static boolean collectStatistics = false;
|
private static boolean collectStatistics = false;
|
||||||
|
|
||||||
public static final String STAT_QUERIES_RAN = "queriesRan";
|
public static final String STAT_QUERIES_RAN = "queriesRan";
|
||||||
|
public static final String STAT_INSERTS_RAN = "insertsRan";
|
||||||
|
|
||||||
private static final Map<String, Integer> statistics = Collections.synchronizedMap(new HashMap<>());
|
private static final Map<String, Integer> statistics = Collections.synchronizedMap(new HashMap<>());
|
||||||
|
|
||||||
@ -173,6 +174,8 @@ public class MemoryRecordStore
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public List<QRecord> insert(InsertInput input, boolean returnInsertedRecords)
|
public List<QRecord> insert(InsertInput input, boolean returnInsertedRecords)
|
||||||
{
|
{
|
||||||
|
incrementStatistic(input);
|
||||||
|
|
||||||
if(input.getRecords() == null)
|
if(input.getRecords() == null)
|
||||||
{
|
{
|
||||||
return (new ArrayList<>());
|
return (new ArrayList<>());
|
||||||
@ -324,6 +327,10 @@ public class MemoryRecordStore
|
|||||||
{
|
{
|
||||||
incrementStatistic(STAT_QUERIES_RAN);
|
incrementStatistic(STAT_QUERIES_RAN);
|
||||||
}
|
}
|
||||||
|
else if(input instanceof InsertInput)
|
||||||
|
{
|
||||||
|
incrementStatistic(STAT_INSERTS_RAN);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ import java.io.Serializable;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.scripts.logging.AccumulatingBuildScriptLogAndScriptLogLineExecutionLogger;
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
||||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
@ -45,10 +46,16 @@ import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.AssociatedScript;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.AssociatedScript;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.scripts.ScriptLog;
|
||||||
import com.kingsrook.qqq.backend.core.model.scripts.ScriptsMetaDataProvider;
|
import com.kingsrook.qqq.backend.core.model.scripts.ScriptsMetaDataProvider;
|
||||||
|
import com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryRecordStore;
|
||||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
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 org.junit.jupiter.api.Test;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -57,15 +64,28 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
|||||||
class RunAssociatedScriptActionTest extends BaseTest
|
class RunAssociatedScriptActionTest extends BaseTest
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@BeforeEach
|
||||||
|
@AfterEach
|
||||||
|
void beforeAndAfterEach()
|
||||||
|
{
|
||||||
|
MemoryRecordStore.getInstance().reset();
|
||||||
|
MemoryRecordStore.resetStatistics();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@Test
|
@Test
|
||||||
void test() throws QException
|
void test() throws QException
|
||||||
{
|
{
|
||||||
QInstance instance = setupInstance();
|
setupInstance();
|
||||||
|
|
||||||
insertScript(instance, 1, """
|
insertScript(1, """
|
||||||
return "Hello";
|
return "Hello";
|
||||||
""");
|
""");
|
||||||
|
|
||||||
@ -86,6 +106,11 @@ class RunAssociatedScriptActionTest extends BaseTest
|
|||||||
.isInstanceOf(QException.class)
|
.isInstanceOf(QException.class)
|
||||||
.hasRootCauseInstanceOf(ClassNotFoundException.class)
|
.hasRootCauseInstanceOf(ClassNotFoundException.class)
|
||||||
.hasRootCauseMessage("com.kingsrook.qqq.languages.javascript.QJavaScriptExecutor");
|
.hasRootCauseMessage("com.kingsrook.qqq.languages.javascript.QJavaScriptExecutor");
|
||||||
|
|
||||||
|
/////////////////////////////////////
|
||||||
|
// assert that a log was generated //
|
||||||
|
/////////////////////////////////////
|
||||||
|
assertEquals(1, TestUtils.queryTable(ScriptLog.TABLE_NAME).size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -93,7 +118,61 @@ class RunAssociatedScriptActionTest extends BaseTest
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private QInstance setupInstance() throws QException
|
@Test
|
||||||
|
void testOverridingLoggerAndCachingScriptLookups() throws QException
|
||||||
|
{
|
||||||
|
setupInstance();
|
||||||
|
|
||||||
|
insertScript(1, """
|
||||||
|
return "Hello";
|
||||||
|
""");
|
||||||
|
|
||||||
|
AccumulatingBuildScriptLogAndScriptLogLineExecutionLogger scriptLogger = new AccumulatingBuildScriptLogAndScriptLogLineExecutionLogger();
|
||||||
|
|
||||||
|
RunAssociatedScriptInput runAssociatedScriptInput = new RunAssociatedScriptInput();
|
||||||
|
runAssociatedScriptInput.setInputValues(Map.of());
|
||||||
|
runAssociatedScriptInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||||
|
runAssociatedScriptInput.setLogger(scriptLogger);
|
||||||
|
runAssociatedScriptInput.setCodeReference(new AssociatedScriptCodeReference()
|
||||||
|
.withRecordTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||||
|
.withRecordPrimaryKey(1)
|
||||||
|
.withFieldName("testScriptId")
|
||||||
|
);
|
||||||
|
RunAssociatedScriptOutput runAssociatedScriptOutput = new RunAssociatedScriptOutput();
|
||||||
|
|
||||||
|
MemoryRecordStore.setCollectStatistics(true);
|
||||||
|
RunAssociatedScriptAction runAssociatedScriptAction = new RunAssociatedScriptAction();
|
||||||
|
|
||||||
|
int N = 10;
|
||||||
|
for(int i = 0; i < N; i++)
|
||||||
|
{
|
||||||
|
assertThatThrownBy(() -> runAssociatedScriptAction.run(runAssociatedScriptInput, runAssociatedScriptOutput));
|
||||||
|
}
|
||||||
|
|
||||||
|
scriptLogger.storeAndClear();
|
||||||
|
|
||||||
|
/////////////////////////////////////
|
||||||
|
// assert that logs were generated //
|
||||||
|
/////////////////////////////////////
|
||||||
|
assertEquals(N, TestUtils.queryTable(ScriptLog.TABLE_NAME).size());
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// and we should have just ran 2 inserts - for the log & logLines (even though empty) //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
assertEquals(2, MemoryRecordStore.getStatistics().get(MemoryRecordStore.STAT_INSERTS_RAN));
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// and we shouldn't have run N queries (which we would have (at least), if we would have built a new Action object inside the loop) //
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
assertThat(MemoryRecordStore.getStatistics().get(MemoryRecordStore.STAT_QUERIES_RAN)).isLessThan(N);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private void setupInstance() throws QException
|
||||||
{
|
{
|
||||||
QInstance instance = QContext.getQInstance();
|
QInstance instance = QContext.getQInstance();
|
||||||
QTableMetaData table = instance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
QTableMetaData table = instance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||||
@ -113,7 +192,6 @@ class RunAssociatedScriptActionTest extends BaseTest
|
|||||||
TestUtils.insertRecords(instance, instance.getTable("scriptType"), List.of(
|
TestUtils.insertRecords(instance, instance.getTable("scriptType"), List.of(
|
||||||
new QRecord().withValue("id", 1).withValue("name", "Test Script Type")
|
new QRecord().withValue("id", 1).withValue("name", "Test Script Type")
|
||||||
));
|
));
|
||||||
return instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -124,7 +202,7 @@ class RunAssociatedScriptActionTest extends BaseTest
|
|||||||
@Test
|
@Test
|
||||||
void testRecordNotFound() throws QException
|
void testRecordNotFound() throws QException
|
||||||
{
|
{
|
||||||
QInstance instance = setupInstance();
|
setupInstance();
|
||||||
|
|
||||||
RunAssociatedScriptInput runAssociatedScriptInput = new RunAssociatedScriptInput();
|
RunAssociatedScriptInput runAssociatedScriptInput = new RunAssociatedScriptInput();
|
||||||
runAssociatedScriptInput.setInputValues(Map.of());
|
runAssociatedScriptInput.setInputValues(Map.of());
|
||||||
@ -149,7 +227,7 @@ class RunAssociatedScriptActionTest extends BaseTest
|
|||||||
@Test
|
@Test
|
||||||
void testNoScriptInRecord() throws QException
|
void testNoScriptInRecord() throws QException
|
||||||
{
|
{
|
||||||
QInstance instance = setupInstance();
|
setupInstance();
|
||||||
|
|
||||||
RunAssociatedScriptInput runAssociatedScriptInput = new RunAssociatedScriptInput();
|
RunAssociatedScriptInput runAssociatedScriptInput = new RunAssociatedScriptInput();
|
||||||
runAssociatedScriptInput.setInputValues(Map.of());
|
runAssociatedScriptInput.setInputValues(Map.of());
|
||||||
@ -174,7 +252,7 @@ class RunAssociatedScriptActionTest extends BaseTest
|
|||||||
@Test
|
@Test
|
||||||
void testBadScriptIdInRecord() throws QException
|
void testBadScriptIdInRecord() throws QException
|
||||||
{
|
{
|
||||||
QInstance instance = setupInstance();
|
setupInstance();
|
||||||
|
|
||||||
UpdateInput updateInput = new UpdateInput();
|
UpdateInput updateInput = new UpdateInput();
|
||||||
updateInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
updateInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||||
@ -204,9 +282,9 @@ class RunAssociatedScriptActionTest extends BaseTest
|
|||||||
@Test
|
@Test
|
||||||
void testNoCurrentScriptRevisionOnScript() throws QException
|
void testNoCurrentScriptRevisionOnScript() throws QException
|
||||||
{
|
{
|
||||||
QInstance instance = setupInstance();
|
setupInstance();
|
||||||
|
|
||||||
insertScript(instance, 1, """
|
insertScript(1, """
|
||||||
return "Hello";
|
return "Hello";
|
||||||
""");
|
""");
|
||||||
|
|
||||||
@ -244,9 +322,9 @@ class RunAssociatedScriptActionTest extends BaseTest
|
|||||||
@Test
|
@Test
|
||||||
void testBadCurrentScriptRevisionOnScript() throws QException
|
void testBadCurrentScriptRevisionOnScript() throws QException
|
||||||
{
|
{
|
||||||
QInstance instance = setupInstance();
|
setupInstance();
|
||||||
|
|
||||||
insertScript(instance, 1, """
|
insertScript(1, """
|
||||||
return "Hello";
|
return "Hello";
|
||||||
""");
|
""");
|
||||||
|
|
||||||
@ -281,7 +359,7 @@ class RunAssociatedScriptActionTest extends BaseTest
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private void insertScript(QInstance instance, Serializable recordId, String code) throws QException
|
private void insertScript(Serializable recordId, String code) throws QException
|
||||||
{
|
{
|
||||||
StoreAssociatedScriptInput storeAssociatedScriptInput = new StoreAssociatedScriptInput();
|
StoreAssociatedScriptInput storeAssociatedScriptInput = new StoreAssociatedScriptInput();
|
||||||
storeAssociatedScriptInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
storeAssociatedScriptInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||||
|
Reference in New Issue
Block a user