diff --git a/src/main/java/com/kingsrook/qqq/backend/core/actions/RunProcessAction.java b/src/main/java/com/kingsrook/qqq/backend/core/actions/RunProcessAction.java index e959a747..d34326ce 100644 --- a/src/main/java/com/kingsrook/qqq/backend/core/actions/RunProcessAction.java +++ b/src/main/java/com/kingsrook/qqq/backend/core/actions/RunProcessAction.java @@ -35,6 +35,7 @@ import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepResu import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessRequest; import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessResult; 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.QStepMetaData; import com.kingsrook.qqq.backend.core.state.InMemoryStateProvider; @@ -86,30 +87,53 @@ public class RunProcessAction List stepList = getAvailableStepList(process, runProcessRequest); try { + STEP_LOOP: for(QStepMetaData step : stepList) { - //////////////////////////////////////////////////////////////////////////////////////////////// - // if the caller requested to only run backend steps, then break if this isn't a backend step // - //////////////////////////////////////////////////////////////////////////////////////////////// - if(runProcessRequest.getBackendOnly()) + if(step instanceof QFrontendStepMetaData) { - if(!(step instanceof QBackendStepMetaData)) + //////////////////////////////////////////////////////////////// + // Handle what to do with frontend steps, per request setting // + //////////////////////////////////////////////////////////////// + switch(runProcessRequest.getFrontendStepBehavior()) { - LOG.info("Breaking process [" + process.getName() + "] at first non-backend step (as requested by caller): " + step.getName()); - processState.setNextStepName(step.getName()); - break; + case BREAK -> + { + LOG.info("Breaking process [" + process.getName() + "] at frontend step (as requested by caller): " + step.getName()); + processState.setNextStepName(step.getName()); + break STEP_LOOP; + } + case SKIP -> + { + LOG.info("Skipping frontend step [" + step.getName() + "] in process [" + process.getName() + "] (as requested by caller)"); + processState.setNextStepName(step.getName()); + + ////////////////////////////////////////////////////////////////////// + // much less error prone in case this code changes in the future... // + ////////////////////////////////////////////////////////////////////// + // noinspection UnnecessaryContinue + continue; + } + case FAIL -> + { + LOG.info("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: " + runProcessRequest.getFrontendStepBehavior()); } } - - ///////////////////////////////////// - // run the step, based on its type // - ///////////////////////////////////// - if(step instanceof QBackendStepMetaData backendStepMetaData) + else if(step instanceof QBackendStepMetaData backendStepMetaData) { + /////////////////////// + // Run backend steps // + /////////////////////// runBackendStep(runProcessRequest, process, runProcessResult, stateKey, backendStepMetaData, processState); } else { + ////////////////////////////////////////////////// + // in case we have a different step type, throw // + ////////////////////////////////////////////////// throw (new QException("Unsure how to run a step of type: " + step.getClass().getName())); } } diff --git a/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceEnricher.java b/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceEnricher.java index ac66d61e..60a6cd1d 100644 --- a/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceEnricher.java +++ b/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceEnricher.java @@ -122,9 +122,20 @@ public class QInstanceEnricher step.getInputFields().forEach(this::enrich); step.getOutputFields().forEach(this::enrich); - if (step instanceof QFrontendStepMetaData) + if (step instanceof QFrontendStepMetaData frontendStepMetaData) { - ((QFrontendStepMetaData)step).getFormFields().forEach(this::enrich); + if(frontendStepMetaData.getFormFields() != null) + { + frontendStepMetaData.getFormFields().forEach(this::enrich); + } + if(frontendStepMetaData.getViewFields() != null) + { + frontendStepMetaData.getViewFields().forEach(this::enrich); + } + if(frontendStepMetaData.getRecordListFields() != null) + { + frontendStepMetaData.getRecordListFields().forEach(this::enrich); + } } } diff --git a/src/main/java/com/kingsrook/qqq/backend/core/interfaces/mock/MockBackendStep.java b/src/main/java/com/kingsrook/qqq/backend/core/interfaces/mock/MockBackendStep.java index eb51d6fd..c0471f85 100644 --- a/src/main/java/com/kingsrook/qqq/backend/core/interfaces/mock/MockBackendStep.java +++ b/src/main/java/com/kingsrook/qqq/backend/core/interfaces/mock/MockBackendStep.java @@ -26,6 +26,8 @@ import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.interfaces.BackendStep; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepRequest; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepResult; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /******************************************************************************* @@ -35,16 +37,26 @@ import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepResu *******************************************************************************/ public class MockBackendStep implements BackendStep { + private static final Logger LOG = LogManager.getLogger(MockBackendStep.class); + public static final String FIELD_GREETING_PREFIX = "greetingPrefix"; public static final String FIELD_GREETING_SUFFIX = "greetingSuffix"; + public static final String FIELD_MOCK_VALUE = "mockValue"; + public static final String MOCK_VALUE = "You so silly"; + + @Override public void run(RunBackendStepRequest runBackendStepRequest, RunBackendStepResult runBackendStepResult) throws QException { - runBackendStepResult.getRecords().forEach(r -> r.setValue("mockValue", "Ha ha!")); + runBackendStepResult.getRecords().forEach(r -> + { + r.setValue(FIELD_MOCK_VALUE, "Ha ha!"); + LOG.info("We are mocking {}: {}", r.getValueString("firstName"), r.getValue(FIELD_MOCK_VALUE)); + }); runBackendStepResult.setValues(runBackendStepRequest.getValues()); - runBackendStepResult.addValue("mockValue", "You so silly"); + runBackendStepResult.addValue(FIELD_MOCK_VALUE, MOCK_VALUE); ///////////////////////////////// // mock the "greet" process... // diff --git a/src/main/java/com/kingsrook/qqq/backend/core/model/actions/processes/RunProcessRequest.java b/src/main/java/com/kingsrook/qqq/backend/core/model/actions/processes/RunProcessRequest.java index c88abd07..69c0059a 100644 --- a/src/main/java/com/kingsrook/qqq/backend/core/model/actions/processes/RunProcessRequest.java +++ b/src/main/java/com/kingsrook/qqq/backend/core/model/actions/processes/RunProcessRequest.java @@ -39,13 +39,25 @@ import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData; *******************************************************************************/ public class RunProcessRequest extends AbstractQRequest { - private String processName; - private QProcessCallback callback; - private ProcessState processState; - private boolean backendOnly = false; - private String startAfterStep; - private String processUUID; - private AsyncJobCallback asyncJobCallback; + private String processName; + private QProcessCallback callback; + private ProcessState processState; + private FrontendStepBehavior frontendStepBehavior = FrontendStepBehavior.BREAK; + private String startAfterStep; + private String processUUID; + private AsyncJobCallback asyncJobCallback; + + + + /******************************************************************************* + ** + *******************************************************************************/ + public enum FrontendStepBehavior + { + BREAK, + SKIP, + FAIL + } @@ -285,21 +297,23 @@ public class RunProcessRequest extends AbstractQRequest /******************************************************************************* + ** Getter for frontendStepBehavior ** *******************************************************************************/ - public void setBackendOnly(boolean backendOnly) + public FrontendStepBehavior getFrontendStepBehavior() { - this.backendOnly = backendOnly; + return frontendStepBehavior; } /******************************************************************************* + ** Setter for frontendStepBehavior ** *******************************************************************************/ - public boolean getBackendOnly() + public void setFrontendStepBehavior(FrontendStepBehavior frontendStepBehavior) { - return backendOnly; + this.frontendStepBehavior = frontendStepBehavior; } diff --git a/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/processes/QFrontendStepMetaData.java b/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/processes/QFrontendStepMetaData.java index 4f19e051..5e5bff1b 100644 --- a/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/processes/QFrontendStepMetaData.java +++ b/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/processes/QFrontendStepMetaData.java @@ -35,6 +35,8 @@ import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData; public class QFrontendStepMetaData extends QStepMetaData { private List formFields; + private List viewFields; + private List recordListFields; @@ -88,6 +90,106 @@ public class QFrontendStepMetaData extends QStepMetaData + /******************************************************************************* + ** Getter for viewFields + ** + *******************************************************************************/ + public List getViewFields() + { + return viewFields; + } + + + + /******************************************************************************* + ** Setter for viewFields + ** + *******************************************************************************/ + public void setViewFields(List viewFields) + { + this.viewFields = viewFields; + } + + + + /******************************************************************************* + ** fluent setter to add a single view field + ** + *******************************************************************************/ + public QFrontendStepMetaData withViewField(QFieldMetaData viewField) + { + if(this.viewFields == null) + { + this.viewFields = new ArrayList<>(); + } + this.viewFields.add(viewField); + return (this); + } + + + + /******************************************************************************* + ** fluent setter for viewFields + ** + *******************************************************************************/ + public QFrontendStepMetaData withViewFields(List viewFields) + { + this.viewFields = viewFields; + return (this); + } + + + + /******************************************************************************* + ** Getter for recordListFields + ** + *******************************************************************************/ + public List getRecordListFields() + { + return recordListFields; + } + + + + /******************************************************************************* + ** Setter for recordListFields + ** + *******************************************************************************/ + public void setRecordListFields(List recordListFields) + { + this.recordListFields = recordListFields; + } + + + + /******************************************************************************* + ** fluent setter to add a single recordList field + ** + *******************************************************************************/ + public QFrontendStepMetaData withRecordListField(QFieldMetaData recordListField) + { + if(this.recordListFields == null) + { + this.recordListFields = new ArrayList<>(); + } + this.recordListFields.add(recordListField); + return (this); + } + + + + /******************************************************************************* + ** fluent setter for recordListFields + ** + *******************************************************************************/ + public QFrontendStepMetaData withRecordListFields(List recordListFields) + { + this.recordListFields = recordListFields; + return (this); + } + + + /******************************************************************************* ** fluent setter for name ** diff --git a/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/LoadInitialRecordsStep.java b/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/LoadInitialRecordsStep.java new file mode 100644 index 00000000..cdcfc464 --- /dev/null +++ b/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/LoadInitialRecordsStep.java @@ -0,0 +1,75 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2022. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.kingsrook.qqq.backend.core.processes.implementations; + + +import com.kingsrook.qqq.backend.core.exceptions.QException; +import com.kingsrook.qqq.backend.core.interfaces.BackendStep; +import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepRequest; +import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepResult; +import com.kingsrook.qqq.backend.core.model.metadata.QCodeReference; +import com.kingsrook.qqq.backend.core.model.metadata.QCodeType; +import com.kingsrook.qqq.backend.core.model.metadata.QCodeUsage; +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.QRecordListMetaData; + + +/******************************************************************************* + ** Function body to take care of loading the initial records to be used by a + ** process. + ** + *******************************************************************************/ +public class LoadInitialRecordsStep implements BackendStep +{ + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public void run(RunBackendStepRequest runBackendStepRequest, RunBackendStepResult runBackendStepResult) throws QException + { + ///////////////////////////////////////////////////////////////////////////////////////////////// + // actually, this is a no-op... we Just need a backendStep to be the first step in the process // + ///////////////////////////////////////////////////////////////////////////////////////////////// + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public static QBackendStepMetaData defineMetaData(String tableName) + { + return (new QBackendStepMetaData() + .withName("loadInitialRecords") + .withCode(new QCodeReference() + .withName(LoadInitialRecordsStep.class.getName()) + .withCodeType(QCodeType.JAVA) + .withCodeUsage(QCodeUsage.BACKEND_STEP)) + .withInputData(new QFunctionInputMetaData() + .withRecordListMetaData(new QRecordListMetaData() + .withTableName(tableName)))); + + } + +} diff --git a/src/test/java/com/kingsrook/qqq/backend/core/actions/RunProcessTest.java b/src/test/java/com/kingsrook/qqq/backend/core/actions/RunProcessTest.java index db3e1cb8..c20907ed 100644 --- a/src/test/java/com/kingsrook/qqq/backend/core/actions/RunProcessTest.java +++ b/src/test/java/com/kingsrook/qqq/backend/core/actions/RunProcessTest.java @@ -29,6 +29,7 @@ import java.util.Map; import java.util.Optional; import com.kingsrook.qqq.backend.core.callbacks.QProcessCallback; import com.kingsrook.qqq.backend.core.exceptions.QException; +import com.kingsrook.qqq.backend.core.interfaces.mock.MockBackendStep; import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessRequest; import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessResult; import com.kingsrook.qqq.backend.core.model.actions.query.QQueryFilter; @@ -37,10 +38,12 @@ import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData; import com.kingsrook.qqq.backend.core.utils.TestUtils; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; /******************************************************************************* @@ -75,7 +78,7 @@ public class RunProcessTest ** *******************************************************************************/ @Test - public void testBackendOnly() throws QException + public void testBreakOnFrontendSteps() throws QException { TestCallback callback = new TestCallback(); QInstance instance = TestUtils.defineInstance(); @@ -84,7 +87,7 @@ public class RunProcessTest request.setSession(TestUtils.getMockSession()); request.setProcessName(processName); - request.setBackendOnly(true); + request.setFrontendStepBehavior(RunProcessRequest.FrontendStepBehavior.BREAK); request.setCallback(callback); RunProcessResult result0 = new RunProcessAction().execute(request); @@ -114,6 +117,58 @@ public class RunProcessTest + /******************************************************************************* + ** + *******************************************************************************/ + @Test + public void testSkipFrontendSteps() throws QException + { + TestCallback callback = new TestCallback(); + QInstance instance = TestUtils.defineInstance(); + RunProcessRequest request = new RunProcessRequest(instance); + String processName = TestUtils.PROCESS_NAME_GREET_PEOPLE_INTERACTIVE; + + request.setSession(TestUtils.getMockSession()); + request.setProcessName(processName); + request.setFrontendStepBehavior(RunProcessRequest.FrontendStepBehavior.SKIP); + request.setCallback(callback); + + RunProcessResult runProcessResult = new RunProcessAction().execute(request); + assertTrue(runProcessResult.getException().isEmpty()); + assertEquals(MockBackendStep.MOCK_VALUE, runProcessResult.getValues().get(MockBackendStep.FIELD_MOCK_VALUE)); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + public void testFailOnFrontendSteps() + { + TestCallback callback = new TestCallback(); + QInstance instance = TestUtils.defineInstance(); + RunProcessRequest request = new RunProcessRequest(instance); + String processName = TestUtils.PROCESS_NAME_GREET_PEOPLE_INTERACTIVE; + + request.setSession(TestUtils.getMockSession()); + request.setProcessName(processName); + request.setFrontendStepBehavior(RunProcessRequest.FrontendStepBehavior.FAIL); + request.setCallback(callback); + + try + { + new RunProcessAction().execute(request); + fail("This should have thrown..."); + } + catch(Exception e) + { + assertTrue(e.getMessage().contains("fail on frontend steps")); + } + } + + + /******************************************************************************* ** *******************************************************************************/