mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-21 22:48:44 +00:00
Compare commits
13 Commits
snapshot-f
...
snapshot-f
Author | SHA1 | Date | |
---|---|---|---|
27dbc72db4 | |||
983a93d38c | |||
28ad0661d1 | |||
a8e235c155 | |||
c96bb9dda8 | |||
e4bef88406 | |||
c18aa44010 | |||
47e95d74e3 | |||
cf4c6d2144 | |||
780341b5cc | |||
20d4a9ffeb | |||
4f1310ded9 | |||
fb16a041fb |
@ -40,6 +40,7 @@ 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.QueryInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||||
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.QCodeReferenceLambda;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionInputMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionInputMetaData;
|
||||||
@ -257,11 +258,20 @@ public class RunBackendStepAction
|
|||||||
{
|
{
|
||||||
runBackendStepOutput.seedFromRequest(runBackendStepInput);
|
runBackendStepOutput.seedFromRequest(runBackendStepInput);
|
||||||
|
|
||||||
Class<?> codeClass = Class.forName(code.getName());
|
Object codeObject;
|
||||||
Object codeObject = codeClass.getConstructor().newInstance();
|
if(code instanceof QCodeReferenceLambda<?> qCodeReferenceLambda)
|
||||||
|
{
|
||||||
|
codeObject = qCodeReferenceLambda.getLambda();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Class<?> codeClass = Class.forName(code.getName());
|
||||||
|
codeObject = codeClass.getConstructor().newInstance();
|
||||||
|
}
|
||||||
|
|
||||||
if(!(codeObject instanceof BackendStep backendStepCodeObject))
|
if(!(codeObject instanceof BackendStep backendStepCodeObject))
|
||||||
{
|
{
|
||||||
throw (new QException("The supplied code [" + codeClass.getName() + "] is not an instance of BackendStep"));
|
throw (new QException("The supplied codeReference [" + code + "] is not a reference to a BackendStep"));
|
||||||
}
|
}
|
||||||
|
|
||||||
backendStepCodeObject.run(runBackendStepInput, runBackendStepOutput);
|
backendStepCodeObject.run(runBackendStepInput, runBackendStepOutput);
|
||||||
|
@ -28,6 +28,7 @@ import java.time.temporal.ChronoUnit;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||||
@ -58,6 +59,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaD
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendComponentMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendComponentMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStateMachineStep;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||||
import com.kingsrook.qqq.backend.core.processes.implementations.basepull.BasepullConfiguration;
|
import com.kingsrook.qqq.backend.core.processes.implementations.basepull.BasepullConfiguration;
|
||||||
@ -133,90 +135,11 @@ public class RunProcessAction
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
String lastStepName = runProcessInput.getStartAfterStep();
|
switch(Objects.requireNonNull(process.getStepFlow(), "Process [" + process.getName() + "] has a null stepFlow."))
|
||||||
|
|
||||||
STEP_LOOP:
|
|
||||||
while(true)
|
|
||||||
{
|
{
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
case LINEAR -> runLinearStepLoop(process, processState, stateKey, runProcessInput, runProcessOutput);
|
||||||
// always refresh the step list - as any step that runs can modify it (in the process state). //
|
case STATE_MACHINE -> runStateMachineStep(runProcessInput.getStartAfterStep(), process, processState, stateKey, runProcessInput, runProcessOutput, 0);
|
||||||
// this is why we don't do a loop over the step list - as we'd get ConcurrentModificationExceptions. //
|
default -> throw (new QException("Unhandled process step flow: " + process.getStepFlow()));
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
List<QStepMetaData> stepList = getAvailableStepList(processState, process, lastStepName);
|
|
||||||
if(stepList.isEmpty())
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStepMetaData step = stepList.get(0);
|
|
||||||
lastStepName = step.getName();
|
|
||||||
|
|
||||||
if(step instanceof QFrontendStepMetaData frontendStep)
|
|
||||||
{
|
|
||||||
////////////////////////////////////////////////////////////////
|
|
||||||
// Handle what to do with frontend steps, per request setting //
|
|
||||||
////////////////////////////////////////////////////////////////
|
|
||||||
switch(runProcessInput.getFrontendStepBehavior())
|
|
||||||
{
|
|
||||||
case BREAK ->
|
|
||||||
{
|
|
||||||
LOG.trace("Breaking process [" + process.getName() + "] at frontend step (as requested by caller): " + step.getName());
|
|
||||||
processFrontendStepFieldDefaultValues(processState, frontendStep);
|
|
||||||
processFrontendComponents(processState, frontendStep);
|
|
||||||
processState.setNextStepName(step.getName());
|
|
||||||
break STEP_LOOP;
|
|
||||||
}
|
|
||||||
case SKIP ->
|
|
||||||
{
|
|
||||||
LOG.trace("Skipping frontend step [" + step.getName() + "] in process [" + process.getName() + "] (as requested by caller)");
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// much less error prone in case this code changes in the future... //
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// noinspection UnnecessaryContinue
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
case FAIL ->
|
|
||||||
{
|
|
||||||
LOG.trace("Throwing error for frontend step [" + step.getName() + "] in process [" + process.getName() + "] (as requested by caller)");
|
|
||||||
throw (new QException("Failing process at step " + step.getName() + " (as requested, to fail on frontend steps)"));
|
|
||||||
}
|
|
||||||
default -> throw new IllegalStateException("Unexpected value: " + runProcessInput.getFrontendStepBehavior());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(step instanceof QBackendStepMetaData backendStepMetaData)
|
|
||||||
{
|
|
||||||
///////////////////////
|
|
||||||
// Run backend steps //
|
|
||||||
///////////////////////
|
|
||||||
LOG.debug("Running backend step [" + step.getName() + "] in process [" + process.getName() + "]");
|
|
||||||
RunBackendStepOutput runBackendStepOutput = runBackendStep(runProcessInput, process, runProcessOutput, stateKey, backendStepMetaData, process, processState);
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// if the step returned an override lastStepName, use that to determine how we proceed //
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
if(runBackendStepOutput.getOverrideLastStepName() != null)
|
|
||||||
{
|
|
||||||
LOG.debug("Process step [" + lastStepName + "] returned an overrideLastStepName [" + runBackendStepOutput.getOverrideLastStepName() + "]!");
|
|
||||||
lastStepName = runBackendStepOutput.getOverrideLastStepName();
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// similarly, if the step produced an updatedFrontendStepList, propagate that data outward //
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
if(runBackendStepOutput.getUpdatedFrontendStepList() != null)
|
|
||||||
{
|
|
||||||
LOG.debug("Process step [" + lastStepName + "] generated an updatedFrontendStepList [" + runBackendStepOutput.getUpdatedFrontendStepList().stream().map(s -> s.getName()).toList() + "]!");
|
|
||||||
runProcessOutput.setUpdatedFrontendStepList(runBackendStepOutput.getUpdatedFrontendStepList());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//////////////////////////////////////////////////
|
|
||||||
// in case we have a different step type, throw //
|
|
||||||
//////////////////////////////////////////////////
|
|
||||||
throw (new QException("Unsure how to run a step of type: " + step.getClass().getName()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
@ -258,6 +181,270 @@ public class RunProcessAction
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
private void runLinearStepLoop(QProcessMetaData process, ProcessState processState, UUIDAndTypeStateKey stateKey, RunProcessInput runProcessInput, RunProcessOutput runProcessOutput) throws Exception
|
||||||
|
{
|
||||||
|
String lastStepName = runProcessInput.getStartAfterStep();
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// always refresh the step list - as any step that runs can modify it (in the process state). //
|
||||||
|
// this is why we don't do a loop over the step list - as we'd get ConcurrentModificationExceptions. //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
List<QStepMetaData> stepList = getAvailableStepList(processState, process, lastStepName);
|
||||||
|
if(stepList.isEmpty())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStepMetaData step = stepList.get(0);
|
||||||
|
lastStepName = step.getName();
|
||||||
|
|
||||||
|
if(step instanceof QFrontendStepMetaData frontendStep)
|
||||||
|
{
|
||||||
|
LoopTodo loopTodo = prepareForFrontendStep(runProcessInput, process, frontendStep, processState);
|
||||||
|
if(loopTodo == LoopTodo.BREAK)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(step instanceof QBackendStepMetaData backendStepMetaData)
|
||||||
|
{
|
||||||
|
RunBackendStepOutput runBackendStepOutput = runBackendStep(process, processState, stateKey, runProcessInput, runProcessOutput, backendStepMetaData, step);
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// if the step returned an override lastStepName, use that to determine how we proceed //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
if(runBackendStepOutput.getOverrideLastStepName() != null)
|
||||||
|
{
|
||||||
|
LOG.debug("Process step [" + lastStepName + "] returned an overrideLastStepName [" + runBackendStepOutput.getOverrideLastStepName() + "]!");
|
||||||
|
lastStepName = runBackendStepOutput.getOverrideLastStepName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//////////////////////////////////////////////////
|
||||||
|
// in case we have a different step type, throw //
|
||||||
|
//////////////////////////////////////////////////
|
||||||
|
throw (new QException("Unsure how to run a step of type: " + step.getClass().getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
private enum LoopTodo
|
||||||
|
{
|
||||||
|
BREAK,
|
||||||
|
CONTINUE
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
private LoopTodo prepareForFrontendStep(RunProcessInput runProcessInput, QProcessMetaData process, QFrontendStepMetaData step, ProcessState processState) throws QException
|
||||||
|
{
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
// Handle what to do with frontend steps, per request setting //
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
switch(runProcessInput.getFrontendStepBehavior())
|
||||||
|
{
|
||||||
|
case BREAK ->
|
||||||
|
{
|
||||||
|
LOG.trace("Breaking process [" + process.getName() + "] at frontend step (as requested by caller): " + step.getName());
|
||||||
|
processFrontendStepFieldDefaultValues(processState, step);
|
||||||
|
processFrontendComponents(processState, step);
|
||||||
|
processState.setNextStepName(step.getName());
|
||||||
|
return LoopTodo.BREAK;
|
||||||
|
}
|
||||||
|
case SKIP ->
|
||||||
|
{
|
||||||
|
LOG.trace("Skipping frontend step [" + step.getName() + "] in process [" + process.getName() + "] (as requested by caller)");
|
||||||
|
return LoopTodo.CONTINUE;
|
||||||
|
}
|
||||||
|
case FAIL ->
|
||||||
|
{
|
||||||
|
LOG.trace("Throwing error for frontend step [" + step.getName() + "] in process [" + process.getName() + "] (as requested by caller)");
|
||||||
|
throw (new QException("Failing process at step " + step.getName() + " (as requested, to fail on frontend steps)"));
|
||||||
|
}
|
||||||
|
default -> throw new IllegalStateException("Unexpected value: " + runProcessInput.getFrontendStepBehavior());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
private void runStateMachineStep(String lastStepName, QProcessMetaData process, ProcessState processState, UUIDAndTypeStateKey stateKey, RunProcessInput runProcessInput, RunProcessOutput runProcessOutput, int stackDepth) throws Exception
|
||||||
|
{
|
||||||
|
//////////////////////////////
|
||||||
|
// check for stack-overflow //
|
||||||
|
//////////////////////////////
|
||||||
|
Integer maxStateMachineProcessStepFlowStackDepth = Objects.requireNonNullElse(runProcessInput.getValueInteger("maxStateMachineProcessStepFlowStackDepth"), 20);
|
||||||
|
if(stackDepth > maxStateMachineProcessStepFlowStackDepth)
|
||||||
|
{
|
||||||
|
throw (new QException("StateMachine process recurred too many times (exceeded maxStateMachineProcessStepFlowStackDepth of " + maxStateMachineProcessStepFlowStackDepth + ")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////
|
||||||
|
// figure out what step to run: //
|
||||||
|
//////////////////////////////////
|
||||||
|
QStepMetaData step = null;
|
||||||
|
if(!StringUtils.hasContent(lastStepName))
|
||||||
|
{
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// if no lastStepName is given, start at the process's first step //
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
if(CollectionUtils.nullSafeIsEmpty(process.getStepList()))
|
||||||
|
{
|
||||||
|
throw (new QException("Process [" + process.getName() + "] does not have a step list defined."));
|
||||||
|
}
|
||||||
|
step = process.getStepList().get(0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/////////////////////////////////////
|
||||||
|
// else run the given lastStepName //
|
||||||
|
/////////////////////////////////////
|
||||||
|
processState.clearNextStepName();
|
||||||
|
step = process.getStep(lastStepName);
|
||||||
|
if(step == null)
|
||||||
|
{
|
||||||
|
throw (new QException("Could not find step by name [" + lastStepName + "]"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
// for the flow of: //
|
||||||
|
// we were on a frontend step (as a sub-step of a state machine step), //
|
||||||
|
// and now we're here to run that state-step's backend step - //
|
||||||
|
// find the state-machine step containing this frontend step. //
|
||||||
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
String skipSubStepsUntil = null;
|
||||||
|
if(step instanceof QFrontendStepMetaData frontendStepMetaData)
|
||||||
|
{
|
||||||
|
QStateMachineStep stateMachineStep = getStateMachineStepContainingSubStep(process, frontendStepMetaData.getName());
|
||||||
|
if(stateMachineStep == null)
|
||||||
|
{
|
||||||
|
throw (new QException("Could not find stateMachineStep that contains last-frontend step: " + frontendStepMetaData.getName()));
|
||||||
|
}
|
||||||
|
step = stateMachineStep;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// set this flag, to know to skip this frontend step in the sub-step loop below //
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////
|
||||||
|
skipSubStepsUntil = frontendStepMetaData.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!(step instanceof QStateMachineStep stateMachineStep))
|
||||||
|
{
|
||||||
|
throw (new QException("Have a non-stateMachineStep in a process using stateMachine flow... " + step.getClass().getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////
|
||||||
|
// run the sub-steps //
|
||||||
|
///////////////////////
|
||||||
|
boolean ranAnySubSteps = false;
|
||||||
|
for(QStepMetaData subStep : stateMachineStep.getSubSteps())
|
||||||
|
{
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// ok, well, skip them if this flag is set (and clear the flag once we've hit this sub-step) //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
if(skipSubStepsUntil != null)
|
||||||
|
{
|
||||||
|
if(skipSubStepsUntil.equals(subStep.getName()))
|
||||||
|
{
|
||||||
|
skipSubStepsUntil = null;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ranAnySubSteps = true;
|
||||||
|
if(subStep instanceof QFrontendStepMetaData frontendStep)
|
||||||
|
{
|
||||||
|
LoopTodo loopTodo = prepareForFrontendStep(runProcessInput, process, frontendStep, processState);
|
||||||
|
if(loopTodo == LoopTodo.BREAK)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(subStep instanceof QBackendStepMetaData backendStepMetaData)
|
||||||
|
{
|
||||||
|
RunBackendStepOutput runBackendStepOutput = runBackendStep(process, processState, stateKey, runProcessInput, runProcessOutput, backendStepMetaData, step);
|
||||||
|
Optional<String> nextStepName = runBackendStepOutput.getProcessState().getNextStepName();
|
||||||
|
|
||||||
|
if(nextStepName.isEmpty() && StringUtils.hasContent(stateMachineStep.getDefaultNextStepName()))
|
||||||
|
{
|
||||||
|
nextStepName = Optional.of(stateMachineStep.getDefaultNextStepName());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(nextStepName.isPresent())
|
||||||
|
{
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// if we've been given a next-step-name, go to that step now. //
|
||||||
|
// it might be a backend-only stateMachineStep, in which case, we should run that backend step now. //
|
||||||
|
// or it might be a frontend-then-backend step, in which case, we want to go to that frontend step. //
|
||||||
|
// if we weren't given a next-step-name, then we should stay in the same state - either to finish //
|
||||||
|
// its sub-steps, or, to fall out of the loop and end the process. //
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
processState.clearNextStepName();
|
||||||
|
runStateMachineStep(nextStepName.get(), process, processState, stateKey, runProcessInput, runProcessOutput, stackDepth + 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//////////////////////////////////////////////////
|
||||||
|
// in case we have a different step type, throw //
|
||||||
|
//////////////////////////////////////////////////
|
||||||
|
throw (new QException("Unsure how to run a step of type: " + step.getClass().getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!ranAnySubSteps)
|
||||||
|
{
|
||||||
|
if(StringUtils.hasContent(stateMachineStep.getDefaultNextStepName()))
|
||||||
|
{
|
||||||
|
runStateMachineStep(stateMachineStep.getDefaultNextStepName(), process, processState, stateKey, runProcessInput, runProcessOutput, stackDepth + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QStateMachineStep getStateMachineStepContainingSubStep(QProcessMetaData process, String stepName)
|
||||||
|
{
|
||||||
|
for(QStepMetaData step : process.getAllSteps().values())
|
||||||
|
{
|
||||||
|
if(step instanceof QStateMachineStep stateMachineStep)
|
||||||
|
{
|
||||||
|
for(QStepMetaData subStep : stateMachineStep.getSubSteps())
|
||||||
|
{
|
||||||
|
if(subStep.getName().equals(stepName))
|
||||||
|
{
|
||||||
|
return (stateMachineStep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -335,12 +522,12 @@ public class RunProcessAction
|
|||||||
///////////////////////////////////////////////////
|
///////////////////////////////////////////////////
|
||||||
runProcessInput.seedFromProcessState(optionalProcessState.get());
|
runProcessInput.seedFromProcessState(optionalProcessState.get());
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// if we're restoring an old state, we can discard a previously stored updatedFrontendStepList - //
|
// if we're restoring an old state, we can discard a previously stored processMetaDataAdjustment - //
|
||||||
// it is only needed on the transitional edge from a backend-step to a frontend step, but not //
|
// it is only needed on the transitional edge from a backend-step to a frontend step, but not //
|
||||||
// in the other directly //
|
// in the other directly //
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
optionalProcessState.get().setUpdatedFrontendStepList(null);
|
optionalProcessState.get().setProcessMetaDataAdjustment(null);
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// if there were values from the caller, put those (back) in the request //
|
// if there were values from the caller, put those (back) in the request //
|
||||||
@ -355,16 +542,40 @@ public class RunProcessAction
|
|||||||
}
|
}
|
||||||
|
|
||||||
ProcessState processState = optionalProcessState.get();
|
ProcessState processState = optionalProcessState.get();
|
||||||
processState.clearNextStepName();
|
|
||||||
return processState;
|
return processState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
private RunBackendStepOutput runBackendStep(QProcessMetaData process, ProcessState processState, UUIDAndTypeStateKey stateKey, RunProcessInput runProcessInput, RunProcessOutput runProcessOutput, QBackendStepMetaData backendStepMetaData, QStepMetaData step) throws Exception
|
||||||
|
{
|
||||||
|
///////////////////////
|
||||||
|
// Run backend steps //
|
||||||
|
///////////////////////
|
||||||
|
LOG.debug("Running backend step [" + step.getName() + "] in process [" + process.getName() + "]");
|
||||||
|
RunBackendStepOutput runBackendStepOutput = runBackendStep(runProcessInput, process, runProcessOutput, stateKey, backendStepMetaData, process, processState);
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// similarly, if the step produced a processMetaDataAdjustment, propagate that data outward //
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
if(runBackendStepOutput.getProcessMetaDataAdjustment() != null)
|
||||||
|
{
|
||||||
|
LOG.debug("Process step [" + step.getName() + "] generated a ProcessMetaDataAdjustment [" + runBackendStepOutput.getProcessMetaDataAdjustment() + "]!");
|
||||||
|
runProcessOutput.setProcessMetaDataAdjustment(runBackendStepOutput.getProcessMetaDataAdjustment());
|
||||||
|
}
|
||||||
|
|
||||||
|
return runBackendStepOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Run a single backend step.
|
** Run a single backend step.
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
protected RunBackendStepOutput runBackendStep(RunProcessInput runProcessInput, QProcessMetaData process, RunProcessOutput runProcessOutput, UUIDAndTypeStateKey stateKey, QBackendStepMetaData backendStep, QProcessMetaData qProcessMetaData, ProcessState processState) throws Exception
|
RunBackendStepOutput runBackendStep(RunProcessInput runProcessInput, QProcessMetaData process, RunProcessOutput runProcessOutput, UUIDAndTypeStateKey stateKey, QBackendStepMetaData backendStep, QProcessMetaData qProcessMetaData, ProcessState processState) throws Exception
|
||||||
{
|
{
|
||||||
RunBackendStepInput runBackendStepInput = new RunBackendStepInput(processState);
|
RunBackendStepInput runBackendStepInput = new RunBackendStepInput(processState);
|
||||||
runBackendStepInput.setProcessName(process.getName());
|
runBackendStepInput.setProcessName(process.getName());
|
||||||
|
@ -58,6 +58,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.processes.QComponentType;
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendComponentMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendComponentMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStateMachineStep;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QSupplementalProcessMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QSupplementalProcessMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportDataSource;
|
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportDataSource;
|
||||||
@ -410,10 +411,27 @@ public class QInstanceEnricher
|
|||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private void enrichStep(QStepMetaData step)
|
private void enrichStep(QStepMetaData step)
|
||||||
|
{
|
||||||
|
enrichStep(step, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
private void enrichStep(QStepMetaData step, boolean isSubStep)
|
||||||
{
|
{
|
||||||
if(!StringUtils.hasContent(step.getLabel()))
|
if(!StringUtils.hasContent(step.getLabel()))
|
||||||
{
|
{
|
||||||
step.setLabel(nameToLabel(step.getName()));
|
if(isSubStep && (step.getName().endsWith(".backend") || step.getName().endsWith(".frontend")))
|
||||||
|
{
|
||||||
|
step.setLabel(nameToLabel(step.getName().replaceFirst("\\.(backend|frontend)", "")));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
step.setLabel(nameToLabel(step.getName()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
step.getInputFields().forEach(this::enrichField);
|
step.getInputFields().forEach(this::enrichField);
|
||||||
@ -434,6 +452,13 @@ public class QInstanceEnricher
|
|||||||
frontendStepMetaData.getRecordListFields().forEach(this::enrichField);
|
frontendStepMetaData.getRecordListFields().forEach(this::enrichField);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if(step instanceof QStateMachineStep stateMachineStep)
|
||||||
|
{
|
||||||
|
for(QStepMetaData subStep : CollectionUtils.nonNullList(stateMachineStep.getSubSteps()))
|
||||||
|
{
|
||||||
|
enrichStep(subStep, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -74,6 +74,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData;
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppSection;
|
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppSection;
|
||||||
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.possiblevalues.QPossibleValueSourceType;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
|
||||||
@ -928,13 +929,8 @@ public class QInstanceValidator
|
|||||||
assertCondition(Objects.equals(fieldName, field.getName()),
|
assertCondition(Objects.equals(fieldName, field.getName()),
|
||||||
"Inconsistent naming in table " + tableName + " for field " + fieldName + "/" + field.getName() + ".");
|
"Inconsistent naming in table " + tableName + " for field " + fieldName + "/" + field.getName() + ".");
|
||||||
|
|
||||||
if(field.getPossibleValueSourceName() != null)
|
|
||||||
{
|
|
||||||
assertCondition(qInstance.getPossibleValueSource(field.getPossibleValueSourceName()) != null,
|
|
||||||
"Unrecognized possibleValueSourceName " + field.getPossibleValueSourceName() + " in table " + tableName + " for field " + fieldName + ".");
|
|
||||||
}
|
|
||||||
|
|
||||||
String prefix = "Field " + fieldName + " in table " + tableName + " ";
|
String prefix = "Field " + fieldName + " in table " + tableName + " ";
|
||||||
|
validateFieldPossibleValueSourceAttributes(qInstance, field, prefix);
|
||||||
|
|
||||||
///////////////////////////////////////////////////
|
///////////////////////////////////////////////////
|
||||||
// validate things we know about field behaviors //
|
// validate things we know about field behaviors //
|
||||||
@ -1039,6 +1035,31 @@ public class QInstanceValidator
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
private void validateFieldPossibleValueSourceAttributes(QInstance qInstance, QFieldMetaData field, String prefix)
|
||||||
|
{
|
||||||
|
if(field.getPossibleValueSourceName() != null)
|
||||||
|
{
|
||||||
|
assertCondition(qInstance.getPossibleValueSource(field.getPossibleValueSourceName()) != null,
|
||||||
|
prefix + "has an unrecognized possibleValueSourceName " + field.getPossibleValueSourceName());
|
||||||
|
|
||||||
|
assertCondition(field.getInlinePossibleValueSource() == null, prefix.trim() + " has both a possibleValueSourceName and an inlinePossibleValueSource, which is not allowed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(field.getInlinePossibleValueSource() != null)
|
||||||
|
{
|
||||||
|
String name = "inlinePossibleValueSource for " + prefix.trim();
|
||||||
|
if(assertCondition(QPossibleValueSourceType.ENUM.equals(field.getInlinePossibleValueSource().getType()), name + " must have a type of ENUM."))
|
||||||
|
{
|
||||||
|
validatePossibleValueSource(qInstance, name, field.getInlinePossibleValueSource());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -1546,6 +1567,16 @@ public class QInstanceValidator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for(QFieldMetaData field : process.getInputFields())
|
||||||
|
{
|
||||||
|
validateFieldPossibleValueSourceAttributes(qInstance, field, "Process " + processName + ", input field " + field.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
for(QFieldMetaData field : process.getOutputFields())
|
||||||
|
{
|
||||||
|
validateFieldPossibleValueSourceAttributes(qInstance, field, "Process " + processName + ", output field " + field.getName());
|
||||||
|
}
|
||||||
|
|
||||||
if(process.getCancelStep() != null)
|
if(process.getCancelStep() != null)
|
||||||
{
|
{
|
||||||
if(assertCondition(process.getCancelStep().getCode() != null, "Cancel step is missing a code reference, in process " + processName))
|
if(assertCondition(process.getCancelStep().getCode() != null, "Cancel step is missing a code reference, in process " + processName))
|
||||||
@ -1948,78 +1979,88 @@ public class QInstanceValidator
|
|||||||
qInstance.getPossibleValueSources().forEach((pvsName, possibleValueSource) ->
|
qInstance.getPossibleValueSources().forEach((pvsName, possibleValueSource) ->
|
||||||
{
|
{
|
||||||
assertCondition(Objects.equals(pvsName, possibleValueSource.getName()), "Inconsistent naming for possibleValueSource: " + pvsName + "/" + possibleValueSource.getName() + ".");
|
assertCondition(Objects.equals(pvsName, possibleValueSource.getName()), "Inconsistent naming for possibleValueSource: " + pvsName + "/" + possibleValueSource.getName() + ".");
|
||||||
if(assertCondition(possibleValueSource.getType() != null, "Missing type for possibleValueSource: " + pvsName))
|
validatePossibleValueSource(qInstance, pvsName, possibleValueSource);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
private void validatePossibleValueSource(QInstance qInstance, String name, QPossibleValueSource possibleValueSource)
|
||||||
|
{
|
||||||
|
if(assertCondition(possibleValueSource.getType() != null, "Missing type for possibleValueSource: " + name))
|
||||||
|
{
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// assert about fields that should and should not be set, based on possible value source type //
|
||||||
|
// do additional type-specific validations as well //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
switch(possibleValueSource.getType())
|
||||||
|
{
|
||||||
|
case ENUM ->
|
||||||
{
|
{
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
assertCondition(!StringUtils.hasContent(possibleValueSource.getTableName()), "enum-type possibleValueSource " + name + " should not have a tableName.");
|
||||||
// assert about fields that should and should not be set, based on possible value source type //
|
assertCondition(!CollectionUtils.nullSafeHasContents(possibleValueSource.getSearchFields()), "enum-type possibleValueSource " + name + " should not have searchFields.");
|
||||||
// do additional type-specific validations as well //
|
assertCondition(!CollectionUtils.nullSafeHasContents(possibleValueSource.getOrderByFields()), "enum-type possibleValueSource " + name + " should not have orderByFields.");
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
assertCondition(possibleValueSource.getCustomCodeReference() == null, "enum-type possibleValueSource " + name + " should not have a customCodeReference.");
|
||||||
switch(possibleValueSource.getType())
|
|
||||||
|
assertCondition(CollectionUtils.nullSafeHasContents(possibleValueSource.getEnumValues()), "enum-type possibleValueSource " + name + " is missing enum values");
|
||||||
|
}
|
||||||
|
case TABLE ->
|
||||||
|
{
|
||||||
|
assertCondition(CollectionUtils.nullSafeIsEmpty(possibleValueSource.getEnumValues()), "table-type possibleValueSource " + name + " should not have enum values.");
|
||||||
|
assertCondition(possibleValueSource.getCustomCodeReference() == null, "table-type possibleValueSource " + name + " should not have a customCodeReference.");
|
||||||
|
|
||||||
|
QTableMetaData tableMetaData = null;
|
||||||
|
if(assertCondition(StringUtils.hasContent(possibleValueSource.getTableName()), "table-type possibleValueSource " + name + " is missing a tableName."))
|
||||||
{
|
{
|
||||||
case ENUM ->
|
tableMetaData = qInstance.getTable(possibleValueSource.getTableName());
|
||||||
{
|
assertCondition(tableMetaData != null, "Unrecognized table " + possibleValueSource.getTableName() + " for possibleValueSource " + name + ".");
|
||||||
assertCondition(!StringUtils.hasContent(possibleValueSource.getTableName()), "enum-type possibleValueSource " + pvsName + " should not have a tableName.");
|
|
||||||
assertCondition(!CollectionUtils.nullSafeHasContents(possibleValueSource.getSearchFields()), "enum-type possibleValueSource " + pvsName + " should not have searchFields.");
|
|
||||||
assertCondition(!CollectionUtils.nullSafeHasContents(possibleValueSource.getOrderByFields()), "enum-type possibleValueSource " + pvsName + " should not have orderByFields.");
|
|
||||||
assertCondition(possibleValueSource.getCustomCodeReference() == null, "enum-type possibleValueSource " + pvsName + " should not have a customCodeReference.");
|
|
||||||
|
|
||||||
assertCondition(CollectionUtils.nullSafeHasContents(possibleValueSource.getEnumValues()), "enum-type possibleValueSource " + pvsName + " is missing enum values");
|
|
||||||
}
|
|
||||||
case TABLE ->
|
|
||||||
{
|
|
||||||
assertCondition(CollectionUtils.nullSafeIsEmpty(possibleValueSource.getEnumValues()), "table-type possibleValueSource " + pvsName + " should not have enum values.");
|
|
||||||
assertCondition(possibleValueSource.getCustomCodeReference() == null, "table-type possibleValueSource " + pvsName + " should not have a customCodeReference.");
|
|
||||||
|
|
||||||
QTableMetaData tableMetaData = null;
|
|
||||||
if(assertCondition(StringUtils.hasContent(possibleValueSource.getTableName()), "table-type possibleValueSource " + pvsName + " is missing a tableName."))
|
|
||||||
{
|
|
||||||
tableMetaData = qInstance.getTable(possibleValueSource.getTableName());
|
|
||||||
assertCondition(tableMetaData != null, "Unrecognized table " + possibleValueSource.getTableName() + " for possibleValueSource " + pvsName + ".");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(assertCondition(CollectionUtils.nullSafeHasContents(possibleValueSource.getSearchFields()), "table-type possibleValueSource " + pvsName + " is missing searchFields."))
|
|
||||||
{
|
|
||||||
if(tableMetaData != null)
|
|
||||||
{
|
|
||||||
QTableMetaData finalTableMetaData = tableMetaData;
|
|
||||||
for(String searchField : possibleValueSource.getSearchFields())
|
|
||||||
{
|
|
||||||
assertNoException(() -> finalTableMetaData.getField(searchField), "possibleValueSource " + pvsName + " has an unrecognized searchField: " + searchField);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(assertCondition(CollectionUtils.nullSafeHasContents(possibleValueSource.getOrderByFields()), "table-type possibleValueSource " + pvsName + " is missing orderByFields."))
|
|
||||||
{
|
|
||||||
if(tableMetaData != null)
|
|
||||||
{
|
|
||||||
QTableMetaData finalTableMetaData = tableMetaData;
|
|
||||||
|
|
||||||
for(QFilterOrderBy orderByField : possibleValueSource.getOrderByFields())
|
|
||||||
{
|
|
||||||
assertNoException(() -> finalTableMetaData.getField(orderByField.getFieldName()), "possibleValueSource " + pvsName + " has an unrecognized orderByField: " + orderByField.getFieldName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case CUSTOM ->
|
|
||||||
{
|
|
||||||
assertCondition(CollectionUtils.nullSafeIsEmpty(possibleValueSource.getEnumValues()), "custom-type possibleValueSource " + pvsName + " should not have enum values.");
|
|
||||||
assertCondition(!StringUtils.hasContent(possibleValueSource.getTableName()), "custom-type possibleValueSource " + pvsName + " should not have a tableName.");
|
|
||||||
assertCondition(!CollectionUtils.nullSafeHasContents(possibleValueSource.getSearchFields()), "custom-type possibleValueSource " + pvsName + " should not have searchFields.");
|
|
||||||
assertCondition(!CollectionUtils.nullSafeHasContents(possibleValueSource.getOrderByFields()), "custom-type possibleValueSource " + pvsName + " should not have orderByFields.");
|
|
||||||
|
|
||||||
if(assertCondition(possibleValueSource.getCustomCodeReference() != null, "custom-type possibleValueSource " + pvsName + " is missing a customCodeReference."))
|
|
||||||
{
|
|
||||||
validateSimpleCodeReference("PossibleValueSource " + pvsName + " custom code reference: ", possibleValueSource.getCustomCodeReference(), QCustomPossibleValueProvider.class);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default -> errors.add("Unexpected possibleValueSource type: " + possibleValueSource.getType());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
runPlugins(QPossibleValueSource.class, possibleValueSource, qInstance);
|
if(assertCondition(CollectionUtils.nullSafeHasContents(possibleValueSource.getSearchFields()), "table-type possibleValueSource " + name + " is missing searchFields."))
|
||||||
|
{
|
||||||
|
if(tableMetaData != null)
|
||||||
|
{
|
||||||
|
QTableMetaData finalTableMetaData = tableMetaData;
|
||||||
|
for(String searchField : possibleValueSource.getSearchFields())
|
||||||
|
{
|
||||||
|
assertNoException(() -> finalTableMetaData.getField(searchField), "possibleValueSource " + name + " has an unrecognized searchField: " + searchField);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(assertCondition(CollectionUtils.nullSafeHasContents(possibleValueSource.getOrderByFields()), "table-type possibleValueSource " + name + " is missing orderByFields."))
|
||||||
|
{
|
||||||
|
if(tableMetaData != null)
|
||||||
|
{
|
||||||
|
QTableMetaData finalTableMetaData = tableMetaData;
|
||||||
|
|
||||||
|
for(QFilterOrderBy orderByField : possibleValueSource.getOrderByFields())
|
||||||
|
{
|
||||||
|
assertNoException(() -> finalTableMetaData.getField(orderByField.getFieldName()), "possibleValueSource " + name + " has an unrecognized orderByField: " + orderByField.getFieldName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
case CUSTOM ->
|
||||||
|
{
|
||||||
|
assertCondition(CollectionUtils.nullSafeIsEmpty(possibleValueSource.getEnumValues()), "custom-type possibleValueSource " + name + " should not have enum values.");
|
||||||
|
assertCondition(!StringUtils.hasContent(possibleValueSource.getTableName()), "custom-type possibleValueSource " + name + " should not have a tableName.");
|
||||||
|
assertCondition(!CollectionUtils.nullSafeHasContents(possibleValueSource.getSearchFields()), "custom-type possibleValueSource " + name + " should not have searchFields.");
|
||||||
|
assertCondition(!CollectionUtils.nullSafeHasContents(possibleValueSource.getOrderByFields()), "custom-type possibleValueSource " + name + " should not have orderByFields.");
|
||||||
|
|
||||||
|
if(assertCondition(possibleValueSource.getCustomCodeReference() != null, "custom-type possibleValueSource " + name + " is missing a customCodeReference."))
|
||||||
|
{
|
||||||
|
validateSimpleCodeReference("PossibleValueSource " + name + " custom code reference: ", possibleValueSource.getCustomCodeReference(), QCustomPossibleValueProvider.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default -> errors.add("Unexpected possibleValueSource type: " + possibleValueSource.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
runPlugins(QPossibleValueSource.class, possibleValueSource, qInstance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2024. 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.actions.processes;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
|
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Object that stores adjustments that a process wants to make, at run-time,
|
||||||
|
** to its meta-data.
|
||||||
|
**
|
||||||
|
** e.g., changing the steps; updating fields (e.g., changing an inline PVS,
|
||||||
|
** or an isRequired attribute)
|
||||||
|
*******************************************************************************/
|
||||||
|
public class ProcessMetaDataAdjustment
|
||||||
|
{
|
||||||
|
private static final QLogger LOG = QLogger.getLogger(ProcessMetaDataAdjustment.class);
|
||||||
|
|
||||||
|
private List<QFrontendStepMetaData> updatedFrontendStepList = null;
|
||||||
|
private Map<String, QFieldMetaData> updatedFields = null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public ProcessMetaDataAdjustment withUpdatedField(QFieldMetaData field)
|
||||||
|
{
|
||||||
|
if(updatedFields == null)
|
||||||
|
{
|
||||||
|
updatedFields = new LinkedHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!StringUtils.hasContent(field.getName()))
|
||||||
|
{
|
||||||
|
LOG.warn("Missing name on field in withUpdatedField - no update will happen.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(updatedFields.containsKey(field.getName()))
|
||||||
|
{
|
||||||
|
LOG.info("UpdatedFields map already contained a field with this name - overwriting it.", logPair("fieldName", field.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedFields.put(field.getName(), field);
|
||||||
|
}
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for updatedFrontendStepList
|
||||||
|
*******************************************************************************/
|
||||||
|
public List<QFrontendStepMetaData> getUpdatedFrontendStepList()
|
||||||
|
{
|
||||||
|
return (this.updatedFrontendStepList);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for updatedFrontendStepList
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setUpdatedFrontendStepList(List<QFrontendStepMetaData> updatedFrontendStepList)
|
||||||
|
{
|
||||||
|
this.updatedFrontendStepList = updatedFrontendStepList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for updatedFrontendStepList
|
||||||
|
*******************************************************************************/
|
||||||
|
public ProcessMetaDataAdjustment withUpdatedFrontendStepList(List<QFrontendStepMetaData> updatedFrontendStepList)
|
||||||
|
{
|
||||||
|
this.updatedFrontendStepList = updatedFrontendStepList;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for updatedFields
|
||||||
|
*******************************************************************************/
|
||||||
|
public Map<String, QFieldMetaData> getUpdatedFields()
|
||||||
|
{
|
||||||
|
return (this.updatedFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for updatedFields
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setUpdatedFields(Map<String, QFieldMetaData> updatedFields)
|
||||||
|
{
|
||||||
|
this.updatedFields = updatedFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for updatedFields
|
||||||
|
*******************************************************************************/
|
||||||
|
public ProcessMetaDataAdjustment withUpdatedFields(Map<String, QFieldMetaData> updatedFields)
|
||||||
|
{
|
||||||
|
this.updatedFields = updatedFields;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -29,7 +29,6 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -42,10 +41,7 @@ public class ProcessState implements Serializable
|
|||||||
private List<String> stepList = new ArrayList<>();
|
private List<String> stepList = new ArrayList<>();
|
||||||
private Optional<String> nextStepName = Optional.empty();
|
private Optional<String> nextStepName = Optional.empty();
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
private ProcessMetaDataAdjustment processMetaDataAdjustment = null;
|
||||||
// maybe, remove this altogether - just let the frontend compute & send if needed... but how does it know last version...? //
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
private List<QFrontendStepMetaData> updatedFrontendStepList = null;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -148,33 +144,36 @@ public class ProcessState implements Serializable
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for updatedFrontendStepList
|
** Getter for processMetaDataAdjustment
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public List<QFrontendStepMetaData> getUpdatedFrontendStepList()
|
public ProcessMetaDataAdjustment getProcessMetaDataAdjustment()
|
||||||
{
|
{
|
||||||
return (this.updatedFrontendStepList);
|
return (this.processMetaDataAdjustment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Setter for updatedFrontendStepList
|
** Setter for processMetaDataAdjustment
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public void setUpdatedFrontendStepList(List<QFrontendStepMetaData> updatedFrontendStepList)
|
public void setProcessMetaDataAdjustment(ProcessMetaDataAdjustment processMetaDataAdjustment)
|
||||||
{
|
{
|
||||||
this.updatedFrontendStepList = updatedFrontendStepList;
|
this.processMetaDataAdjustment = processMetaDataAdjustment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Fluent setter for updatedFrontendStepList
|
** Fluent setter for processMetaDataAdjustment
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public ProcessState withUpdatedFrontendStepList(List<QFrontendStepMetaData> updatedFrontendStepList)
|
public ProcessState withProcessMetaDataAdjustment(ProcessMetaDataAdjustment processMetaDataAdjustment)
|
||||||
{
|
{
|
||||||
this.updatedFrontendStepList = updatedFrontendStepList;
|
this.processMetaDataAdjustment = processMetaDataAdjustment;
|
||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -374,7 +374,13 @@ public class RunBackendStepOutput extends AbstractActionOutput implements Serial
|
|||||||
.map(step -> (QFrontendStepMetaData) step)
|
.map(step -> (QFrontendStepMetaData) step)
|
||||||
.toList());
|
.toList());
|
||||||
|
|
||||||
setUpdatedFrontendStepList(updatedFrontendStepList);
|
ProcessMetaDataAdjustment processMetaDataAdjustment = getProcessMetaDataAdjustment();
|
||||||
|
if(processMetaDataAdjustment == null)
|
||||||
|
{
|
||||||
|
processMetaDataAdjustment = new ProcessMetaDataAdjustment();
|
||||||
|
}
|
||||||
|
processMetaDataAdjustment.setUpdatedFrontendStepList(updatedFrontendStepList);
|
||||||
|
setProcessMetaDataAdjustment(processMetaDataAdjustment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -411,21 +417,21 @@ public class RunBackendStepOutput extends AbstractActionOutput implements Serial
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for updatedFrontendStepList
|
** Getter for ProcessMetaDataAdjustment (pass-through to processState)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public List<QFrontendStepMetaData> getUpdatedFrontendStepList()
|
public ProcessMetaDataAdjustment getProcessMetaDataAdjustment()
|
||||||
{
|
{
|
||||||
return (this.processState.getUpdatedFrontendStepList());
|
return (this.processState.getProcessMetaDataAdjustment());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Setter for updatedFrontendStepList
|
** Setter for updatedFrontendStepList (pass-through to processState)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public void setUpdatedFrontendStepList(List<QFrontendStepMetaData> updatedFrontendStepList)
|
public void setProcessMetaDataAdjustment(ProcessMetaDataAdjustment processMetaDataAdjustment)
|
||||||
{
|
{
|
||||||
this.processState.setUpdatedFrontendStepList(updatedFrontendStepList);
|
this.processState.setProcessMetaDataAdjustment(processMetaDataAdjustment);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ import java.util.Optional;
|
|||||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.ObjectUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||||
|
|
||||||
|
|
||||||
@ -336,7 +337,12 @@ public class RunProcessOutput extends AbstractActionOutput implements Serializab
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public void setUpdatedFrontendStepList(List<QFrontendStepMetaData> updatedFrontendStepList)
|
public void setUpdatedFrontendStepList(List<QFrontendStepMetaData> updatedFrontendStepList)
|
||||||
{
|
{
|
||||||
this.processState.setUpdatedFrontendStepList(updatedFrontendStepList);
|
if(this.processState.getProcessMetaDataAdjustment() == null)
|
||||||
|
{
|
||||||
|
this.processState.setProcessMetaDataAdjustment(new ProcessMetaDataAdjustment());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.processState.getProcessMetaDataAdjustment().setUpdatedFrontendStepList(updatedFrontendStepList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -346,7 +352,27 @@ public class RunProcessOutput extends AbstractActionOutput implements Serializab
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public List<QFrontendStepMetaData> getUpdatedFrontendStepList()
|
public List<QFrontendStepMetaData> getUpdatedFrontendStepList()
|
||||||
{
|
{
|
||||||
return this.processState.getUpdatedFrontendStepList();
|
return ObjectUtils.tryElse(() -> this.processState.getProcessMetaDataAdjustment().getUpdatedFrontendStepList(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for processMetaDataAdjustment
|
||||||
|
*******************************************************************************/
|
||||||
|
public ProcessMetaDataAdjustment getProcessMetaDataAdjustment()
|
||||||
|
{
|
||||||
|
return (this.processState.getProcessMetaDataAdjustment());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for processMetaDataAdjustment
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setProcessMetaDataAdjustment(ProcessMetaDataAdjustment processMetaDataAdjustment)
|
||||||
|
{
|
||||||
|
this.processState.setProcessMetaDataAdjustment(processMetaDataAdjustment);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -52,12 +52,13 @@ public class CompositeWidgetData extends AbstractBlockWidgetData<CompositeWidget
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public enum Layout
|
public enum Layout
|
||||||
{
|
{
|
||||||
/////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////
|
||||||
// note, these are used in QQQ FMD CompositeWidgetData.tsx //
|
// note, these are used in QQQ FMD CompositeWidget.tsx //
|
||||||
/////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////
|
||||||
FLEX_COLUMN,
|
FLEX_COLUMN,
|
||||||
FLEX_ROW_WRAPPED,
|
FLEX_ROW_WRAPPED,
|
||||||
FLEX_ROW_SPACE_BETWEEN,
|
FLEX_ROW_SPACE_BETWEEN,
|
||||||
|
FLEX_ROW_CENTER,
|
||||||
TABLE_SUB_ROW_DETAILS,
|
TABLE_SUB_ROW_DETAILS,
|
||||||
BADGES_WRAPPER
|
BADGES_WRAPPER
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ import java.util.Map;
|
|||||||
** Base class for the data returned by rendering a Widget.
|
** Base class for the data returned by rendering a Widget.
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public abstract class QWidgetData
|
public abstract class QWidgetData implements Serializable
|
||||||
{
|
{
|
||||||
private String label;
|
private String label;
|
||||||
private String sublabel;
|
private String sublabel;
|
||||||
|
@ -52,6 +52,11 @@ public abstract class AbstractBlockWidgetData<
|
|||||||
private V values;
|
private V values;
|
||||||
private SX styles;
|
private SX styles;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// optional field name to act as a 'guard' for the block - e.g., only include it //
|
||||||
|
// if the value for this field is true //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
private String conditional;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -443,4 +448,35 @@ public abstract class AbstractBlockWidgetData<
|
|||||||
return (T) this;
|
return (T) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for conditional
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getConditional()
|
||||||
|
{
|
||||||
|
return (this.conditional);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for conditional
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setConditional(String conditional)
|
||||||
|
{
|
||||||
|
this.conditional = conditional;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for conditional
|
||||||
|
*******************************************************************************/
|
||||||
|
public AbstractBlockWidgetData withConditional(String conditional)
|
||||||
|
{
|
||||||
|
this.conditional = conditional;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2024. 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.dashboard.widgets.blocks.actionbutton;
|
||||||
|
|
||||||
|
|
||||||
|
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.AbstractBlockWidgetData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.base.BaseSlots;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.base.BaseStyles;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** a button (for a process - not sure yet what this could do in a standalone
|
||||||
|
** widget?) to submit the process screen to run a specific action (e.g., not just
|
||||||
|
** 'next')
|
||||||
|
*******************************************************************************/
|
||||||
|
public class ActionButtonBlockData extends AbstractBlockWidgetData<ActionButtonBlockData, ActionButtonValues, BaseSlots, BaseStyles>
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public String getBlockTypeName()
|
||||||
|
{
|
||||||
|
return "ACTION_BUTTON";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2024. 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.dashboard.widgets.blocks.actionbutton;
|
||||||
|
|
||||||
|
|
||||||
|
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.BlockValuesInterface;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public class ActionButtonValues implements BlockValuesInterface
|
||||||
|
{
|
||||||
|
private String label;
|
||||||
|
private String actionCode;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Constructor
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public ActionButtonValues()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Constructor
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public ActionButtonValues(String label, String actionCode)
|
||||||
|
{
|
||||||
|
setLabel(label);
|
||||||
|
setActionCode(actionCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for label
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getLabel()
|
||||||
|
{
|
||||||
|
return (this.label);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for label
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setLabel(String label)
|
||||||
|
{
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for label
|
||||||
|
*******************************************************************************/
|
||||||
|
public ActionButtonValues withLabel(String label)
|
||||||
|
{
|
||||||
|
this.label = label;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for actionCode
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getActionCode()
|
||||||
|
{
|
||||||
|
return (this.actionCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for actionCode
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setActionCode(String actionCode)
|
||||||
|
{
|
||||||
|
this.actionCode = actionCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for actionCode
|
||||||
|
*******************************************************************************/
|
||||||
|
public ActionButtonValues withActionCode(String actionCode)
|
||||||
|
{
|
||||||
|
this.actionCode = actionCode;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2024. 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.dashboard.widgets.blocks.audio;
|
||||||
|
|
||||||
|
|
||||||
|
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.AbstractBlockWidgetData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.base.BaseSlots;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.base.BaseStyles;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** block that plays an audio file
|
||||||
|
*******************************************************************************/
|
||||||
|
public class AudioBlockData extends AbstractBlockWidgetData<AudioBlockData, AudioValues, BaseSlots, BaseStyles>
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public String getBlockTypeName()
|
||||||
|
{
|
||||||
|
return "AUDIO";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2024. 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.dashboard.widgets.blocks.audio;
|
||||||
|
|
||||||
|
|
||||||
|
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.BlockValuesInterface;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public class AudioValues implements BlockValuesInterface
|
||||||
|
{
|
||||||
|
private String path;
|
||||||
|
private boolean showControls = false;
|
||||||
|
private boolean autoPlay = true;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for path
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getPath()
|
||||||
|
{
|
||||||
|
return (this.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for path
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setPath(String path)
|
||||||
|
{
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for path
|
||||||
|
*******************************************************************************/
|
||||||
|
public AudioValues withPath(String path)
|
||||||
|
{
|
||||||
|
this.path = path;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for showControls
|
||||||
|
*******************************************************************************/
|
||||||
|
public boolean getShowControls()
|
||||||
|
{
|
||||||
|
return (this.showControls);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for showControls
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setShowControls(boolean showControls)
|
||||||
|
{
|
||||||
|
this.showControls = showControls;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for showControls
|
||||||
|
*******************************************************************************/
|
||||||
|
public AudioValues withShowControls(boolean showControls)
|
||||||
|
{
|
||||||
|
this.showControls = showControls;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for autoPlay
|
||||||
|
*******************************************************************************/
|
||||||
|
public boolean getAutoPlay()
|
||||||
|
{
|
||||||
|
return (this.autoPlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for autoPlay
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setAutoPlay(boolean autoPlay)
|
||||||
|
{
|
||||||
|
this.autoPlay = autoPlay;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for autoPlay
|
||||||
|
*******************************************************************************/
|
||||||
|
public AudioValues withAutoPlay(boolean autoPlay)
|
||||||
|
{
|
||||||
|
this.autoPlay = autoPlay;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2024. 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.dashboard.widgets.blocks.image;
|
||||||
|
|
||||||
|
|
||||||
|
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.AbstractBlockWidgetData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.base.BaseSlots;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** block to display an image
|
||||||
|
*******************************************************************************/
|
||||||
|
public class ImageBlockData extends AbstractBlockWidgetData<ImageBlockData, ImageValues, BaseSlots, ImageStyles>
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public String getBlockTypeName()
|
||||||
|
{
|
||||||
|
return "IMAGE";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2024. 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.dashboard.widgets.blocks.image;
|
||||||
|
|
||||||
|
|
||||||
|
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.BlockStylesInterface;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public class ImageStyles implements BlockStylesInterface
|
||||||
|
{
|
||||||
|
private String width;
|
||||||
|
private String height;
|
||||||
|
private boolean bordered = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for bordered
|
||||||
|
*******************************************************************************/
|
||||||
|
public boolean getBordered()
|
||||||
|
{
|
||||||
|
return (this.bordered);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for bordered
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setBordered(boolean bordered)
|
||||||
|
{
|
||||||
|
this.bordered = bordered;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for bordered
|
||||||
|
*******************************************************************************/
|
||||||
|
public ImageStyles withBordered(boolean bordered)
|
||||||
|
{
|
||||||
|
this.bordered = bordered;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for width
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getWidth()
|
||||||
|
{
|
||||||
|
return (this.width);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for width
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setWidth(String width)
|
||||||
|
{
|
||||||
|
this.width = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for width
|
||||||
|
*******************************************************************************/
|
||||||
|
public ImageStyles withWidth(String width)
|
||||||
|
{
|
||||||
|
this.width = width;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for height
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getHeight()
|
||||||
|
{
|
||||||
|
return (this.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for height
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setHeight(String height)
|
||||||
|
{
|
||||||
|
this.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for height
|
||||||
|
*******************************************************************************/
|
||||||
|
public ImageStyles withHeight(String height)
|
||||||
|
{
|
||||||
|
this.height = height;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2024. 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.dashboard.widgets.blocks.image;
|
||||||
|
|
||||||
|
|
||||||
|
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.BlockValuesInterface;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public class ImageValues implements BlockValuesInterface
|
||||||
|
{
|
||||||
|
private String path;
|
||||||
|
private String alt;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for path
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getPath()
|
||||||
|
{
|
||||||
|
return (this.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for path
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setPath(String path)
|
||||||
|
{
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for path
|
||||||
|
*******************************************************************************/
|
||||||
|
public ImageValues withPath(String path)
|
||||||
|
{
|
||||||
|
this.path = path;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for alt
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getAlt()
|
||||||
|
{
|
||||||
|
return (this.alt);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for alt
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setAlt(String alt)
|
||||||
|
{
|
||||||
|
this.alt = alt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for alt
|
||||||
|
*******************************************************************************/
|
||||||
|
public ImageValues withAlt(String alt)
|
||||||
|
{
|
||||||
|
this.alt = alt;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2024. 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.dashboard.widgets.blocks.inputfield;
|
||||||
|
|
||||||
|
|
||||||
|
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.AbstractBlockWidgetData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.base.BaseSlots;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.base.BaseStyles;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** block to display an input field - initially targeted at widgets-in-processes
|
||||||
|
*******************************************************************************/
|
||||||
|
public class InputFieldBlockData extends AbstractBlockWidgetData<InputFieldBlockData, InputFieldValues, BaseSlots, BaseStyles>
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public String getBlockTypeName()
|
||||||
|
{
|
||||||
|
return "INPUT_FIELD";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2024. 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.dashboard.widgets.blocks.inputfield;
|
||||||
|
|
||||||
|
|
||||||
|
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.BlockValuesInterface;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public class InputFieldValues implements BlockValuesInterface
|
||||||
|
{
|
||||||
|
private QFieldMetaData fieldMetaData;
|
||||||
|
private Boolean autoFocus;
|
||||||
|
private Boolean submitOnEnter;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Constructor
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public InputFieldValues()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Constructor
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public InputFieldValues(QFieldMetaData fieldMetaData)
|
||||||
|
{
|
||||||
|
setFieldMetaData(fieldMetaData);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for fieldMetaData
|
||||||
|
*******************************************************************************/
|
||||||
|
public QFieldMetaData getFieldMetaData()
|
||||||
|
{
|
||||||
|
return (this.fieldMetaData);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for fieldMetaData
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setFieldMetaData(QFieldMetaData fieldMetaData)
|
||||||
|
{
|
||||||
|
this.fieldMetaData = fieldMetaData;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for fieldMetaData
|
||||||
|
*******************************************************************************/
|
||||||
|
public InputFieldValues withFieldMetaData(QFieldMetaData fieldMetaData)
|
||||||
|
{
|
||||||
|
this.fieldMetaData = fieldMetaData;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for autoFocus
|
||||||
|
*******************************************************************************/
|
||||||
|
public Boolean getAutoFocus()
|
||||||
|
{
|
||||||
|
return (this.autoFocus);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for autoFocus
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setAutoFocus(Boolean autoFocus)
|
||||||
|
{
|
||||||
|
this.autoFocus = autoFocus;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for autoFocus
|
||||||
|
*******************************************************************************/
|
||||||
|
public InputFieldValues withAutoFocus(Boolean autoFocus)
|
||||||
|
{
|
||||||
|
this.autoFocus = autoFocus;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for submitOnEnter
|
||||||
|
*******************************************************************************/
|
||||||
|
public Boolean getSubmitOnEnter()
|
||||||
|
{
|
||||||
|
return (this.submitOnEnter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for submitOnEnter
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setSubmitOnEnter(Boolean submitOnEnter)
|
||||||
|
{
|
||||||
|
this.submitOnEnter = submitOnEnter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for submitOnEnter
|
||||||
|
*******************************************************************************/
|
||||||
|
public InputFieldValues withSubmitOnEnter(Boolean submitOnEnter)
|
||||||
|
{
|
||||||
|
this.submitOnEnter = submitOnEnter;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -30,4 +30,104 @@ import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.BlockStyles
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class TextStyles implements BlockStylesInterface
|
public class TextStyles implements BlockStylesInterface
|
||||||
{
|
{
|
||||||
|
private StandardColor standardColor;
|
||||||
|
|
||||||
|
private boolean isAlert;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
public enum StandardColor
|
||||||
|
{
|
||||||
|
SUCCESS,
|
||||||
|
WARNING,
|
||||||
|
ERROR,
|
||||||
|
INFO,
|
||||||
|
MUTED
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Constructor
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public TextStyles()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
public TextStyles(StandardColor standardColor)
|
||||||
|
{
|
||||||
|
setStandardColor(standardColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for standardColor
|
||||||
|
*******************************************************************************/
|
||||||
|
public StandardColor getStandardColor()
|
||||||
|
{
|
||||||
|
return (this.standardColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for standardColor
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setStandardColor(StandardColor standardColor)
|
||||||
|
{
|
||||||
|
this.standardColor = standardColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for standardColor
|
||||||
|
*******************************************************************************/
|
||||||
|
public TextStyles withStandardColor(StandardColor standardColor)
|
||||||
|
{
|
||||||
|
this.standardColor = standardColor;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for isAlert
|
||||||
|
*******************************************************************************/
|
||||||
|
public boolean getIsAlert()
|
||||||
|
{
|
||||||
|
return (this.isAlert);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for isAlert
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setIsAlert(boolean isAlert)
|
||||||
|
{
|
||||||
|
this.isAlert = isAlert;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for isAlert
|
||||||
|
*******************************************************************************/
|
||||||
|
public TextStyles withIsAlert(boolean isAlert)
|
||||||
|
{
|
||||||
|
this.isAlert = isAlert;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2024. 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.code;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Specialized type of QCodeReference that takes a lambda function object.
|
||||||
|
**
|
||||||
|
** Originally intended for more concise setup of backend steps in tests - but,
|
||||||
|
** may be generally useful.
|
||||||
|
*******************************************************************************/
|
||||||
|
public class QCodeReferenceLambda<T> extends QCodeReference
|
||||||
|
{
|
||||||
|
private final T lambda;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
public QCodeReferenceLambda(T lambda)
|
||||||
|
{
|
||||||
|
this.lambda = lambda;
|
||||||
|
this.setCodeType(QCodeType.JAVA);
|
||||||
|
this.setName("[Lambda:" + lambda.toString() + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for lambda
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public T getLambda()
|
||||||
|
{
|
||||||
|
return lambda;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -42,6 +42,7 @@ 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.help.HelpRole;
|
import com.kingsrook.qqq.backend.core.model.metadata.help.HelpRole;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.help.QHelpContent;
|
import com.kingsrook.qqq.backend.core.model.metadata.help.QHelpContent;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.security.FieldSecurityLock;
|
import com.kingsrook.qqq.backend.core.model.metadata.security.FieldSecurityLock;
|
||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
@ -73,10 +74,12 @@ public class QFieldMetaData implements Cloneable
|
|||||||
// propose doing that in a secondary field, e.g., "onlyEditableOn=insert|update" //
|
// propose doing that in a secondary field, e.g., "onlyEditableOn=insert|update" //
|
||||||
///////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private String displayFormat = "%s";
|
private String displayFormat = "%s";
|
||||||
private Serializable defaultValue;
|
private Serializable defaultValue;
|
||||||
private String possibleValueSourceName;
|
|
||||||
private QQueryFilter possibleValueSourceFilter;
|
private String possibleValueSourceName;
|
||||||
|
private QQueryFilter possibleValueSourceFilter;
|
||||||
|
private QPossibleValueSource inlinePossibleValueSource;
|
||||||
|
|
||||||
private Integer maxLength;
|
private Integer maxLength;
|
||||||
private Set<FieldBehavior<?>> behaviors;
|
private Set<FieldBehavior<?>> behaviors;
|
||||||
@ -1058,4 +1061,35 @@ public class QFieldMetaData implements Cloneable
|
|||||||
QInstanceHelpContentManager.removeHelpContentByRoleSetFromList(roles, this.helpContents);
|
QInstanceHelpContentManager.removeHelpContentByRoleSetFromList(roles, this.helpContents);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for inlinePossibleValueSource
|
||||||
|
*******************************************************************************/
|
||||||
|
public QPossibleValueSource getInlinePossibleValueSource()
|
||||||
|
{
|
||||||
|
return (this.inlinePossibleValueSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for inlinePossibleValueSource
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setInlinePossibleValueSource(QPossibleValueSource inlinePossibleValueSource)
|
||||||
|
{
|
||||||
|
this.inlinePossibleValueSource = inlinePossibleValueSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for inlinePossibleValueSource
|
||||||
|
*******************************************************************************/
|
||||||
|
public QFieldMetaData withInlinePossibleValueSource(QPossibleValueSource inlinePossibleValueSource)
|
||||||
|
{
|
||||||
|
this.inlinePossibleValueSource = inlinePossibleValueSource;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldBehaviorForFron
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.help.QHelpContent;
|
import com.kingsrook.qqq.backend.core.model.metadata.help.QHelpContent;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
|
|
||||||
|
|
||||||
@ -56,6 +57,7 @@ public class QFrontendFieldMetaData
|
|||||||
|
|
||||||
private List<FieldAdornment> adornments;
|
private List<FieldAdornment> adornments;
|
||||||
private List<QHelpContent> helpContents;
|
private List<QHelpContent> helpContents;
|
||||||
|
private QPossibleValueSource inlinePossibleValueSource;
|
||||||
|
|
||||||
private List<FieldBehaviorForFrontend> behaviors;
|
private List<FieldBehaviorForFrontend> behaviors;
|
||||||
|
|
||||||
@ -81,6 +83,7 @@ public class QFrontendFieldMetaData
|
|||||||
this.adornments = fieldMetaData.getAdornments();
|
this.adornments = fieldMetaData.getAdornments();
|
||||||
this.defaultValue = fieldMetaData.getDefaultValue();
|
this.defaultValue = fieldMetaData.getDefaultValue();
|
||||||
this.helpContents = fieldMetaData.getHelpContents();
|
this.helpContents = fieldMetaData.getHelpContents();
|
||||||
|
this.inlinePossibleValueSource = fieldMetaData.getInlinePossibleValueSource();
|
||||||
|
|
||||||
for(FieldBehavior<?> behavior : CollectionUtils.nonNullCollection(fieldMetaData.getBehaviors()))
|
for(FieldBehavior<?> behavior : CollectionUtils.nonNullCollection(fieldMetaData.getBehaviors()))
|
||||||
{
|
{
|
||||||
@ -218,6 +221,17 @@ public class QFrontendFieldMetaData
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for inlinePossibleValueSource
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QPossibleValueSource getInlinePossibleValueSource()
|
||||||
|
{
|
||||||
|
return inlinePossibleValueSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for fieldBehaviors
|
** Getter for fieldBehaviors
|
||||||
**
|
**
|
||||||
|
@ -31,6 +31,7 @@ import com.kingsrook.qqq.backend.core.actions.permissions.PermissionsHelper;
|
|||||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStateMachineStep;
|
||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
|
|
||||||
|
|
||||||
@ -50,6 +51,7 @@ public class QFrontendProcessMetaData
|
|||||||
private String iconName;
|
private String iconName;
|
||||||
|
|
||||||
private List<QFrontendStepMetaData> frontendSteps;
|
private List<QFrontendStepMetaData> frontendSteps;
|
||||||
|
private String stepFlow;
|
||||||
|
|
||||||
private boolean hasPermission;
|
private boolean hasPermission;
|
||||||
|
|
||||||
@ -68,15 +70,27 @@ public class QFrontendProcessMetaData
|
|||||||
this.label = processMetaData.getLabel();
|
this.label = processMetaData.getLabel();
|
||||||
this.tableName = processMetaData.getTableName();
|
this.tableName = processMetaData.getTableName();
|
||||||
this.isHidden = processMetaData.getIsHidden();
|
this.isHidden = processMetaData.getIsHidden();
|
||||||
|
this.stepFlow = processMetaData.getStepFlow().toString();
|
||||||
|
|
||||||
if(includeSteps)
|
if(includeSteps)
|
||||||
{
|
{
|
||||||
if(CollectionUtils.nullSafeHasContents(processMetaData.getStepList()))
|
if(CollectionUtils.nullSafeHasContents(processMetaData.getStepList()))
|
||||||
{
|
{
|
||||||
this.frontendSteps = processMetaData.getStepList().stream()
|
this.frontendSteps = switch(processMetaData.getStepFlow())
|
||||||
.filter(QFrontendStepMetaData.class::isInstance)
|
{
|
||||||
.map(QFrontendStepMetaData.class::cast)
|
case LINEAR -> processMetaData.getStepList().stream()
|
||||||
.collect(Collectors.toList());
|
.filter(QFrontendStepMetaData.class::isInstance)
|
||||||
|
.map(QFrontendStepMetaData.class::cast)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
case STATE_MACHINE -> processMetaData.getAllSteps().values().stream()
|
||||||
|
.filter(QStateMachineStep.class::isInstance)
|
||||||
|
.map(QStateMachineStep.class::cast)
|
||||||
|
.flatMap(step -> step.getSubSteps().stream())
|
||||||
|
.filter(QFrontendStepMetaData.class::isInstance)
|
||||||
|
.map(QFrontendStepMetaData.class::cast)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -180,4 +194,14 @@ public class QFrontendProcessMetaData
|
|||||||
return hasPermission;
|
return hasPermission;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for stepFlow
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getStepFlow()
|
||||||
|
{
|
||||||
|
return stepFlow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2024. 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.processes;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Possible ways the steps of a process can flow.
|
||||||
|
**
|
||||||
|
** LINEAR - (the default) - the list of steps in the process are executed in-order
|
||||||
|
**
|
||||||
|
** STATE_MACHINE - concept of "states", each which has a backend & frontend step;
|
||||||
|
** a backend step can (must?) set the field "stepState" (or "nextStepName") to
|
||||||
|
** say what the next (frontend) step is.
|
||||||
|
*******************************************************************************/
|
||||||
|
public enum ProcessStepFlow
|
||||||
|
{
|
||||||
|
LINEAR,
|
||||||
|
STATE_MACHINE
|
||||||
|
}
|
@ -57,6 +57,7 @@ public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissi
|
|||||||
private Integer minInputRecords = null;
|
private Integer minInputRecords = null;
|
||||||
private Integer maxInputRecords = null;
|
private Integer maxInputRecords = null;
|
||||||
|
|
||||||
|
private ProcessStepFlow stepFlow = ProcessStepFlow.LINEAR;
|
||||||
private List<QStepMetaData> stepList; // these are the steps that are ran, by-default, in the order they are ran in
|
private List<QStepMetaData> stepList; // these are the steps that are ran, by-default, in the order they are ran in
|
||||||
private Map<String, QStepMetaData> steps; // this is the full map of possible steps
|
private Map<String, QStepMetaData> steps; // this is the full map of possible steps
|
||||||
|
|
||||||
@ -213,11 +214,10 @@ public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissi
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/***************************************************************************
|
||||||
** add a step to the stepList and map
|
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
***************************************************************************/
|
||||||
public QProcessMetaData addStep(QStepMetaData step)
|
public QProcessMetaData withStep(QStepMetaData step)
|
||||||
{
|
{
|
||||||
int index = 0;
|
int index = 0;
|
||||||
if(this.stepList != null)
|
if(this.stepList != null)
|
||||||
@ -231,11 +231,23 @@ public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissi
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** add a step to the stepList and map
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Deprecated(since = "withStep was added")
|
||||||
|
public QProcessMetaData addStep(QStepMetaData step)
|
||||||
|
{
|
||||||
|
return (withStep(step));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** add a step to the stepList (at the specified index) and the step map
|
** add a step to the stepList (at the specified index) and the step map
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QProcessMetaData addStep(int index, QStepMetaData step)
|
public QProcessMetaData withStep(int index, QStepMetaData step)
|
||||||
{
|
{
|
||||||
if(this.stepList == null)
|
if(this.stepList == null)
|
||||||
{
|
{
|
||||||
@ -260,11 +272,23 @@ public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissi
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** add a step to the stepList (at the specified index) and the step map
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Deprecated(since = "withStep was added")
|
||||||
|
public QProcessMetaData addStep(int index, QStepMetaData step)
|
||||||
|
{
|
||||||
|
return (withStep(index, step));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** add a step ONLY to the step map - NOT the list w/ default execution order.
|
** add a step ONLY to the step map - NOT the list w/ default execution order.
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QProcessMetaData addOptionalStep(QStepMetaData step)
|
public QProcessMetaData withOptionalStep(QStepMetaData step)
|
||||||
{
|
{
|
||||||
if(this.steps == null)
|
if(this.steps == null)
|
||||||
{
|
{
|
||||||
@ -283,6 +307,18 @@ public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissi
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** add a step ONLY to the step map - NOT the list w/ default execution order.
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Deprecated(since = "withOptionalStep was added")
|
||||||
|
public QProcessMetaData addOptionalStep(QStepMetaData step)
|
||||||
|
{
|
||||||
|
return (withOptionalStep(step));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Setter for stepList
|
** Setter for stepList
|
||||||
**
|
**
|
||||||
@ -299,7 +335,26 @@ public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissi
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QStepMetaData getStep(String stepName)
|
public QStepMetaData getStep(String stepName)
|
||||||
{
|
{
|
||||||
return (steps.get(stepName));
|
if(steps.containsKey(stepName))
|
||||||
|
{
|
||||||
|
return steps.get(stepName);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(QStepMetaData step : steps.values())
|
||||||
|
{
|
||||||
|
if(step instanceof QStateMachineStep stateMachineStep)
|
||||||
|
{
|
||||||
|
for(QStepMetaData subStep : stateMachineStep.getSubSteps())
|
||||||
|
{
|
||||||
|
if(subStep.getName().equals(stepName))
|
||||||
|
{
|
||||||
|
return (subStep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -780,4 +835,35 @@ public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissi
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for stepFlow
|
||||||
|
*******************************************************************************/
|
||||||
|
public ProcessStepFlow getStepFlow()
|
||||||
|
{
|
||||||
|
return (this.stepFlow);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for stepFlow
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setStepFlow(ProcessStepFlow stepFlow)
|
||||||
|
{
|
||||||
|
this.stepFlow = stepFlow;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for stepFlow
|
||||||
|
*******************************************************************************/
|
||||||
|
public QProcessMetaData withStepFlow(ProcessStepFlow stepFlow)
|
||||||
|
{
|
||||||
|
this.stepFlow = stepFlow;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,188 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2024. 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.processes;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** A step for a state-machine flow based Process.
|
||||||
|
**
|
||||||
|
** Consists of 1 or 2 sub-steps, which are frontend and/or backend.
|
||||||
|
*******************************************************************************/
|
||||||
|
public class QStateMachineStep extends QStepMetaData
|
||||||
|
{
|
||||||
|
private List<QStepMetaData> subSteps = new ArrayList<>();
|
||||||
|
|
||||||
|
private String defaultNextStepName;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Constructor
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private QStateMachineStep(List<QStepMetaData> subSteps)
|
||||||
|
{
|
||||||
|
setStepType("stateMachine");
|
||||||
|
this.subSteps.addAll(subSteps);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
public static QStateMachineStep frontendOnly(String name, QFrontendStepMetaData frontendStepMetaData)
|
||||||
|
{
|
||||||
|
if(!StringUtils.hasContent(frontendStepMetaData.getName()))
|
||||||
|
{
|
||||||
|
frontendStepMetaData.setName(name + ".frontend");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (new QStateMachineStep(List.of(frontendStepMetaData)).withName(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
public static QStateMachineStep backendOnly(String name, QBackendStepMetaData backendStepMetaData)
|
||||||
|
{
|
||||||
|
if(!StringUtils.hasContent(backendStepMetaData.getName()))
|
||||||
|
{
|
||||||
|
backendStepMetaData.setName(name + ".backend");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (new QStateMachineStep(List.of(backendStepMetaData)).withName(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
public static QStateMachineStep frontendThenBackend(String name, QFrontendStepMetaData frontendStepMetaData, QBackendStepMetaData backendStepMetaData)
|
||||||
|
{
|
||||||
|
if(!StringUtils.hasContent(frontendStepMetaData.getName()))
|
||||||
|
{
|
||||||
|
frontendStepMetaData.setName(name + ".frontend");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!StringUtils.hasContent(backendStepMetaData.getName()))
|
||||||
|
{
|
||||||
|
backendStepMetaData.setName(name + ".backend");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (new QStateMachineStep(List.of(frontendStepMetaData, backendStepMetaData)).withName(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
@Override
|
||||||
|
public QStateMachineStep withName(String name)
|
||||||
|
{
|
||||||
|
super.withName(name);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
@Override
|
||||||
|
public QStateMachineStep withLabel(String label)
|
||||||
|
{
|
||||||
|
super.withLabel(label);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for subSteps
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public List<QStepMetaData> getSubSteps()
|
||||||
|
{
|
||||||
|
return subSteps;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for defaultNextStepName
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getDefaultNextStepName()
|
||||||
|
{
|
||||||
|
return (this.defaultNextStepName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for defaultNextStepName
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setDefaultNextStepName(String defaultNextStepName)
|
||||||
|
{
|
||||||
|
this.defaultNextStepName = defaultNextStepName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for defaultNextStepName
|
||||||
|
*******************************************************************************/
|
||||||
|
public QStateMachineStep withDefaultNextStepName(String defaultNextStepName)
|
||||||
|
{
|
||||||
|
this.defaultNextStepName = defaultNextStepName;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Get a list of all of the input fields used by this step (all of its sub-steps)
|
||||||
|
*******************************************************************************/
|
||||||
|
@JsonIgnore
|
||||||
|
@Override
|
||||||
|
public List<QFieldMetaData> getInputFields()
|
||||||
|
{
|
||||||
|
List<QFieldMetaData> rs = new ArrayList<>();
|
||||||
|
for(QStepMetaData subStep : subSteps)
|
||||||
|
{
|
||||||
|
rs.addAll(subStep.getInputFields());
|
||||||
|
}
|
||||||
|
return (rs);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -176,9 +176,9 @@ public class StreamedETLExecuteStep extends BaseStreamedETLStep implements Backe
|
|||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// propagate data from inner-step state to process-level step state //
|
// propagate data from inner-step state to process-level step state //
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
if(postRunOutput.getUpdatedFrontendStepList() != null)
|
if(postRunOutput.getProcessMetaDataAdjustment() != null)
|
||||||
{
|
{
|
||||||
runBackendStepOutput.setUpdatedFrontendStepList(postRunOutput.getUpdatedFrontendStepList());
|
runBackendStepOutput.setProcessMetaDataAdjustment(postRunOutput.getProcessMetaDataAdjustment());
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -281,10 +281,10 @@ public class StreamedETLExecuteStep extends BaseStreamedETLStep implements Backe
|
|||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// propagate data from inner-step state to process-level step state //
|
// propagate data from inner-step state to process-level step state //
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
if(streamedBackendStepOutput.getUpdatedFrontendStepList() != null)
|
if(streamedBackendStepOutput.getProcessMetaDataAdjustment() != null)
|
||||||
{
|
{
|
||||||
runBackendStepOutput.getProcessState().setStepList(streamedBackendStepOutput.getProcessState().getStepList());
|
runBackendStepOutput.getProcessState().setStepList(streamedBackendStepOutput.getProcessState().getStepList());
|
||||||
runBackendStepOutput.setUpdatedFrontendStepList(streamedBackendStepOutput.getUpdatedFrontendStepList());
|
runBackendStepOutput.getProcessState().setProcessMetaDataAdjustment(streamedBackendStepOutput.getProcessMetaDataAdjustment());
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
@ -299,10 +299,10 @@ public class StreamedETLExecuteStep extends BaseStreamedETLStep implements Backe
|
|||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// propagate data from inner-step state to process-level step state //
|
// propagate data from inner-step state to process-level step state //
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
if(streamedBackendStepOutput.getUpdatedFrontendStepList() != null)
|
if(streamedBackendStepOutput.getProcessMetaDataAdjustment() != null)
|
||||||
{
|
{
|
||||||
runBackendStepOutput.getProcessState().setStepList(streamedBackendStepOutput.getProcessState().getStepList());
|
runBackendStepOutput.getProcessState().setStepList(streamedBackendStepOutput.getProcessState().getStepList());
|
||||||
runBackendStepOutput.setUpdatedFrontendStepList(streamedBackendStepOutput.getUpdatedFrontendStepList());
|
runBackendStepOutput.getProcessState().setProcessMetaDataAdjustment(streamedBackendStepOutput.getProcessMetaDataAdjustment());
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////
|
||||||
|
@ -148,9 +148,9 @@ public class StreamedETLPreviewStep extends BaseStreamedETLStep implements Backe
|
|||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// propagate data from inner-step state to process-level step state //
|
// propagate data from inner-step state to process-level step state //
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
if(postRunOutput.getUpdatedFrontendStepList() != null)
|
if(postRunOutput.getProcessMetaDataAdjustment() != null)
|
||||||
{
|
{
|
||||||
runBackendStepOutput.setUpdatedFrontendStepList(postRunOutput.getUpdatedFrontendStepList());
|
runBackendStepOutput.setProcessMetaDataAdjustment(postRunOutput.getProcessMetaDataAdjustment());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,10 +219,9 @@ public class StreamedETLPreviewStep extends BaseStreamedETLStep implements Backe
|
|||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// propagate data from inner-step state to process-level step state //
|
// propagate data from inner-step state to process-level step state //
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
if(streamedBackendStepOutput.getUpdatedFrontendStepList() != null)
|
if(streamedBackendStepOutput.getProcessMetaDataAdjustment() != null)
|
||||||
{
|
{
|
||||||
runBackendStepOutput.getProcessState().setStepList(streamedBackendStepOutput.getProcessState().getStepList());
|
runBackendStepOutput.getProcessState().setProcessMetaDataAdjustment(streamedBackendStepOutput.getProcessMetaDataAdjustment());
|
||||||
runBackendStepOutput.setUpdatedFrontendStepList(streamedBackendStepOutput.getUpdatedFrontendStepList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////
|
////////////////////////////////////////////////////
|
||||||
|
@ -145,9 +145,9 @@ public class StreamedETLValidateStep extends BaseStreamedETLStep implements Back
|
|||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// propagate data from inner-step state to process-level step state //
|
// propagate data from inner-step state to process-level step state //
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
if(postRunOutput.getUpdatedFrontendStepList() != null)
|
if(postRunOutput.getProcessMetaDataAdjustment() != null)
|
||||||
{
|
{
|
||||||
runBackendStepOutput.setUpdatedFrontendStepList(postRunOutput.getUpdatedFrontendStepList());
|
runBackendStepOutput.setProcessMetaDataAdjustment(postRunOutput.getProcessMetaDataAdjustment());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,10 +183,9 @@ public class StreamedETLValidateStep extends BaseStreamedETLStep implements Back
|
|||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// propagate data from inner-step state to process-level step state //
|
// propagate data from inner-step state to process-level step state //
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
if(streamedBackendStepOutput.getUpdatedFrontendStepList() != null)
|
if(streamedBackendStepOutput.getProcessMetaDataAdjustment() != null)
|
||||||
{
|
{
|
||||||
runBackendStepOutput.getProcessState().setStepList(streamedBackendStepOutput.getProcessState().getStepList());
|
runBackendStepOutput.setProcessMetaDataAdjustment(streamedBackendStepOutput.getProcessMetaDataAdjustment());
|
||||||
runBackendStepOutput.setUpdatedFrontendStepList(streamedBackendStepOutput.getUpdatedFrontendStepList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////
|
||||||
|
@ -0,0 +1,327 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2024. 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.processes;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||||
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessOutput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReferenceLambda;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.ProcessStepFlow;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStateMachineStep;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
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.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Unit test for RunProcessAction
|
||||||
|
*******************************************************************************/
|
||||||
|
class RunProcessActionTest extends BaseTest
|
||||||
|
{
|
||||||
|
private static List<String> log = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@BeforeEach
|
||||||
|
void beforeEach()
|
||||||
|
{
|
||||||
|
log.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testStateMachineTwoBackendSteps() throws QException
|
||||||
|
{
|
||||||
|
QProcessMetaData process = new QProcessMetaData().withName("test")
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
// two-steps - a, points at b; b has no next-step, so it exits //
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
.addStep(QStateMachineStep.backendOnly("a", new QBackendStepMetaData().withName("aBackend")
|
||||||
|
.withCode(new QCodeReferenceLambda<BackendStep>((runBackendStepInput, runBackendStepOutput) ->
|
||||||
|
{
|
||||||
|
log.add("in StepA");
|
||||||
|
runBackendStepOutput.getProcessState().setNextStepName("b");
|
||||||
|
}))))
|
||||||
|
|
||||||
|
.addStep(QStateMachineStep.backendOnly("b", new QBackendStepMetaData().withName("bBackend")
|
||||||
|
.withCode(new QCodeReferenceLambda<BackendStep>((runBackendStepInput, runBackendStepOutput) ->
|
||||||
|
{
|
||||||
|
log.add("in StepB");
|
||||||
|
}))))
|
||||||
|
|
||||||
|
.withStepFlow(ProcessStepFlow.STATE_MACHINE);
|
||||||
|
|
||||||
|
QContext.getQInstance().addProcess(process);
|
||||||
|
|
||||||
|
RunProcessInput input = new RunProcessInput();
|
||||||
|
input.setProcessName("test");
|
||||||
|
input.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.BREAK);
|
||||||
|
|
||||||
|
RunProcessOutput runProcessOutput = new RunProcessAction().execute(input);
|
||||||
|
assertEquals(List.of("in StepA", "in StepB"), log);
|
||||||
|
assertThat(runProcessOutput.getProcessState().getNextStepName()).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testStateMachineTwoFrontendOnlySteps() throws QException
|
||||||
|
{
|
||||||
|
QProcessMetaData process = new QProcessMetaData().withName("test")
|
||||||
|
|
||||||
|
.addStep(QStateMachineStep.frontendOnly("a", new QFrontendStepMetaData().withName("aFrontend")).withDefaultNextStepName("b"))
|
||||||
|
.addStep(QStateMachineStep.frontendOnly("b", new QFrontendStepMetaData().withName("bFrontend")))
|
||||||
|
|
||||||
|
.withStepFlow(ProcessStepFlow.STATE_MACHINE);
|
||||||
|
|
||||||
|
QContext.getQInstance().addProcess(process);
|
||||||
|
|
||||||
|
RunProcessInput input = new RunProcessInput();
|
||||||
|
input.setProcessName("test");
|
||||||
|
input.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.BREAK);
|
||||||
|
|
||||||
|
RunProcessOutput runProcessOutput = new RunProcessAction().execute(input);
|
||||||
|
assertThat(runProcessOutput.getProcessState().getNextStepName())
|
||||||
|
.isPresent().get()
|
||||||
|
.isEqualTo("aFrontend");
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// resume after a, go to b //
|
||||||
|
/////////////////////////////
|
||||||
|
input.setStartAfterStep("aFrontend");
|
||||||
|
runProcessOutput = new RunProcessAction().execute(input);
|
||||||
|
assertThat(runProcessOutput.getProcessState().getNextStepName())
|
||||||
|
.isPresent().get()
|
||||||
|
.isEqualTo("bFrontend");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testStateMachineOneBackendStepReferencingItselfDoesNotInfiniteLoop() throws QException
|
||||||
|
{
|
||||||
|
QProcessMetaData process = new QProcessMetaData().withName("test")
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////
|
||||||
|
// set up step that always points back at itself. //
|
||||||
|
// since it never goes to the frontend, it'll stack overflow //
|
||||||
|
// (though we'll catch it ourselves before JVM does) //
|
||||||
|
///////////////////////////////////////////////////////////////
|
||||||
|
.addStep(QStateMachineStep.backendOnly("a", new QBackendStepMetaData().withName("aBackend")
|
||||||
|
.withCode(new QCodeReferenceLambda<BackendStep>((runBackendStepInput, runBackendStepOutput) ->
|
||||||
|
{
|
||||||
|
log.add("in StepA");
|
||||||
|
runBackendStepOutput.getProcessState().setNextStepName("a");
|
||||||
|
}))))
|
||||||
|
|
||||||
|
.withStepFlow(ProcessStepFlow.STATE_MACHINE);
|
||||||
|
|
||||||
|
QContext.getQInstance().addProcess(process);
|
||||||
|
|
||||||
|
RunProcessInput input = new RunProcessInput();
|
||||||
|
input.setProcessName("test");
|
||||||
|
input.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.BREAK);
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> new RunProcessAction().execute(input))
|
||||||
|
.isInstanceOf(QException.class)
|
||||||
|
.hasMessageContaining("maxStateMachineProcessStepFlowStackDepth of 20");
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////
|
||||||
|
// make sure we can set a custom max-stack-depth //
|
||||||
|
///////////////////////////////////////////////////
|
||||||
|
input.addValue("maxStateMachineProcessStepFlowStackDepth", 5);
|
||||||
|
assertThatThrownBy(() -> new RunProcessAction().execute(input))
|
||||||
|
.isInstanceOf(QException.class)
|
||||||
|
.hasMessageContaining("maxStateMachineProcessStepFlowStackDepth of 5");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testStateMachineTwoBackendStepsReferencingEachOtherDoesNotInfiniteLoop() throws QException
|
||||||
|
{
|
||||||
|
QProcessMetaData process = new QProcessMetaData().withName("test")
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////
|
||||||
|
// set up two steps that always points back at each other. //
|
||||||
|
// since it never goes to the frontend, it'll stack overflow //
|
||||||
|
// (though we'll catch it ourselves before JVM does) //
|
||||||
|
///////////////////////////////////////////////////////////////
|
||||||
|
.addStep(QStateMachineStep.backendOnly("a", new QBackendStepMetaData().withName("aBackend")
|
||||||
|
.withCode(new QCodeReferenceLambda<BackendStep>((runBackendStepInput, runBackendStepOutput) ->
|
||||||
|
{
|
||||||
|
log.add("in StepA");
|
||||||
|
runBackendStepOutput.getProcessState().setNextStepName("b");
|
||||||
|
}))))
|
||||||
|
|
||||||
|
.addStep(QStateMachineStep.backendOnly("b", new QBackendStepMetaData().withName("bBackend")
|
||||||
|
.withCode(new QCodeReferenceLambda<BackendStep>((runBackendStepInput, runBackendStepOutput) ->
|
||||||
|
{
|
||||||
|
log.add("in StepB");
|
||||||
|
runBackendStepOutput.getProcessState().setNextStepName("a");
|
||||||
|
}))))
|
||||||
|
|
||||||
|
.withStepFlow(ProcessStepFlow.STATE_MACHINE);
|
||||||
|
|
||||||
|
QContext.getQInstance().addProcess(process);
|
||||||
|
|
||||||
|
RunProcessInput input = new RunProcessInput();
|
||||||
|
input.setProcessName("test");
|
||||||
|
input.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.BREAK);
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> new RunProcessAction().execute(input))
|
||||||
|
.isInstanceOf(QException.class)
|
||||||
|
.hasMessageContaining("maxStateMachineProcessStepFlowStackDepth of 20");
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////
|
||||||
|
// make sure we can set a custom max-stack-depth //
|
||||||
|
///////////////////////////////////////////////////
|
||||||
|
input.addValue("maxStateMachineProcessStepFlowStackDepth", 5);
|
||||||
|
assertThatThrownBy(() -> new RunProcessAction().execute(input))
|
||||||
|
.isInstanceOf(QException.class)
|
||||||
|
.hasMessageContaining("maxStateMachineProcessStepFlowStackDepth of 5");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testStateSequenceOfFrontendAndBackendSteps() throws QException
|
||||||
|
{
|
||||||
|
QProcessMetaData process = new QProcessMetaData().withName("test")
|
||||||
|
|
||||||
|
.addStep(QStateMachineStep.frontendThenBackend("a",
|
||||||
|
new QFrontendStepMetaData().withName("aFrontend"),
|
||||||
|
new QBackendStepMetaData().withName("aBackend")
|
||||||
|
.withCode(new QCodeReferenceLambda<BackendStep>((runBackendStepInput, runBackendStepOutput) ->
|
||||||
|
{
|
||||||
|
log.add("in StepA");
|
||||||
|
runBackendStepOutput.getProcessState().setNextStepName("b");
|
||||||
|
}))))
|
||||||
|
|
||||||
|
.addStep(QStateMachineStep.frontendThenBackend("b",
|
||||||
|
new QFrontendStepMetaData().withName("bFrontend"),
|
||||||
|
new QBackendStepMetaData().withName("bBackend")
|
||||||
|
.withCode(new QCodeReferenceLambda<BackendStep>((runBackendStepInput, runBackendStepOutput) ->
|
||||||
|
{
|
||||||
|
log.add("in StepB");
|
||||||
|
runBackendStepOutput.getProcessState().setNextStepName("c");
|
||||||
|
}))))
|
||||||
|
|
||||||
|
.addStep(QStateMachineStep.frontendThenBackend("c",
|
||||||
|
new QFrontendStepMetaData().withName("cFrontend"),
|
||||||
|
new QBackendStepMetaData().withName("cBackend")
|
||||||
|
.withCode(new QCodeReferenceLambda<BackendStep>((runBackendStepInput, runBackendStepOutput) ->
|
||||||
|
{
|
||||||
|
log.add("in StepC");
|
||||||
|
runBackendStepOutput.getProcessState().setNextStepName("d");
|
||||||
|
}))))
|
||||||
|
|
||||||
|
.addStep(QStateMachineStep.frontendOnly("d",
|
||||||
|
new QFrontendStepMetaData().withName("dFrontend")))
|
||||||
|
|
||||||
|
.withStepFlow(ProcessStepFlow.STATE_MACHINE);
|
||||||
|
|
||||||
|
QContext.getQInstance().addProcess(process);
|
||||||
|
|
||||||
|
RunProcessInput input = new RunProcessInput();
|
||||||
|
input.setProcessName("test");
|
||||||
|
input.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.BREAK);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
// start the process - we should be sent to aFrontend //
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
RunProcessOutput runProcessOutput = new RunProcessAction().execute(input);
|
||||||
|
assertThat(runProcessOutput.getProcessState().getNextStepName())
|
||||||
|
.isPresent().get()
|
||||||
|
.isEqualTo("aFrontend");
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// resume after aFrontend - we should run StepA (backend), and then be sent to bFrontend //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
input.setStartAfterStep("aFrontend");
|
||||||
|
runProcessOutput = new RunProcessAction().execute(input);
|
||||||
|
assertEquals(List.of("in StepA"), log);
|
||||||
|
assertThat(runProcessOutput.getProcessState().getNextStepName())
|
||||||
|
.isPresent().get()
|
||||||
|
.isEqualTo("bFrontend");
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// resume after bFrontend - we should run StepB (backend), and then be sent to cFrontend //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
input.setStartAfterStep("bFrontend");
|
||||||
|
runProcessOutput = new RunProcessAction().execute(input);
|
||||||
|
assertEquals(List.of("in StepA", "in StepB"), log);
|
||||||
|
assertThat(runProcessOutput.getProcessState().getNextStepName())
|
||||||
|
.isPresent().get()
|
||||||
|
.isEqualTo("cFrontend");
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// resume after cFrontend - we should run StepC (backend), and then be sent to dFrontend //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
input.setStartAfterStep("cFrontend");
|
||||||
|
runProcessOutput = new RunProcessAction().execute(input);
|
||||||
|
assertEquals(List.of("in StepA", "in StepB", "in StepC"), log);
|
||||||
|
assertThat(runProcessOutput.getProcessState().getNextStepName())
|
||||||
|
.isPresent().get()
|
||||||
|
.isEqualTo("dFrontend");
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// if we resume again here, we'll be past the end of the process, so no next-step //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
input.setStartAfterStep("dFrontend");
|
||||||
|
runProcessOutput = new RunProcessAction().execute(input);
|
||||||
|
assertEquals(List.of("in StepA", "in StepB", "in StepC"), log);
|
||||||
|
assertThat(runProcessOutput.getProcessState().getNextStepName()).isEmpty();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -265,6 +265,38 @@ public class QInstanceValidatorTest extends BaseTest
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testTableFieldInlinePossibleValueSource()
|
||||||
|
{
|
||||||
|
////////////////////////////////////////////////////
|
||||||
|
// make sure can't have both named and inline PVS //
|
||||||
|
////////////////////////////////////////////////////
|
||||||
|
assertValidationFailureReasonsAllowingExtraReasons((qInstance) -> qInstance.getTable("person").getField("homeStateId")
|
||||||
|
.withInlinePossibleValueSource(new QPossibleValueSource().withType(QPossibleValueSourceType.TABLE).withTableName("person")),
|
||||||
|
"both a possibleValueSourceName and an inlinePossibleValueSource");
|
||||||
|
|
||||||
|
/////////////////////////////////////////////
|
||||||
|
// make require inline PVS to be enum type //
|
||||||
|
/////////////////////////////////////////////
|
||||||
|
assertValidationFailureReasonsAllowingExtraReasons((qInstance) -> qInstance.getTable("person").getField("homeStateId")
|
||||||
|
.withPossibleValueSourceName(null)
|
||||||
|
.withInlinePossibleValueSource(new QPossibleValueSource().withType(QPossibleValueSourceType.TABLE)),
|
||||||
|
"must have a type of ENUM");
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////
|
||||||
|
// make sure validation on the inline PVS happens //
|
||||||
|
////////////////////////////////////////////////////
|
||||||
|
assertValidationFailureReasonsAllowingExtraReasons((qInstance) -> qInstance.getTable("person").getField("homeStateId")
|
||||||
|
.withPossibleValueSourceName(null)
|
||||||
|
.withInlinePossibleValueSource(new QPossibleValueSource().withType(QPossibleValueSourceType.ENUM)),
|
||||||
|
"missing enum values");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Test that if a process specifies a table that doesn't exist, that it fails.
|
** Test that if a process specifies a table that doesn't exist, that it fails.
|
||||||
**
|
**
|
||||||
@ -717,8 +749,8 @@ public class QInstanceValidatorTest extends BaseTest
|
|||||||
@Test
|
@Test
|
||||||
public void test_validateFieldWithMissingPossibleValueSource()
|
public void test_validateFieldWithMissingPossibleValueSource()
|
||||||
{
|
{
|
||||||
assertValidationFailureReasons((qInstance) -> qInstance.getTable("person").getField("homeStateId").setPossibleValueSourceName("not a real possible value source"),
|
assertValidationFailureReasonsAllowingExtraReasons((qInstance) -> qInstance.getTable("person").getField("homeStateId").setPossibleValueSourceName("not a real possible value source"),
|
||||||
"Unrecognized possibleValueSourceName");
|
"unrecognized possibleValueSourceName");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@ import java.util.Objects;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import com.kingsrook.qqq.backend.core.actions.async.AsyncJobManager;
|
import com.kingsrook.qqq.backend.core.actions.async.AsyncJobManager;
|
||||||
import com.kingsrook.qqq.backend.core.actions.async.AsyncJobState;
|
import com.kingsrook.qqq.backend.core.actions.async.AsyncJobState;
|
||||||
import com.kingsrook.qqq.backend.core.actions.async.AsyncJobStatus;
|
import com.kingsrook.qqq.backend.core.actions.async.AsyncJobStatus;
|
||||||
@ -432,7 +433,18 @@ public class QJavalinProcessHandler
|
|||||||
QJavalinAccessLogger.logEndSuccess();
|
QJavalinAccessLogger.logEndSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
context.result(JsonUtils.toJson(resultForCaller));
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Note: originally we did not have this serializationInclusion:ALWAYS here - //
|
||||||
|
// which meant that null and empty values from backend would not go to frontend, //
|
||||||
|
// which made things like clearing out a value in a field not happen. //
|
||||||
|
// So, this is added to get that beneficial effect. Unclear if there are any //
|
||||||
|
// negative side-effects - but be aware. //
|
||||||
|
// One could imagine that we'd need this to be configurable in the future? //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
context.result(JsonUtils.toJson(resultForCaller, mapper ->
|
||||||
|
{
|
||||||
|
mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -453,11 +465,19 @@ public class QJavalinProcessHandler
|
|||||||
resultForCaller.put("values", runProcessOutput.getValues());
|
resultForCaller.put("values", runProcessOutput.getValues());
|
||||||
runProcessOutput.getProcessState().getNextStepName().ifPresent(nextStep -> resultForCaller.put("nextStep", nextStep));
|
runProcessOutput.getProcessState().getNextStepName().ifPresent(nextStep -> resultForCaller.put("nextStep", nextStep));
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// todo - delete after all frontends look for processMetaDataAdjustment instead of updatedFrontendStepList //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
List<QFrontendStepMetaData> updatedFrontendStepList = runProcessOutput.getUpdatedFrontendStepList();
|
List<QFrontendStepMetaData> updatedFrontendStepList = runProcessOutput.getUpdatedFrontendStepList();
|
||||||
if(updatedFrontendStepList != null)
|
if(updatedFrontendStepList != null)
|
||||||
{
|
{
|
||||||
resultForCaller.put("updatedFrontendStepList", updatedFrontendStepList);
|
resultForCaller.put("updatedFrontendStepList", updatedFrontendStepList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(runProcessOutput.getProcessMetaDataAdjustment() != null)
|
||||||
|
{
|
||||||
|
resultForCaller.put("processMetaDataAdjustment", runProcessOutput.getProcessMetaDataAdjustment());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user