CE-1955 - Add back to processes

This commit is contained in:
2024-11-27 14:59:57 -06:00
parent 9213b8987b
commit 8c6b4e6863
10 changed files with 437 additions and 28 deletions

View File

@ -23,10 +23,15 @@ package com.kingsrook.qqq.backend.core.actions.processes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
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.ProcessState;
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;
@ -35,6 +40,8 @@ import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaD
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 com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
import com.kingsrook.qqq.backend.core.utils.collections.MultiLevelMapHelper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
@ -73,14 +80,14 @@ class RunProcessActionTest extends BaseTest
/////////////////////////////////////////////////////////////////
// two-steps - a, points at b; b has no next-step, so it exits //
/////////////////////////////////////////////////////////////////
.addStep(QStateMachineStep.backendOnly("a", new QBackendStepMetaData().withName("aBackend")
.withStep(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")
.withStep(QStateMachineStep.backendOnly("b", new QBackendStepMetaData().withName("bBackend")
.withCode(new QCodeReferenceLambda<BackendStep>((runBackendStepInput, runBackendStepOutput) ->
{
log.add("in StepB");
@ -109,8 +116,8 @@ class RunProcessActionTest extends BaseTest
{
QProcessMetaData process = new QProcessMetaData().withName("test")
.addStep(QStateMachineStep.frontendOnly("a", new QFrontendStepMetaData().withName("aFrontend")).withDefaultNextStepName("b"))
.addStep(QStateMachineStep.frontendOnly("b", new QFrontendStepMetaData().withName("bFrontend")))
.withStep(QStateMachineStep.frontendOnly("a", new QFrontendStepMetaData().withName("aFrontend")).withDefaultNextStepName("b"))
.withStep(QStateMachineStep.frontendOnly("b", new QFrontendStepMetaData().withName("bFrontend")))
.withStepFlow(ProcessStepFlow.STATE_MACHINE);
@ -150,7 +157,7 @@ class RunProcessActionTest extends BaseTest
// 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")
.withStep(QStateMachineStep.backendOnly("a", new QBackendStepMetaData().withName("aBackend")
.withCode(new QCodeReferenceLambda<BackendStep>((runBackendStepInput, runBackendStepOutput) ->
{
log.add("in StepA");
@ -193,14 +200,14 @@ class RunProcessActionTest extends BaseTest
// 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")
.withStep(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")
.withStep(QStateMachineStep.backendOnly("b", new QBackendStepMetaData().withName("bBackend")
.withCode(new QCodeReferenceLambda<BackendStep>((runBackendStepInput, runBackendStepOutput) ->
{
log.add("in StepB");
@ -238,7 +245,7 @@ class RunProcessActionTest extends BaseTest
{
QProcessMetaData process = new QProcessMetaData().withName("test")
.addStep(QStateMachineStep.frontendThenBackend("a",
.withStep(QStateMachineStep.frontendThenBackend("a",
new QFrontendStepMetaData().withName("aFrontend"),
new QBackendStepMetaData().withName("aBackend")
.withCode(new QCodeReferenceLambda<BackendStep>((runBackendStepInput, runBackendStepOutput) ->
@ -247,7 +254,7 @@ class RunProcessActionTest extends BaseTest
runBackendStepOutput.getProcessState().setNextStepName("b");
}))))
.addStep(QStateMachineStep.frontendThenBackend("b",
.withStep(QStateMachineStep.frontendThenBackend("b",
new QFrontendStepMetaData().withName("bFrontend"),
new QBackendStepMetaData().withName("bBackend")
.withCode(new QCodeReferenceLambda<BackendStep>((runBackendStepInput, runBackendStepOutput) ->
@ -256,7 +263,7 @@ class RunProcessActionTest extends BaseTest
runBackendStepOutput.getProcessState().setNextStepName("c");
}))))
.addStep(QStateMachineStep.frontendThenBackend("c",
.withStep(QStateMachineStep.frontendThenBackend("c",
new QFrontendStepMetaData().withName("cFrontend"),
new QBackendStepMetaData().withName("cBackend")
.withCode(new QCodeReferenceLambda<BackendStep>((runBackendStepInput, runBackendStepOutput) ->
@ -265,7 +272,7 @@ class RunProcessActionTest extends BaseTest
runBackendStepOutput.getProcessState().setNextStepName("d");
}))))
.addStep(QStateMachineStep.frontendOnly("d",
.withStep(QStateMachineStep.frontendOnly("d",
new QFrontendStepMetaData().withName("dFrontend")))
.withStepFlow(ProcessStepFlow.STATE_MACHINE);
@ -321,7 +328,132 @@ class RunProcessActionTest extends BaseTest
runProcessOutput = new RunProcessAction().execute(input);
assertEquals(List.of("in StepA", "in StepB", "in StepC"), log);
assertThat(runProcessOutput.getProcessState().getNextStepName()).isEmpty();
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testGoingBack() throws QException
{
AtomicInteger backCount = new AtomicInteger(0);
Map<String, Integer> stepRunCounts = new HashMap<>();
BackendStep backendStep = (runBackendStepInput, runBackendStepOutput) ->
{
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// shared backend-step lambda, that will do the same thing for both - but using step name to count how many times each is executed. //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MultiLevelMapHelper.getOrPutAndIncrement(stepRunCounts, runBackendStepInput.getStepName());
if(runBackendStepInput.getIsStepBack())
{
backCount.incrementAndGet();
}
};
///////////////////////////////////////////////////////////
// normal flow here: a -> b -> c //
// but, b can go back to a, as in: a -> b -> a -> b -> c //
///////////////////////////////////////////////////////////
QProcessMetaData process = new QProcessMetaData().withName("test")
.withStep(new QBackendStepMetaData()
.withName("a")
.withCode(new QCodeReferenceLambda<>(backendStep)))
.withStep(new QFrontendStepMetaData()
.withName("b")
.withBackStepName("a"))
.withStep(new QBackendStepMetaData()
.withName("c")
.withCode(new QCodeReferenceLambda<>(backendStep)))
.withStepFlow(ProcessStepFlow.LINEAR);
QContext.getQInstance().addProcess(process);
RunProcessInput input = new RunProcessInput();
input.setProcessName("test");
input.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.BREAK);
///////////////////////////////////////////////////////////
// start the process - we should be sent to b (frontend) //
///////////////////////////////////////////////////////////
RunProcessOutput runProcessOutput = new RunProcessAction().execute(input);
assertThat(runProcessOutput.getProcessState().getNextStepName())
.isPresent().get()
.isEqualTo("b");
assertEquals(0, backCount.get());
assertEquals(Map.of("a", 1), stepRunCounts);
////////////////////////////////////////////////////////////////
// resume after b, but in back-mode - should end up back at b //
////////////////////////////////////////////////////////////////
input.setStartAfterStep(null);
input.setStartAtStep("a");
runProcessOutput = new RunProcessAction().execute(input);
assertThat(runProcessOutput.getProcessState().getNextStepName())
.isPresent().get()
.isEqualTo("b");
assertEquals(1, backCount.get());
assertEquals(Map.of("a", 2), stepRunCounts);
////////////////////////////////////////////////////////////////////////////
// resume after b, in regular (forward) mode - should wrap up the process //
////////////////////////////////////////////////////////////////////////////
input.setStartAfterStep("b");
input.setStartAtStep(null);
runProcessOutput = new RunProcessAction().execute(input);
assertThat(runProcessOutput.getProcessState().getNextStepName())
.isEmpty();
assertEquals(1, backCount.get());
assertEquals(Map.of("a", 2, "c", 1), stepRunCounts);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testGetAvailableStepList() throws QException
{
QProcessMetaData process = new QProcessMetaData()
.withStep(new QBackendStepMetaData().withName("A"))
.withStep(new QBackendStepMetaData().withName("B"))
.withStep(new QBackendStepMetaData().withName("C"))
.withStep(new QBackendStepMetaData().withName("D"))
.withStep(new QBackendStepMetaData().withName("E"));
ProcessState processState = new ProcessState();
processState.setStepList(process.getStepList().stream().map(s -> s.getName()).toList());
assertStepListNames(List.of("A", "B", "C", "D", "E"), RunProcessAction.getAvailableStepList(processState, process, null, false));
assertStepListNames(List.of("A", "B", "C", "D", "E"), RunProcessAction.getAvailableStepList(processState, process, null, true));
assertStepListNames(List.of("B", "C", "D", "E"), RunProcessAction.getAvailableStepList(processState, process, "A", false));
assertStepListNames(List.of("A", "B", "C", "D", "E"), RunProcessAction.getAvailableStepList(processState, process, "A", true));
assertStepListNames(List.of("D", "E"), RunProcessAction.getAvailableStepList(processState, process, "C", false));
assertStepListNames(List.of("C", "D", "E"), RunProcessAction.getAvailableStepList(processState, process, "C", true));
assertStepListNames(Collections.emptyList(), RunProcessAction.getAvailableStepList(processState, process, "E", false));
assertStepListNames(List.of("E"), RunProcessAction.getAvailableStepList(processState, process, "E", true));
assertStepListNames(Collections.emptyList(), RunProcessAction.getAvailableStepList(processState, process, "Z", false));
assertStepListNames(Collections.emptyList(), RunProcessAction.getAvailableStepList(processState, process, "Z", true));
}
/***************************************************************************
**
***************************************************************************/
private void assertStepListNames(List<String> expectedNames, List<QStepMetaData> actualSteps)
{
assertEquals(expectedNames, actualSteps.stream().map(s -> s.getName()).toList());
}
}