QQQ-21 starting to refactor process - 'steps' instead of 'functions'; more process meta-data

This commit is contained in:
2022-07-06 13:42:21 -05:00
parent dbaecfd879
commit c43f7a1edd
29 changed files with 923 additions and 335 deletions

View File

@ -59,7 +59,7 @@ public class MetaDataAction
Map<String, QFrontendProcessMetaData> processes = new LinkedHashMap<>(); Map<String, QFrontendProcessMetaData> processes = new LinkedHashMap<>();
for(Map.Entry<String, QProcessMetaData> entry : metaDataRequest.getInstance().getProcesses().entrySet()) for(Map.Entry<String, QProcessMetaData> entry : metaDataRequest.getInstance().getProcesses().entrySet())
{ {
processes.put(entry.getKey(), new QFrontendProcessMetaData(entry.getValue())); processes.put(entry.getKey(), new QFrontendProcessMetaData(entry.getValue(), false));
} }
metaDataResult.setProcesses(processes); metaDataResult.setProcesses(processes);

View File

@ -0,0 +1,60 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.actions;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException;
import com.kingsrook.qqq.backend.core.model.actions.metadata.process.ProcessMetaDataRequest;
import com.kingsrook.qqq.backend.core.model.actions.metadata.process.ProcessMetaDataResult;
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendProcessMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
/*******************************************************************************
** Action to fetch meta-data for a process.
**
*******************************************************************************/
public class ProcessMetaDataAction
{
/*******************************************************************************
**
*******************************************************************************/
public ProcessMetaDataResult execute(ProcessMetaDataRequest processMetaDataRequest) throws QException
{
ActionHelper.validateSession(processMetaDataRequest);
// todo pre-customization - just get to modify the request?
ProcessMetaDataResult processMetaDataResult = new ProcessMetaDataResult();
QProcessMetaData process = processMetaDataRequest.getInstance().getProcess(processMetaDataRequest.getProcessName());
if(process == null)
{
throw (new QNotFoundException("Process [" + processMetaDataRequest.getProcessName() + "] was not found."));
}
processMetaDataResult.setProcess(new QFrontendProcessMetaData(process, true));
// todo post-customization - can do whatever w/ the result if you want
return processMetaDataResult;
}
}

View File

@ -28,15 +28,16 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import com.kingsrook.qqq.backend.core.callbacks.QProcessCallback; import com.kingsrook.qqq.backend.core.callbacks.QProcessCallback;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.interfaces.FunctionBody; import com.kingsrook.qqq.backend.core.interfaces.BackendStep;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionRequest; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepRequest;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionResult; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepResult;
import com.kingsrook.qqq.backend.core.model.actions.query.QueryRequest; import com.kingsrook.qqq.backend.core.model.actions.query.QueryRequest;
import com.kingsrook.qqq.backend.core.model.actions.query.QueryResult; import com.kingsrook.qqq.backend.core.model.actions.query.QueryResult;
import com.kingsrook.qqq.backend.core.model.metadata.QCodeReference; import com.kingsrook.qqq.backend.core.model.metadata.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData; import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData;
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;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
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.utils.CollectionUtils; import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
@ -44,54 +45,61 @@ import org.apache.logging.log4j.Logger;
/******************************************************************************* /*******************************************************************************
** Action handler for running q-functions. ** Action handler for running backend steps as part of processes.
* *
*******************************************************************************/ *******************************************************************************/
public class RunFunctionAction public class RunBackendStepAction
{ {
private static final Logger LOG = LogManager.getLogger(RunFunctionAction.class); private static final Logger LOG = LogManager.getLogger(RunBackendStepAction.class);
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public RunFunctionResult execute(RunFunctionRequest runFunctionRequest) throws QException public RunBackendStepResult execute(RunBackendStepRequest runBackendStepRequest) throws QException
{ {
ActionHelper.validateSession(runFunctionRequest); ActionHelper.validateSession(runBackendStepRequest);
QProcessMetaData process = runFunctionRequest.getInstance().getProcess(runFunctionRequest.getProcessName()); QProcessMetaData process = runBackendStepRequest.getInstance().getProcess(runBackendStepRequest.getProcessName());
if(process == null) if(process == null)
{ {
throw new QException("Process [" + runFunctionRequest.getProcessName() + "] is not defined in this instance."); throw new QException("Process [" + runBackendStepRequest.getProcessName() + "] is not defined in this instance.");
} }
QFunctionMetaData function = process.getFunction(runFunctionRequest.getFunctionName()); QStepMetaData stepMetaData = process.getStep(runBackendStepRequest.getStepName());
if(function == null) if(stepMetaData == null)
{ {
throw new QException("Function [" + runFunctionRequest.getFunctionName() + "] is not defined in the process [" + process.getName() + "]"); throw new QException("Step [" + runBackendStepRequest.getStepName() + "] is not defined in the process [" + process.getName() + "]");
}
if(!(stepMetaData instanceof QBackendStepMetaData backendStepMetaData))
{
throw new QException("Step [" + runBackendStepRequest.getStepName() + "] is not a backend step.");
} }
////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////
// ensure input data is set as needed - use callback object to get anything missing // // ensure input data is set as needed - use callback object to get anything missing //
////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////
ensureRecordsAreInRequest(runFunctionRequest, function); ensureRecordsAreInRequest(runBackendStepRequest, backendStepMetaData);
ensureInputFieldsAreInRequest(runFunctionRequest, function); ensureInputFieldsAreInRequest(runBackendStepRequest, backendStepMetaData);
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// load and run the user-defined code that actually does the work // // load and run the user-defined code that actually does the work //
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
return (runFunctionBodyCode(function.getCode(), runFunctionRequest)); return (runStepCode(backendStepMetaData.getCode(), runBackendStepRequest));
} }
/******************************************************************************* /*******************************************************************************
** check if this function needs any input fields - and if so, if we need to get one ** check if this step needs any input fields - and if so, if we need to get one
** via the callback ** via the callback
** **
*******************************************************************************/ *******************************************************************************/
private void ensureInputFieldsAreInRequest(RunFunctionRequest runFunctionRequest, QFunctionMetaData function) throws QException private void ensureInputFieldsAreInRequest(RunBackendStepRequest runBackendStepRequest, QBackendStepMetaData step) throws QException
{ {
QFunctionInputMetaData inputMetaData = function.getInputMetaData(); QFunctionInputMetaData inputMetaData = step.getInputMetaData();
if(inputMetaData == null) if(inputMetaData == null)
{ {
return; return;
@ -100,12 +108,12 @@ public class RunFunctionAction
List<QFieldMetaData> fieldsToGet = new ArrayList<>(); List<QFieldMetaData> fieldsToGet = new ArrayList<>();
for(QFieldMetaData field : inputMetaData.getFieldList()) for(QFieldMetaData field : inputMetaData.getFieldList())
{ {
Serializable value = runFunctionRequest.getValue(field.getName()); Serializable value = runBackendStepRequest.getValue(field.getName());
if(value == null) if(value == null)
{ {
if(field.getDefaultValue() != null) if(field.getDefaultValue() != null)
{ {
runFunctionRequest.addValue(field.getName(), field.getDefaultValue()); runBackendStepRequest.addValue(field.getName(), field.getDefaultValue());
} }
else else
{ {
@ -117,7 +125,7 @@ public class RunFunctionAction
if(!fieldsToGet.isEmpty()) if(!fieldsToGet.isEmpty())
{ {
QProcessCallback callback = runFunctionRequest.getCallback(); QProcessCallback callback = runBackendStepRequest.getCallback();
if(callback == null) if(callback == null)
{ {
throw (new QException("Function is missing values for fields, but no callback was present to request fields from a user")); throw (new QException("Function is missing values for fields, but no callback was present to request fields from a user"));
@ -126,7 +134,7 @@ public class RunFunctionAction
Map<String, Serializable> fieldValues = callback.getFieldValues(fieldsToGet); Map<String, Serializable> fieldValues = callback.getFieldValues(fieldsToGet);
for(Map.Entry<String, Serializable> entry : fieldValues.entrySet()) for(Map.Entry<String, Serializable> entry : fieldValues.entrySet())
{ {
runFunctionRequest.addValue(entry.getKey(), entry.getValue()); runBackendStepRequest.addValue(entry.getKey(), entry.getValue());
// todo - check to make sure got values back? // todo - check to make sure got values back?
} }
} }
@ -135,25 +143,25 @@ public class RunFunctionAction
/******************************************************************************* /*******************************************************************************
** check if this function uses a record list - and if so, if we need to get one ** check if this step uses a record list - and if so, if we need to get one
** via the callback ** via the callback
*******************************************************************************/ *******************************************************************************/
private void ensureRecordsAreInRequest(RunFunctionRequest runFunctionRequest, QFunctionMetaData function) throws QException private void ensureRecordsAreInRequest(RunBackendStepRequest runBackendStepRequest, QBackendStepMetaData step) throws QException
{ {
QFunctionInputMetaData inputMetaData = function.getInputMetaData(); QFunctionInputMetaData inputMetaData = step.getInputMetaData();
if(inputMetaData != null && inputMetaData.getRecordListMetaData() != null) if(inputMetaData != null && inputMetaData.getRecordListMetaData() != null)
{ {
if(CollectionUtils.nullSafeIsEmpty(runFunctionRequest.getRecords())) if(CollectionUtils.nullSafeIsEmpty(runBackendStepRequest.getRecords()))
{ {
QueryRequest queryRequest = new QueryRequest(runFunctionRequest.getInstance()); QueryRequest queryRequest = new QueryRequest(runBackendStepRequest.getInstance());
queryRequest.setSession(runFunctionRequest.getSession()); queryRequest.setSession(runBackendStepRequest.getSession());
queryRequest.setTableName(inputMetaData.getRecordListMetaData().getTableName()); queryRequest.setTableName(inputMetaData.getRecordListMetaData().getTableName());
// todo - handle this being async (e.g., http) // todo - handle this being async (e.g., http)
// seems like it just needs to throw, breaking this flow, and to send a response to the frontend, directing it to prompt the user for the needed data // seems like it just needs to throw, breaking this flow, and to send a response to the frontend, directing it to prompt the user for the needed data
// then this function can re-run, hopefully with the needed data. // then this step can re-run, hopefully with the needed data.
QProcessCallback callback = runFunctionRequest.getCallback(); QProcessCallback callback = runBackendStepRequest.getCallback();
if(callback == null) if(callback == null)
{ {
throw (new QException("Function is missing input records, but no callback was present to get a query filter from a user")); throw (new QException("Function is missing input records, but no callback was present to get a query filter from a user"));
@ -162,7 +170,7 @@ public class RunFunctionAction
queryRequest.setFilter(callback.getQueryFilter()); queryRequest.setFilter(callback.getQueryFilter());
QueryResult queryResult = new QueryAction().execute(queryRequest); QueryResult queryResult = new QueryAction().execute(queryRequest);
runFunctionRequest.setRecords(queryResult.getRecords()); runBackendStepRequest.setRecords(queryResult.getRecords());
// todo - handle 0 results found? // todo - handle 0 results found?
} }
} }
@ -173,29 +181,29 @@ public class RunFunctionAction
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
private RunFunctionResult runFunctionBodyCode(QCodeReference code, RunFunctionRequest runFunctionRequest) private RunBackendStepResult runStepCode(QCodeReference code, RunBackendStepRequest runBackendStepRequest)
{ {
RunFunctionResult runFunctionResult = new RunFunctionResult(); RunBackendStepResult runBackendStepResult = new RunBackendStepResult();
try try
{ {
runFunctionResult.seedFromRequest(runFunctionRequest); runBackendStepResult.seedFromRequest(runBackendStepRequest);
Class<?> codeClass = Class.forName(code.getName()); Class<?> codeClass = Class.forName(code.getName());
Object codeObject = codeClass.getConstructor().newInstance(); Object codeObject = codeClass.getConstructor().newInstance();
if(!(codeObject instanceof FunctionBody functionBodyCodeObject)) if(!(codeObject instanceof BackendStep backendStepCodeObject))
{ {
throw (new QException("The supplied code [" + codeClass.getName() + "] is not an instance of FunctionBody")); throw (new QException("The supplied code [" + codeClass.getName() + "] is not an instance of FunctionBody"));
} }
functionBodyCodeObject.run(runFunctionRequest, runFunctionResult); backendStepCodeObject.run(runBackendStepRequest, runBackendStepResult);
} }
catch(Exception e) catch(Exception e)
{ {
runFunctionResult = new RunFunctionResult(); runBackendStepResult = new RunBackendStepResult();
runFunctionResult.setError("Error running function code: " + e.getMessage()); runBackendStepResult.setError("Error running backend step code: " + e.getMessage());
LOG.info("Error running function code", e); LOG.info("Error running backend step code", e);
} }
return (runFunctionResult); return (runBackendStepResult);
} }
} }

View File

@ -26,12 +26,12 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
import com.kingsrook.qqq.backend.core.exceptions.QException; 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.ProcessState;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionRequest; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepRequest;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionResult; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepResult;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessRequest; 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.processes.RunProcessResult;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionMetaData;
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.state.InMemoryStateProvider; import com.kingsrook.qqq.backend.core.state.InMemoryStateProvider;
import com.kingsrook.qqq.backend.core.state.StateProviderInterface; import com.kingsrook.qqq.backend.core.state.StateProviderInterface;
import com.kingsrook.qqq.backend.core.state.UUIDStateKey; import com.kingsrook.qqq.backend.core.state.UUIDStateKey;
@ -59,35 +59,35 @@ public class RunProcessAction
RunProcessResult runProcessResult = new RunProcessResult(); RunProcessResult runProcessResult = new RunProcessResult();
UUIDStateKey stateKey = new UUIDStateKey(); UUIDStateKey stateKey = new UUIDStateKey();
RunFunctionResult lastFunctionResult = null; RunBackendStepResult lastFunctionResult = null;
// todo - custom routing? // todo - custom routing?
List<QFunctionMetaData> functionList = process.getFunctionList(); List<QStepMetaData> functionList = process.getStepList();
for(QFunctionMetaData function : functionList) for(QStepMetaData function : functionList)
{ {
RunFunctionRequest runFunctionRequest = new RunFunctionRequest(runProcessRequest.getInstance()); RunBackendStepRequest runBackendStepRequest = new RunBackendStepRequest(runProcessRequest.getInstance());
if(lastFunctionResult == null) if(lastFunctionResult == null)
{ {
/////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////
// for the first request, load state from the run process request to prime the run function request. // // for the first request, load state from the run process request to prime the run function request. //
/////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////
primeFunction(runProcessRequest, runFunctionRequest); primeFunction(runProcessRequest, runBackendStepRequest);
} }
else else
{ {
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// for functions after the first one, load from state management to prime the request // // for functions after the first one, load from state management to prime the request //
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
loadState(stateKey, runFunctionRequest); loadState(stateKey, runBackendStepRequest);
} }
runFunctionRequest.setProcessName(process.getName()); runBackendStepRequest.setProcessName(process.getName());
runFunctionRequest.setFunctionName(function.getName()); runBackendStepRequest.setStepName(function.getName());
runFunctionRequest.setSession(runProcessRequest.getSession()); runBackendStepRequest.setSession(runProcessRequest.getSession());
runFunctionRequest.setCallback(runProcessRequest.getCallback()); runBackendStepRequest.setCallback(runProcessRequest.getCallback());
lastFunctionResult = new RunFunctionAction().execute(runFunctionRequest); lastFunctionResult = new RunBackendStepAction().execute(runBackendStepRequest);
if(lastFunctionResult.getError() != null) if(lastFunctionResult.getError() != null)
{ {
runProcessResult.setError(lastFunctionResult.getError()); runProcessResult.setError(lastFunctionResult.getError());
@ -116,7 +116,7 @@ public class RunProcessAction
// TODO - read this from somewhere in meta data eh? // TODO - read this from somewhere in meta data eh?
return InMemoryStateProvider.getInstance(); return InMemoryStateProvider.getInstance();
// TODO - by using JSON serialization internally, this makes stupidly large payloads and crashes things. // todo - by using JSON serialization internally, this makes stupidly large payloads and crashes things.
// return TempFileStateProvider.getInstance(); // return TempFileStateProvider.getInstance();
} }
@ -126,9 +126,9 @@ public class RunProcessAction
** Store the process state from a function result to the state provider ** Store the process state from a function result to the state provider
** **
*******************************************************************************/ *******************************************************************************/
private void storeState(UUIDStateKey stateKey, RunFunctionResult runFunctionResult) private void storeState(UUIDStateKey stateKey, RunBackendStepResult runBackendStepResult)
{ {
getStateProvider().put(stateKey, runFunctionResult.getProcessState()); getStateProvider().put(stateKey, runBackendStepResult.getProcessState());
} }
@ -137,9 +137,9 @@ public class RunProcessAction
** Copy data (the state) down from the run-process request, down into the run- ** Copy data (the state) down from the run-process request, down into the run-
** function request. ** function request.
*******************************************************************************/ *******************************************************************************/
private void primeFunction(RunProcessRequest runProcessRequest, RunFunctionRequest runFunctionRequest) private void primeFunction(RunProcessRequest runProcessRequest, RunBackendStepRequest runBackendStepRequest)
{ {
runFunctionRequest.seedFromRunProcessRequest(runProcessRequest); runBackendStepRequest.seedFromRunProcessRequest(runProcessRequest);
} }
@ -148,10 +148,10 @@ public class RunProcessAction
** Load the process state into a function request from the state provider ** Load the process state into a function request from the state provider
** **
*******************************************************************************/ *******************************************************************************/
private void loadState(UUIDStateKey stateKey, RunFunctionRequest runFunctionRequest) throws QException private void loadState(UUIDStateKey stateKey, RunBackendStepRequest runBackendStepRequest) throws QException
{ {
Optional<ProcessState> processState = getStateProvider().get(ProcessState.class, stateKey); Optional<ProcessState> processState = getStateProvider().get(ProcessState.class, stateKey);
runFunctionRequest.seedFromProcessState(processState runBackendStepRequest.seedFromProcessState(processState
.orElseThrow(() -> new QException("Could not find process state in state provider."))); .orElseThrow(() -> new QException("Could not find process state in state provider.")));
} }

View File

@ -23,7 +23,7 @@ package com.kingsrook.qqq.backend.core.actions;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException; import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException;
import com.kingsrook.qqq.backend.core.model.actions.metadata.table.TableMetaDataRequest; import com.kingsrook.qqq.backend.core.model.actions.metadata.table.TableMetaDataRequest;
import com.kingsrook.qqq.backend.core.model.actions.metadata.table.TableMetaDataResult; import com.kingsrook.qqq.backend.core.model.actions.metadata.table.TableMetaDataResult;
import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData; import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData;
@ -49,7 +49,7 @@ public class TableMetaDataAction
QTableMetaData table = tableMetaDataRequest.getInstance().getTable(tableMetaDataRequest.getTableName()); QTableMetaData table = tableMetaDataRequest.getInstance().getTable(tableMetaDataRequest.getTableName());
if(table == null) if(table == null)
{ {
throw (new QUserFacingException("Table [" + tableMetaDataRequest.getTableName() + "] was not found.")); throw (new QNotFoundException("Table [" + tableMetaDataRequest.getTableName() + "] was not found."));
} }
tableMetaDataResult.setTable(new QFrontendTableMetaData(table, true)); tableMetaDataResult.setTable(new QFrontendTableMetaData(table, true));

View File

@ -0,0 +1,54 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.exceptions;
/*******************************************************************************
** User-facing exception for when something wasn't found (e.g., a named table or
** record-by-id).
**
*******************************************************************************/
public class QNotFoundException extends QUserFacingException
{
/*******************************************************************************
** Constructor of message
**
*******************************************************************************/
public QNotFoundException(String message)
{
super(message);
}
/*******************************************************************************
** Constructor of message & cause
**
*******************************************************************************/
public QNotFoundException(String message, Throwable cause)
{
super(message, cause);
}
}

View File

@ -27,7 +27,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData; import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData;
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.QTableMetaData; import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
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.utils.StringUtils; import com.kingsrook.qqq.backend.core.utils.StringUtils;
@ -100,9 +100,9 @@ public class QInstanceEnricher
process.setLabel(nameToLabel(process.getName())); process.setLabel(nameToLabel(process.getName()));
} }
if(process.getFunctionList() != null) if(process.getStepList() != null)
{ {
process.getFunctionList().forEach(this::enrich); process.getStepList().forEach(this::enrich);
} }
} }
@ -111,7 +111,7 @@ public class QInstanceEnricher
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
private void enrich(QFunctionMetaData function) private void enrich(QStepMetaData function)
{ {
if(!StringUtils.hasContent(function.getLabel())) if(!StringUtils.hasContent(function.getLabel()))
{ {

View File

@ -23,22 +23,22 @@ package com.kingsrook.qqq.backend.core.interfaces;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionRequest; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepRequest;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionResult; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepResult;
/******************************************************************************* /*******************************************************************************
** Simple interface that a "custom" function (as in, a component of a Process) ** Simple interface that Backend Steps (e.g., code within processes) must implement
** must implement. **
*******************************************************************************/ *******************************************************************************/
public interface FunctionBody public interface BackendStep
{ {
/******************************************************************************* /*******************************************************************************
** Execute the function - using the request as input, and the result as output. ** Execute the backend step - using the request as input, and the result as output.
** **
** TODO - think about - why take the Result object as a param, instead of return it? ** TODO - think about - why take the Result object as a param, instead of return it?
** Is this way easier for inter-language operability maybe? ** Is this way easier for inter-language operability maybe?
* Also - there's way too much "process-specific gunk" in the Request object - can we simplify it? * Also - there's way too much "process-specific gunk" in the Request object - can we simplify it?
*******************************************************************************/ *******************************************************************************/
void run(RunFunctionRequest runFunctionRequest, RunFunctionResult runFunctionResult) throws QException; void run(RunBackendStepRequest runBackendStepRequest, RunBackendStepResult runBackendStepResult) throws QException;
} }

View File

@ -22,9 +22,9 @@
package com.kingsrook.qqq.backend.core.interfaces.mock; package com.kingsrook.qqq.backend.core.interfaces.mock;
import com.kingsrook.qqq.backend.core.interfaces.FunctionBody; import com.kingsrook.qqq.backend.core.interfaces.BackendStep;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionRequest; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepRequest;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionResult; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepResult;
/******************************************************************************* /*******************************************************************************
@ -32,19 +32,22 @@ import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionResult;
** **
** Basically just passes data from the request to the response. ** Basically just passes data from the request to the response.
*******************************************************************************/ *******************************************************************************/
public class MockFunctionBody implements FunctionBody public class MockBackendStep implements BackendStep
{ {
@Override public final static String FIELD_GREETING_PREFIX = "greetingPrefix";
public void run(RunFunctionRequest runFunctionRequest, RunFunctionResult runFunctionResult) public final static String FIELD_GREETING_SUFFIX = "greetingSuffix";
{
runFunctionResult.getRecords().forEach(r -> r.setValue("mockValue", "Ha ha!"));
runFunctionResult.setValues(runFunctionRequest.getValues()); @Override
runFunctionResult.addValue("mockValue", "You so silly"); public void run(RunBackendStepRequest runBackendStepRequest, RunBackendStepResult runBackendStepResult)
{
runBackendStepResult.getRecords().forEach(r -> r.setValue("mockValue", "Ha ha!"));
runBackendStepResult.setValues(runBackendStepRequest.getValues());
runBackendStepResult.addValue("mockValue", "You so silly");
///////////////////////////////// /////////////////////////////////
// mock the "greet" process... // // mock the "greet" process... //
///////////////////////////////// /////////////////////////////////
runFunctionResult.addValue("outputMessage", runFunctionRequest.getValueString("greetingPrefix") + " X " + runFunctionRequest.getValueString("greetingSuffix")); runBackendStepResult.addValue("outputMessage", runBackendStepRequest.getValueString(FIELD_GREETING_PREFIX) + " X " + runBackendStepRequest.getValueString(FIELD_GREETING_SUFFIX));
} }
} }

View File

@ -0,0 +1,77 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.actions.metadata.process;
import com.kingsrook.qqq.backend.core.model.actions.AbstractQRequest;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
/*******************************************************************************
** Request for meta-data for a process.
**
*******************************************************************************/
public class ProcessMetaDataRequest extends AbstractQRequest
{
private String processName;
/*******************************************************************************
**
*******************************************************************************/
public ProcessMetaDataRequest()
{
}
/*******************************************************************************
**
*******************************************************************************/
public ProcessMetaDataRequest(QInstance instance)
{
super(instance);
}
/*******************************************************************************
** Getter for processName
**
*******************************************************************************/
public String getProcessName()
{
return processName;
}
/*******************************************************************************
** Setter for processName
**
*******************************************************************************/
public void setProcessName(String processName)
{
this.processName = processName;
}
}

View File

@ -0,0 +1,58 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.actions.metadata.process;
import com.kingsrook.qqq.backend.core.model.actions.AbstractQResult;
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendProcessMetaData;
/*******************************************************************************
* Result for a process-metaData action
*
*******************************************************************************/
public class ProcessMetaDataResult extends AbstractQResult
{
QFrontendProcessMetaData process;
/*******************************************************************************
** Getter for process
**
*******************************************************************************/
public QFrontendProcessMetaData getProcess()
{
return process;
}
/*******************************************************************************
** Setter for process
**
*******************************************************************************/
public void setProcess(QFrontendProcessMetaData process)
{
this.process = process;
}
}

View File

@ -29,18 +29,18 @@ import com.kingsrook.qqq.backend.core.callbacks.QProcessCallback;
import com.kingsrook.qqq.backend.core.model.actions.AbstractQRequest; import com.kingsrook.qqq.backend.core.model.actions.AbstractQRequest;
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.QInstance; import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
/******************************************************************************* /*******************************************************************************
** Request data container for the RunFunction action ** Request data container for the RunBackendStep action
** **
*******************************************************************************/ *******************************************************************************/
public class RunFunctionRequest extends AbstractQRequest public class RunBackendStepRequest extends AbstractQRequest
{ {
private ProcessState processState; private ProcessState processState;
private String processName; private String processName;
private String functionName; private String stepName;
private QProcessCallback callback; private QProcessCallback callback;
@ -48,7 +48,7 @@ public class RunFunctionRequest extends AbstractQRequest
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public RunFunctionRequest() public RunBackendStepRequest()
{ {
processState = new ProcessState(); processState = new ProcessState();
} }
@ -58,7 +58,7 @@ public class RunFunctionRequest extends AbstractQRequest
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public RunFunctionRequest(QInstance instance) public RunBackendStepRequest(QInstance instance)
{ {
super(instance); super(instance);
processState = new ProcessState(); processState = new ProcessState();
@ -91,9 +91,9 @@ public class RunFunctionRequest extends AbstractQRequest
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public QFunctionMetaData getFunctionMetaData() public QStepMetaData getStepMetaData()
{ {
return (instance.getFunction(getProcessName(), getFunctionName())); return (instance.getProcessStep(getProcessName(), getStepName()));
} }
@ -124,7 +124,7 @@ public class RunFunctionRequest extends AbstractQRequest
** Setter for processName ** Setter for processName
** **
*******************************************************************************/ *******************************************************************************/
public RunFunctionRequest withProcessName(String processName) public RunBackendStepRequest withProcessName(String processName)
{ {
this.processName = processName; this.processName = processName;
return (this); return (this);
@ -136,9 +136,9 @@ public class RunFunctionRequest extends AbstractQRequest
** Getter for functionName ** Getter for functionName
** **
*******************************************************************************/ *******************************************************************************/
public String getFunctionName() public String getStepName()
{ {
return functionName; return stepName;
} }
@ -147,9 +147,9 @@ public class RunFunctionRequest extends AbstractQRequest
** Setter for functionName ** Setter for functionName
** **
*******************************************************************************/ *******************************************************************************/
public void setFunctionName(String functionName) public void setStepName(String stepName)
{ {
this.functionName = functionName; this.stepName = stepName;
} }
@ -158,9 +158,9 @@ public class RunFunctionRequest extends AbstractQRequest
** Setter for functionName ** Setter for functionName
** **
*******************************************************************************/ *******************************************************************************/
public RunFunctionRequest withFunctionName(String functionName) public RunBackendStepRequest withFunctionName(String functionName)
{ {
this.functionName = functionName; this.stepName = functionName;
return (this); return (this);
} }
@ -192,7 +192,7 @@ public class RunFunctionRequest extends AbstractQRequest
** Setter for records ** Setter for records
** **
*******************************************************************************/ *******************************************************************************/
public RunFunctionRequest withRecords(List<QRecord> records) public RunBackendStepRequest withRecords(List<QRecord> records)
{ {
this.processState.setRecords(records); this.processState.setRecords(records);
return (this); return (this);
@ -226,7 +226,7 @@ public class RunFunctionRequest extends AbstractQRequest
** Setter for values ** Setter for values
** **
*******************************************************************************/ *******************************************************************************/
public RunFunctionRequest withValues(Map<String, Serializable> values) public RunBackendStepRequest withValues(Map<String, Serializable> values)
{ {
this.processState.setValues(values); this.processState.setValues(values);
return (this); return (this);
@ -238,7 +238,7 @@ public class RunFunctionRequest extends AbstractQRequest
** Setter for values ** Setter for values
** **
*******************************************************************************/ *******************************************************************************/
public RunFunctionRequest addValue(String fieldName, Serializable value) public RunBackendStepRequest addValue(String fieldName, Serializable value)
{ {
this.processState.getValues().put(fieldName, value); this.processState.getValues().put(fieldName, value);
return (this); return (this);
@ -272,7 +272,7 @@ public class RunFunctionRequest extends AbstractQRequest
** Setter for callback ** Setter for callback
** **
*******************************************************************************/ *******************************************************************************/
public RunFunctionRequest withCallback(QProcessCallback callback) public RunBackendStepRequest withCallback(QProcessCallback callback)
{ {
this.callback = callback; this.callback = callback;
return (this); return (this);

View File

@ -30,10 +30,10 @@ import com.kingsrook.qqq.backend.core.model.data.QRecord;
/******************************************************************************* /*******************************************************************************
** Result data container for the RunFunction action ** Result data container for the RunBackendStep action
** **
*******************************************************************************/ *******************************************************************************/
public class RunFunctionResult extends AbstractQResult public class RunBackendStepResult extends AbstractQResult
{ {
private ProcessState processState; private ProcessState processState;
private String error; private String error;
@ -46,7 +46,7 @@ public class RunFunctionResult extends AbstractQResult
@Override @Override
public String toString() public String toString()
{ {
return "RunFunctionResult{error='" + error return "RunBackendStepResult{error='" + error
+ ",records.size()=" + (processState == null ? null : processState.getRecords().size()) + ",records.size()=" + (processState == null ? null : processState.getRecords().size())
+ ",values=" + (processState == null ? null : processState.getValues()) + ",values=" + (processState == null ? null : processState.getValues())
+ "}"; + "}";
@ -57,7 +57,7 @@ public class RunFunctionResult extends AbstractQResult
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public RunFunctionResult() public RunBackendStepResult()
{ {
this.processState = new ProcessState(); this.processState = new ProcessState();
} }
@ -68,9 +68,9 @@ public class RunFunctionResult extends AbstractQResult
** e.g., populate the process state (records, values) in this result object. ** e.g., populate the process state (records, values) in this result object.
** **
*******************************************************************************/ *******************************************************************************/
public void seedFromRequest(RunFunctionRequest runFunctionRequest) public void seedFromRequest(RunBackendStepRequest runBackendStepRequest)
{ {
this.processState = runFunctionRequest.getProcessState(); this.processState = runBackendStepRequest.getProcessState();
} }
@ -101,7 +101,7 @@ public class RunFunctionResult extends AbstractQResult
** Setter for records ** Setter for records
** **
*******************************************************************************/ *******************************************************************************/
public RunFunctionResult withRecords(List<QRecord> records) public RunBackendStepResult withRecords(List<QRecord> records)
{ {
this.processState.setRecords(records); this.processState.setRecords(records);
return (this); return (this);
@ -135,7 +135,7 @@ public class RunFunctionResult extends AbstractQResult
** Setter for values ** Setter for values
** **
*******************************************************************************/ *******************************************************************************/
public RunFunctionResult withValues(Map<String, Serializable> values) public RunBackendStepResult withValues(Map<String, Serializable> values)
{ {
this.processState.setValues(values); this.processState.setValues(values);
return (this); return (this);
@ -147,7 +147,7 @@ public class RunFunctionResult extends AbstractQResult
** Setter for values ** Setter for values
** **
*******************************************************************************/ *******************************************************************************/
public RunFunctionResult addValue(String fieldName, Serializable value) public RunBackendStepResult addValue(String fieldName, Serializable value)
{ {
this.processState.getValues().put(fieldName, value); this.processState.getValues().put(fieldName, value);
return (this); return (this);

View File

@ -69,9 +69,9 @@ public class RunProcessResult extends AbstractQResult
** the final function result ** the final function result
** **
*******************************************************************************/ *******************************************************************************/
public void seedFromLastFunctionResult(RunFunctionResult runFunctionResult) public void seedFromLastFunctionResult(RunBackendStepResult runBackendStepResult)
{ {
this.processState = runFunctionResult.getProcessState(); this.processState = runBackendStepResult.getProcessState();
} }

View File

@ -29,7 +29,7 @@ import java.util.Map;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.kingsrook.qqq.backend.core.instances.QInstanceValidationKey; import com.kingsrook.qqq.backend.core.instances.QInstanceValidationKey;
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.processes.QFunctionMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
@ -211,7 +211,7 @@ public class QInstance
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public QFunctionMetaData getFunction(String processName, String functionName) public QStepMetaData getProcessStep(String processName, String functionName)
{ {
QProcessMetaData qProcessMetaData = this.processes.get(processName); QProcessMetaData qProcessMetaData = this.processes.get(processName);
if(qProcessMetaData == null) if(qProcessMetaData == null)
@ -219,7 +219,7 @@ public class QInstance
return (null); return (null);
} }
return (qProcessMetaData.getFunction(functionName)); return (qProcessMetaData.getStep(functionName));
} }

View File

@ -22,10 +22,14 @@
package com.kingsrook.qqq.backend.core.model.metadata.frontend; package com.kingsrook.qqq.backend.core.model.metadata.frontend;
import java.util.Map; import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonInclude.Include;
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.utils.CollectionUtils;
/******************************************************************************* /*******************************************************************************
@ -39,7 +43,8 @@ public class QFrontendProcessMetaData
private String name; private String name;
private String label; private String label;
private String tableName; private String tableName;
private Map<String, QFrontendFieldMetaData> fields;
private List<QFrontendStepMetaData> frontendSteps;
////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////
// do not add setters. take values from the source-object in the constructor!! // // do not add setters. take values from the source-object in the constructor!! //
@ -50,11 +55,26 @@ public class QFrontendProcessMetaData
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public QFrontendProcessMetaData(QProcessMetaData processMetaData) public QFrontendProcessMetaData(QProcessMetaData processMetaData, boolean includeSteps)
{ {
this.name = processMetaData.getName(); this.name = processMetaData.getName();
this.label = processMetaData.getLabel(); this.label = processMetaData.getLabel();
this.tableName = processMetaData.getTableName(); this.tableName = processMetaData.getTableName();
if(includeSteps)
{
if(CollectionUtils.nullSafeHasContents(processMetaData.getStepList()))
{
this.frontendSteps = processMetaData.getStepList().stream()
.filter(QFrontendStepMetaData.class::isInstance)
.map(QFrontendStepMetaData.class::cast)
.collect(Collectors.toList());
}
else
{
frontendSteps = new ArrayList<>();
}
}
} }
@ -93,11 +113,22 @@ public class QFrontendProcessMetaData
/******************************************************************************* /*******************************************************************************
** Getter for fields ** Getter for frontendSteps
** **
*******************************************************************************/ *******************************************************************************/
public Map<String, QFrontendFieldMetaData> getFields() public List<QFrontendStepMetaData> getFrontendSteps()
{ {
return fields; return frontendSteps;
}
/*******************************************************************************
** Setter for frontendSteps
**
*******************************************************************************/
public void setFrontendSteps(List<QFrontendStepMetaData> frontendSteps)
{
this.frontendSteps = frontendSteps;
} }
} }

View File

@ -24,67 +24,21 @@ package com.kingsrook.qqq.backend.core.model.metadata.processes;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.kingsrook.qqq.backend.core.model.metadata.QCodeReference; import com.kingsrook.qqq.backend.core.model.metadata.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData; import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData;
/******************************************************************************* /*******************************************************************************
** Meta-Data to define a function in a QQQ instance. ** Meta-Data to define a backend-step in a process in a QQQ instance. e.g.,
** code that runs on a server/backend, to do something to some data.
** **
*******************************************************************************/ *******************************************************************************/
public class QFunctionMetaData public class QBackendStepMetaData extends QStepMetaData
{ {
private String name;
private String label;
private QFunctionInputMetaData inputMetaData; private QFunctionInputMetaData inputMetaData;
private QFunctionOutputMetaData outputMetaData; private QFunctionOutputMetaData outputMetaData;
private QCodeReference code; private QCodeReference code;
private QOutputView outputView;
/*******************************************************************************
** Getter for name
**
*******************************************************************************/
public String getName()
{
return name;
}
/*******************************************************************************
** Setter for name
**
*******************************************************************************/
public void setName(String name)
{
this.name = name;
}
/*******************************************************************************
** Setter for name
**
*******************************************************************************/
public QFunctionMetaData withName(String name)
{
this.name = name;
return (this);
}
/*******************************************************************************
** Getter for label
**
*******************************************************************************/
public String getLabel()
{
return label;
}
@ -92,20 +46,10 @@ public class QFunctionMetaData
** Setter for label ** Setter for label
** **
*******************************************************************************/ *******************************************************************************/
public void setLabel(String label) @Override
public QBackendStepMetaData withName(String name)
{ {
this.label = label; setName(name);
}
/*******************************************************************************
** Setter for label
**
*******************************************************************************/
public QFunctionMetaData withLabel(String label)
{
this.label = label;
return (this); return (this);
} }
@ -137,7 +81,7 @@ public class QFunctionMetaData
** Setter for inputData ** Setter for inputData
** **
*******************************************************************************/ *******************************************************************************/
public QFunctionMetaData withInputData(QFunctionInputMetaData inputData) public QBackendStepMetaData withInputData(QFunctionInputMetaData inputData)
{ {
this.inputMetaData = inputData; this.inputMetaData = inputData;
return (this); return (this);
@ -171,7 +115,7 @@ public class QFunctionMetaData
** Setter for outputData ** Setter for outputData
** **
*******************************************************************************/ *******************************************************************************/
public QFunctionMetaData withOutputMetaData(QFunctionOutputMetaData outputMetaData) public QBackendStepMetaData withOutputMetaData(QFunctionOutputMetaData outputMetaData)
{ {
this.outputMetaData = outputMetaData; this.outputMetaData = outputMetaData;
return (this); return (this);
@ -205,7 +149,7 @@ public class QFunctionMetaData
** Setter for code ** Setter for code
** **
*******************************************************************************/ *******************************************************************************/
public QFunctionMetaData withCode(QCodeReference code) public QBackendStepMetaData withCode(QCodeReference code)
{ {
this.code = code; this.code = code;
return (this); return (this);
@ -213,43 +157,11 @@ public class QFunctionMetaData
/*******************************************************************************
** Getter for outputView
**
*******************************************************************************/
public QOutputView getOutputView()
{
return outputView;
}
/*******************************************************************************
** Setter for outputView
**
*******************************************************************************/
public void setOutputView(QOutputView outputView)
{
this.outputView = outputView;
}
/*******************************************************************************
** Setter for outputView
**
*******************************************************************************/
public QFunctionMetaData withOutputView(QOutputView outputView)
{
this.outputView = outputView;
return (this);
}
/******************************************************************************* /*******************************************************************************
** Get a list of all of the input fields used by this function ** Get a list of all of the input fields used by this function
*******************************************************************************/ *******************************************************************************/
@JsonIgnore
@Override
public List<QFieldMetaData> getInputFields() public List<QFieldMetaData> getInputFields()
{ {
List<QFieldMetaData> rs = new ArrayList<>(); List<QFieldMetaData> rs = new ArrayList<>();
@ -265,6 +177,8 @@ public class QFunctionMetaData
/******************************************************************************* /*******************************************************************************
** Get a list of all of the output fields used by this function ** Get a list of all of the output fields used by this function
*******************************************************************************/ *******************************************************************************/
@JsonIgnore
@Override
public List<QFieldMetaData> getOutputFields() public List<QFieldMetaData> getOutputFields()
{ {
List<QFieldMetaData> rs = new ArrayList<>(); List<QFieldMetaData> rs = new ArrayList<>();

View File

@ -0,0 +1,115 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.metadata.processes;
import java.util.ArrayList;
import java.util.List;
import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData;
/*******************************************************************************
** Meta-Data to define a front-end step in a process in a QQQ instance (e.g.,
** a screen presented to a user).
**
*******************************************************************************/
public class QFrontendStepMetaData extends QStepMetaData
{
private List<QFieldMetaData> formFields;
/*******************************************************************************
** Getter for formFields
**
*******************************************************************************/
public List<QFieldMetaData> getFormFields()
{
return formFields;
}
/*******************************************************************************
** Setter for formFields
**
*******************************************************************************/
public void setFormFields(List<QFieldMetaData> formFields)
{
this.formFields = formFields;
}
/*******************************************************************************
** fluent setter to add a single form field
**
*******************************************************************************/
public QFrontendStepMetaData withFormField(QFieldMetaData formField)
{
if(this.formFields == null)
{
this.formFields = new ArrayList<>();
}
this.formFields.add(formField);
return (this);
}
/*******************************************************************************
** fluent setter for formFields
**
*******************************************************************************/
public QFrontendStepMetaData withFormFields(List<QFieldMetaData> formFields)
{
this.formFields = formFields;
return (this);
}
/*******************************************************************************
** fluent setter for name
**
*******************************************************************************/
@Override
public QFrontendStepMetaData withName(String name)
{
setName(name);
return (this);
}
/*******************************************************************************
** fluent setter for label
**
*******************************************************************************/
@Override
public QFrontendStepMetaData withLabel(String label)
{
setLabel(label);
return (this);
}
}

View File

@ -24,6 +24,7 @@ package com.kingsrook.qqq.backend.core.model.metadata.processes;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData; import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData;
@ -33,10 +34,10 @@ import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData;
*******************************************************************************/ *******************************************************************************/
public class QProcessMetaData public class QProcessMetaData
{ {
private String name; private String name;
private String label; private String label;
private String tableName; private String tableName;
private List<QFunctionMetaData> functionList; private List<QStepMetaData> stepList;
@ -143,51 +144,51 @@ public class QProcessMetaData
/******************************************************************************* /*******************************************************************************
** Getter for functionList ** Getter for stepList
** **
*******************************************************************************/ *******************************************************************************/
public List<QFunctionMetaData> getFunctionList() public List<QStepMetaData> getStepList()
{ {
return functionList; return stepList;
} }
/******************************************************************************* /*******************************************************************************
** Setter for functionList ** Setter for stepList
** **
*******************************************************************************/ *******************************************************************************/
public QProcessMetaData withFunctionList(List<QFunctionMetaData> functionList) public QProcessMetaData withStepList(List<QStepMetaData> stepList)
{ {
this.functionList = functionList; this.stepList = stepList;
return (this); return (this);
} }
/******************************************************************************* /*******************************************************************************
** Setter for functionList ** Setter for stepList
** **
*******************************************************************************/ *******************************************************************************/
public QProcessMetaData addFunction(QFunctionMetaData function) public QProcessMetaData addStep(QStepMetaData step)
{ {
if(this.functionList == null) if(this.stepList == null)
{ {
this.functionList = new ArrayList<>(); this.stepList = new ArrayList<>();
} }
this.functionList.add(function); this.stepList.add(step);
return (this); return (this);
} }
/******************************************************************************* /*******************************************************************************
** Setter for functionList ** Setter for stepList
** **
*******************************************************************************/ *******************************************************************************/
public void setFunctionList(List<QFunctionMetaData> functionList) public void setStepList(List<QStepMetaData> stepList)
{ {
this.functionList = functionList; this.stepList = stepList;
} }
@ -195,13 +196,13 @@ public class QProcessMetaData
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public QFunctionMetaData getFunction(String functionName) public QStepMetaData getStep(String stepName)
{ {
for(QFunctionMetaData function : functionList) for(QStepMetaData step : stepList)
{ {
if(function.getName().equals(functionName)) if(step.getName().equals(stepName))
{ {
return (function); return (step);
} }
} }
@ -211,16 +212,27 @@ public class QProcessMetaData
/******************************************************************************* /*******************************************************************************
** Get a list of all of the input fields used by all the functions in this process. ** Wrapper to getStep, that internally casts t0 BackendStepMetaData
*******************************************************************************/ *******************************************************************************/
public QBackendStepMetaData getBackendStep(String name)
{
return (QBackendStepMetaData) getStep(name);
}
/*******************************************************************************
** Get a list of all of the input fields used by all the steps in this process.
*******************************************************************************/
@JsonIgnore
public List<QFieldMetaData> getInputFields() public List<QFieldMetaData> getInputFields()
{ {
List<QFieldMetaData> rs = new ArrayList<>(); List<QFieldMetaData> rs = new ArrayList<>();
if(functionList != null) if(stepList != null)
{ {
for(QFunctionMetaData function : functionList) for(QStepMetaData step : stepList)
{ {
rs.addAll(function.getInputFields()); rs.addAll(step.getInputFields());
} }
} }
return (rs); return (rs);
@ -229,18 +241,20 @@ public class QProcessMetaData
/******************************************************************************* /*******************************************************************************
** Get a list of all of the output fields used by all the functions in this process. ** Get a list of all of the output fields used by all the steps in this process.
*******************************************************************************/ *******************************************************************************/
@JsonIgnore
public List<QFieldMetaData> getOutputFields() public List<QFieldMetaData> getOutputFields()
{ {
List<QFieldMetaData> rs = new ArrayList<>(); List<QFieldMetaData> rs = new ArrayList<>();
if(functionList != null) if(stepList != null)
{ {
for(QFunctionMetaData function : functionList) for(QStepMetaData step : stepList)
{ {
rs.addAll(function.getOutputFields()); rs.addAll(step.getOutputFields());
} }
} }
return (rs); return (rs);
} }
} }

View File

@ -0,0 +1,130 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.metadata.processes;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData;
/*******************************************************************************
** Meta-Data to define a step in a process in a QQQ instance.
**
*******************************************************************************/
public class QStepMetaData
{
private String name;
private String label;
/*******************************************************************************
** Getter for name
**
*******************************************************************************/
public String getName()
{
return name;
}
/*******************************************************************************
** Setter for name
**
*******************************************************************************/
public void setName(String name)
{
this.name = name;
}
/*******************************************************************************
** Setter for name
**
*******************************************************************************/
public QStepMetaData withName(String name)
{
this.name = name;
return (this);
}
/*******************************************************************************
** Getter for label
**
*******************************************************************************/
public String getLabel()
{
return label;
}
/*******************************************************************************
** Setter for label
**
*******************************************************************************/
public void setLabel(String label)
{
this.label = label;
}
/*******************************************************************************
** Setter for label
**
*******************************************************************************/
public QStepMetaData withLabel(String label)
{
this.label = label;
return (this);
}
/*******************************************************************************
** Get a list of all of the input fields used by this function
*******************************************************************************/
@JsonIgnore
public List<QFieldMetaData> getInputFields()
{
return (new ArrayList<>());
}
/*******************************************************************************
** Get a list of all of the output fields used by this function
*******************************************************************************/
@JsonIgnore
public List<QFieldMetaData> getOutputFields()
{
return (new ArrayList<>());
}
}

View File

@ -24,9 +24,9 @@ package com.kingsrook.qqq.backend.core.processes.implementations.etl.basic;
import com.kingsrook.qqq.backend.core.actions.QueryAction; import com.kingsrook.qqq.backend.core.actions.QueryAction;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.interfaces.FunctionBody; import com.kingsrook.qqq.backend.core.interfaces.BackendStep;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionRequest; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepRequest;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionResult; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepResult;
import com.kingsrook.qqq.backend.core.model.actions.query.QueryRequest; import com.kingsrook.qqq.backend.core.model.actions.query.QueryRequest;
import com.kingsrook.qqq.backend.core.model.actions.query.QueryResult; import com.kingsrook.qqq.backend.core.model.actions.query.QueryResult;
@ -34,14 +34,15 @@ import com.kingsrook.qqq.backend.core.model.actions.query.QueryResult;
/******************************************************************************* /*******************************************************************************
** Function body for performing the Extract step of a basic ETL process. ** Function body for performing the Extract step of a basic ETL process.
*******************************************************************************/ *******************************************************************************/
public class BasicETLExtractFunction implements FunctionBody public class BasicETLExtractFunction implements BackendStep
{ {
@Override @Override
public void run(RunFunctionRequest runFunctionRequest, RunFunctionResult runFunctionResult) throws QException public void run(RunBackendStepRequest runBackendStepRequest, RunBackendStepResult runBackendStepResult) throws QException
{ {
QueryRequest queryRequest = new QueryRequest(runFunctionRequest.getInstance()); QueryRequest queryRequest = new QueryRequest(runBackendStepRequest.getInstance());
queryRequest.setSession(runFunctionRequest.getSession()); queryRequest.setSession(runBackendStepRequest.getSession());
queryRequest.setTableName(runFunctionRequest.getValueString(BasicETLProcess.FIELD_SOURCE_TABLE)); queryRequest.setTableName(runBackendStepRequest.getValueString(BasicETLProcess.FIELD_SOURCE_TABLE));
// queryRequest.setSkip(integerQueryParam(context, "skip")); // queryRequest.setSkip(integerQueryParam(context, "skip"));
// queryRequest.setLimit(integerQueryParam(context, "limit")); // queryRequest.setLimit(integerQueryParam(context, "limit"));
@ -54,6 +55,6 @@ public class BasicETLExtractFunction implements FunctionBody
QueryAction queryAction = new QueryAction(); QueryAction queryAction = new QueryAction();
QueryResult queryResult = queryAction.execute(queryRequest); QueryResult queryResult = queryAction.execute(queryRequest);
runFunctionResult.setRecords(queryResult.getRecords()); runBackendStepResult.setRecords(queryResult.getRecords());
} }
} }

View File

@ -26,11 +26,11 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.kingsrook.qqq.backend.core.actions.InsertAction; import com.kingsrook.qqq.backend.core.actions.InsertAction;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.interfaces.FunctionBody; import com.kingsrook.qqq.backend.core.interfaces.BackendStep;
import com.kingsrook.qqq.backend.core.model.actions.insert.InsertRequest; import com.kingsrook.qqq.backend.core.model.actions.insert.InsertRequest;
import com.kingsrook.qqq.backend.core.model.actions.insert.InsertResult; import com.kingsrook.qqq.backend.core.model.actions.insert.InsertResult;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionRequest; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepRequest;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionResult; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepResult;
import com.kingsrook.qqq.backend.core.model.data.QRecord; import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
@ -40,7 +40,7 @@ import org.apache.logging.log4j.Logger;
/******************************************************************************* /*******************************************************************************
** Function body for performing the Load step of a basic ETL process. ** Function body for performing the Load step of a basic ETL process.
*******************************************************************************/ *******************************************************************************/
public class BasicETLLoadFunction implements FunctionBody public class BasicETLLoadFunction implements BackendStep
{ {
private static final Logger LOG = LogManager.getLogger(BasicETLLoadFunction.class); private static final Logger LOG = LogManager.getLogger(BasicETLLoadFunction.class);
@ -50,23 +50,23 @@ public class BasicETLLoadFunction implements FunctionBody
** **
*******************************************************************************/ *******************************************************************************/
@Override @Override
public void run(RunFunctionRequest runFunctionRequest, RunFunctionResult runFunctionResult) throws QException public void run(RunBackendStepRequest runBackendStepRequest, RunBackendStepResult runBackendStepResult) throws QException
{ {
////////////////////////////////////////////////////// //////////////////////////////////////////////////////
// exit early with no-op if no records made it here // // exit early with no-op if no records made it here //
////////////////////////////////////////////////////// //////////////////////////////////////////////////////
List<QRecord> inputRecords = runFunctionRequest.getRecords(); List<QRecord> inputRecords = runBackendStepRequest.getRecords();
LOG.info("Received [" + inputRecords.size() + "] records to load"); LOG.info("Received [" + inputRecords.size() + "] records to load");
if(CollectionUtils.nullSafeIsEmpty(inputRecords)) if(CollectionUtils.nullSafeIsEmpty(inputRecords))
{ {
runFunctionResult.addValue(BasicETLProcess.FIELD_RECORD_COUNT, 0); runBackendStepResult.addValue(BasicETLProcess.FIELD_RECORD_COUNT, 0);
return; return;
} }
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
// put the destination table name in all records being inserted // // put the destination table name in all records being inserted //
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
String table = runFunctionRequest.getValueString(BasicETLProcess.FIELD_DESTINATION_TABLE); String table = runBackendStepRequest.getValueString(BasicETLProcess.FIELD_DESTINATION_TABLE);
for(QRecord record : inputRecords) for(QRecord record : inputRecords)
{ {
record.setTableName(table); record.setTableName(table);
@ -82,8 +82,8 @@ public class BasicETLLoadFunction implements FunctionBody
for(List<QRecord> page : CollectionUtils.getPages(inputRecords, pageSize)) for(List<QRecord> page : CollectionUtils.getPages(inputRecords, pageSize))
{ {
LOG.info("Inserting a page of [" + page.size() + "] records. Progress: " + recordsInserted + " loaded out of " + inputRecords.size() + " total"); LOG.info("Inserting a page of [" + page.size() + "] records. Progress: " + recordsInserted + " loaded out of " + inputRecords.size() + " total");
InsertRequest insertRequest = new InsertRequest(runFunctionRequest.getInstance()); InsertRequest insertRequest = new InsertRequest(runBackendStepRequest.getInstance());
insertRequest.setSession(runFunctionRequest.getSession()); insertRequest.setSession(runBackendStepRequest.getSession());
insertRequest.setTableName(table); insertRequest.setTableName(table);
insertRequest.setRecords(page); insertRequest.setRecords(page);
@ -93,8 +93,8 @@ public class BasicETLLoadFunction implements FunctionBody
recordsInserted += insertResult.getRecords().size(); recordsInserted += insertResult.getRecords().size();
} }
runFunctionResult.setRecords(outputRecords); runBackendStepResult.setRecords(outputRecords);
runFunctionResult.addValue(BasicETLProcess.FIELD_RECORD_COUNT, recordsInserted); runBackendStepResult.addValue(BasicETLProcess.FIELD_RECORD_COUNT, recordsInserted);
} }
} }

View File

@ -27,10 +27,11 @@ 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.QCodeUsage;
import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData; import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.QFieldType; import com.kingsrook.qqq.backend.core.model.metadata.QFieldType;
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;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionOutputMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionOutputMetaData;
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;
/******************************************************************************* /*******************************************************************************
@ -54,7 +55,7 @@ public class BasicETLProcess
*******************************************************************************/ *******************************************************************************/
public QProcessMetaData defineProcessMetaData() public QProcessMetaData defineProcessMetaData()
{ {
QFunctionMetaData extractFunction = new QFunctionMetaData() QStepMetaData extractFunction = new QBackendStepMetaData()
.withName(FUNCTION_NAME_EXTRACT) .withName(FUNCTION_NAME_EXTRACT)
.withCode(new QCodeReference() .withCode(new QCodeReference()
.withName(BasicETLExtractFunction.class.getName()) .withName(BasicETLExtractFunction.class.getName())
@ -63,7 +64,7 @@ public class BasicETLProcess
.withInputData(new QFunctionInputMetaData() .withInputData(new QFunctionInputMetaData()
.addField(new QFieldMetaData(FIELD_SOURCE_TABLE, QFieldType.STRING))); .addField(new QFieldMetaData(FIELD_SOURCE_TABLE, QFieldType.STRING)));
QFunctionMetaData transformFunction = new QFunctionMetaData() QStepMetaData transformFunction = new QBackendStepMetaData()
.withName(FUNCTION_NAME_TRANSFORM) .withName(FUNCTION_NAME_TRANSFORM)
.withCode(new QCodeReference() .withCode(new QCodeReference()
.withName(BasicETLTransformFunction.class.getName()) .withName(BasicETLTransformFunction.class.getName())
@ -73,7 +74,7 @@ public class BasicETLProcess
.addField(new QFieldMetaData(FIELD_MAPPING_JSON, QFieldType.STRING)) .addField(new QFieldMetaData(FIELD_MAPPING_JSON, QFieldType.STRING))
.addField(new QFieldMetaData(FIELD_DESTINATION_TABLE, QFieldType.STRING))); .addField(new QFieldMetaData(FIELD_DESTINATION_TABLE, QFieldType.STRING)));
QFunctionMetaData loadFunction = new QFunctionMetaData() QStepMetaData loadFunction = new QBackendStepMetaData()
.withName(FUNCTION_NAME_LOAD) .withName(FUNCTION_NAME_LOAD)
.withCode(new QCodeReference() .withCode(new QCodeReference()
.withName(BasicETLLoadFunction.class.getName()) .withName(BasicETLLoadFunction.class.getName())
@ -86,8 +87,8 @@ public class BasicETLProcess
return new QProcessMetaData() return new QProcessMetaData()
.withName(PROCESS_NAME) .withName(PROCESS_NAME)
.addFunction(extractFunction) .addStep(extractFunction)
.addFunction(transformFunction) .addStep(transformFunction)
.addFunction(loadFunction); .addStep(loadFunction);
} }
} }

View File

@ -28,9 +28,9 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.kingsrook.qqq.backend.core.adapters.JsonToQFieldMappingAdapter; import com.kingsrook.qqq.backend.core.adapters.JsonToQFieldMappingAdapter;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.interfaces.FunctionBody; import com.kingsrook.qqq.backend.core.interfaces.BackendStep;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionRequest; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepRequest;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionResult; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepResult;
import com.kingsrook.qqq.backend.core.model.actions.shared.mapping.AbstractQFieldMapping; import com.kingsrook.qqq.backend.core.model.actions.shared.mapping.AbstractQFieldMapping;
import com.kingsrook.qqq.backend.core.model.actions.shared.mapping.QKeyBasedFieldMapping; import com.kingsrook.qqq.backend.core.model.actions.shared.mapping.QKeyBasedFieldMapping;
import com.kingsrook.qqq.backend.core.model.data.QRecord; import com.kingsrook.qqq.backend.core.model.data.QRecord;
@ -46,7 +46,7 @@ import org.apache.logging.log4j.Logger;
/******************************************************************************* /*******************************************************************************
** Function body for performing the Extract step of a basic ETL process. ** Function body for performing the Extract step of a basic ETL process.
*******************************************************************************/ *******************************************************************************/
public class BasicETLTransformFunction implements FunctionBody public class BasicETLTransformFunction implements BackendStep
{ {
private static final Logger LOG = LogManager.getLogger(BasicETLTransformFunction.class); private static final Logger LOG = LogManager.getLogger(BasicETLTransformFunction.class);
@ -56,17 +56,17 @@ public class BasicETLTransformFunction implements FunctionBody
** **
*******************************************************************************/ *******************************************************************************/
@Override @Override
public void run(RunFunctionRequest runFunctionRequest, RunFunctionResult runFunctionResult) throws QException public void run(RunBackendStepRequest runBackendStepRequest, RunBackendStepResult runBackendStepResult) throws QException
{ {
//////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////
// exit early with no-op if no records made it here, or if we don't have a mapping to use // // exit early with no-op if no records made it here, or if we don't have a mapping to use //
//////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////
if(CollectionUtils.nullSafeIsEmpty(runFunctionRequest.getRecords())) if(CollectionUtils.nullSafeIsEmpty(runBackendStepRequest.getRecords()))
{ {
return; return;
} }
String mappingJSON = runFunctionRequest.getValueString(BasicETLProcess.FIELD_MAPPING_JSON); String mappingJSON = runBackendStepRequest.getValueString(BasicETLProcess.FIELD_MAPPING_JSON);
if(!StringUtils.hasContent(mappingJSON)) if(!StringUtils.hasContent(mappingJSON))
{ {
return; return;
@ -81,16 +81,16 @@ public class BasicETLTransformFunction implements FunctionBody
throw (new QException("Mapping was not a Key-based mapping type. Was a : " + mapping.getClass().getName())); throw (new QException("Mapping was not a Key-based mapping type. Was a : " + mapping.getClass().getName()));
} }
String tableName = runFunctionRequest.getValueString(BasicETLProcess.FIELD_DESTINATION_TABLE); String tableName = runBackendStepRequest.getValueString(BasicETLProcess.FIELD_DESTINATION_TABLE);
QTableMetaData table = runFunctionRequest.getInstance().getTable(tableName); QTableMetaData table = runBackendStepRequest.getInstance().getTable(tableName);
List<QRecord> mappedRecords = applyMapping(runFunctionRequest.getRecords(), table, keyBasedFieldMapping); List<QRecord> mappedRecords = applyMapping(runBackendStepRequest.getRecords(), table, keyBasedFieldMapping);
////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////
// todo - should this be conditional, e.g., driven by a field, or an opt-in customization function? // // todo - should this be conditional, e.g., driven by a field, or an opt-in customization function? //
////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////
removeNonNumericValuesFromMappedRecords(table, mappedRecords); removeNonNumericValuesFromMappedRecords(table, mappedRecords);
runFunctionResult.setRecords(mappedRecords); runBackendStepResult.setRecords(mappedRecords);
} }

View File

@ -0,0 +1,78 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.actions;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
import com.kingsrook.qqq.backend.core.model.actions.metadata.process.ProcessMetaDataRequest;
import com.kingsrook.qqq.backend.core.model.actions.metadata.process.ProcessMetaDataResult;
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.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
/*******************************************************************************
** Unit test for ProcessMetaDataAction
**
*******************************************************************************/
class ProcessMetaDataActionTest
{
/*******************************************************************************
** Test basic success case.
**
*******************************************************************************/
@Test
public void test() throws QException
{
ProcessMetaDataRequest request = new ProcessMetaDataRequest(TestUtils.defineInstance());
request.setSession(TestUtils.getMockSession());
request.setProcessName(TestUtils.PROCESS_NAME_GREET_PEOPLE_INTERACTIVE);
ProcessMetaDataResult result = new ProcessMetaDataAction().execute(request);
assertNotNull(result);
assertNotNull(result.getProcess());
assertEquals("greetInteractive", result.getProcess().getName());
assertEquals("Greet Interactive", result.getProcess().getLabel());
assertEquals(2, result.getProcess().getFrontendSteps().size());
}
/*******************************************************************************
** Test exception is thrown for the "not-found" case.
**
*******************************************************************************/
@Test
public void test_notFound()
{
assertThrows(QUserFacingException.class, () -> {
ProcessMetaDataRequest request = new ProcessMetaDataRequest(TestUtils.defineInstance());
request.setSession(TestUtils.getMockSession());
request.setProcessName("willNotBeFound");
new ProcessMetaDataAction().execute(request);
});
}
}

View File

@ -29,8 +29,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import com.kingsrook.qqq.backend.core.callbacks.QProcessCallback; import com.kingsrook.qqq.backend.core.callbacks.QProcessCallback;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionRequest; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepRequest;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionResult; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepResult;
import com.kingsrook.qqq.backend.core.model.actions.query.QQueryFilter; import com.kingsrook.qqq.backend.core.model.actions.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData; import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData;
import com.kingsrook.qqq.backend.core.utils.TestUtils; import com.kingsrook.qqq.backend.core.utils.TestUtils;
@ -44,7 +44,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public class RunFunctionTest public class RunBackendStepActionTest
{ {
/******************************************************************************* /*******************************************************************************
@ -53,13 +53,13 @@ public class RunFunctionTest
@Test @Test
public void test() throws QException public void test() throws QException
{ {
TestCallback callback = new TestCallback(); TestCallback callback = new TestCallback();
RunFunctionRequest request = new RunFunctionRequest(TestUtils.defineInstance()); RunBackendStepRequest request = new RunBackendStepRequest(TestUtils.defineInstance());
request.setSession(TestUtils.getMockSession()); request.setSession(TestUtils.getMockSession());
request.setProcessName("greet"); request.setProcessName("greet");
request.setFunctionName("prepare"); request.setStepName("prepare");
request.setCallback(callback); request.setCallback(callback);
RunFunctionResult result = new RunFunctionAction().execute(request); RunBackendStepResult result = new RunBackendStepAction().execute(request);
assertNotNull(result); assertNotNull(result);
assertNull(result.getError()); assertNull(result.getError());
assertTrue(result.getRecords().stream().allMatch(r -> r.getValues().containsKey("mockValue")), "records should have a mock value"); assertTrue(result.getRecords().stream().allMatch(r -> r.getValues().containsKey("mockValue")), "records should have a mock value");

View File

@ -22,29 +22,29 @@
package com.kingsrook.qqq.backend.core.actions.processes.person.addtopeoplesage; package com.kingsrook.qqq.backend.core.actions.processes.person.addtopeoplesage;
import com.kingsrook.qqq.backend.core.interfaces.FunctionBody; import com.kingsrook.qqq.backend.core.interfaces.BackendStep;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionRequest; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepRequest;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionResult; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepResult;
import com.kingsrook.qqq.backend.core.model.data.QRecord; import com.kingsrook.qqq.backend.core.model.data.QRecord;
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public class AddAge implements FunctionBody public class AddAge implements BackendStep
{ {
@Override @Override
public void run(RunFunctionRequest runFunctionRequest, RunFunctionResult runFunctionResult) public void run(RunBackendStepRequest runBackendStepRequest, RunBackendStepResult runBackendStepResult)
{ {
int totalYearsAdded = 0; int totalYearsAdded = 0;
Integer yearsToAdd = runFunctionRequest.getValueInteger("yearsToAdd"); Integer yearsToAdd = runBackendStepRequest.getValueInteger("yearsToAdd");
for(QRecord record : runFunctionRequest.getRecords()) for(QRecord record : runBackendStepRequest.getRecords())
{ {
Integer age = record.getValueInteger("age"); Integer age = record.getValueInteger("age");
age += yearsToAdd; age += yearsToAdd;
totalYearsAdded += yearsToAdd; totalYearsAdded += yearsToAdd;
record.setValue("age", age); record.setValue("age", age);
} }
runFunctionResult.addValue("totalYearsAdded", totalYearsAdded); runBackendStepResult.addValue("totalYearsAdded", totalYearsAdded);
} }
} }

View File

@ -24,24 +24,24 @@ package com.kingsrook.qqq.backend.core.actions.processes.person.addtopeoplesage;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.Period; import java.time.Period;
import com.kingsrook.qqq.backend.core.interfaces.FunctionBody; import com.kingsrook.qqq.backend.core.interfaces.BackendStep;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionRequest; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepRequest;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionResult; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepResult;
import com.kingsrook.qqq.backend.core.model.data.QRecord; import com.kingsrook.qqq.backend.core.model.data.QRecord;
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public class GetAgeStatistics implements FunctionBody public class GetAgeStatistics implements BackendStep
{ {
@Override @Override
public void run(RunFunctionRequest runFunctionRequest, RunFunctionResult runFunctionResult) public void run(RunBackendStepRequest runBackendStepRequest, RunBackendStepResult runBackendStepResult)
{ {
Integer min = null; Integer min = null;
Integer max = null; Integer max = null;
LocalDate now = LocalDate.now(); LocalDate now = LocalDate.now();
for(QRecord record : runFunctionRequest.getRecords()) for(QRecord record : runBackendStepRequest.getRecords())
{ {
LocalDate birthDate = record.getValueDate("birthDate"); LocalDate birthDate = record.getValueDate("birthDate");
Period until = birthDate.until(now); Period until = birthDate.until(now);
@ -52,7 +52,7 @@ public class GetAgeStatistics implements FunctionBody
max = (max == null || age > max) ? age : max; max = (max == null || age > max) ? age : max;
} }
runFunctionResult.addValue("minAge", min); runBackendStepResult.addValue("minAge", min);
runFunctionResult.addValue("maxAge", max); runBackendStepResult.addValue("maxAge", max);
} }
} }

View File

@ -23,7 +23,8 @@ package com.kingsrook.qqq.backend.core.utils;
import java.util.List; import java.util.List;
import com.kingsrook.qqq.backend.core.interfaces.mock.MockFunctionBody; import com.kingsrook.qqq.backend.core.adapters.QInstanceAdapter;
import com.kingsrook.qqq.backend.core.interfaces.mock.MockBackendStep;
import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationMetaData; import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData; import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.QCodeReference; import com.kingsrook.qqq.backend.core.model.metadata.QCodeReference;
@ -35,13 +36,12 @@ import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData; import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData;
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.possiblevalues.QPossibleValueSourceType;
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.QFunctionInputMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionInputMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionOutputMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionOutputMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QOutputView;
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.QRecordListMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QRecordListMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QRecordListView;
import com.kingsrook.qqq.backend.core.model.session.QSession; import com.kingsrook.qqq.backend.core.model.session.QSession;
import com.kingsrook.qqq.backend.core.modules.mock.MockAuthenticationModule; import com.kingsrook.qqq.backend.core.modules.mock.MockAuthenticationModule;
import com.kingsrook.qqq.backend.core.processes.implementations.etl.basic.BasicETLProcess; import com.kingsrook.qqq.backend.core.processes.implementations.etl.basic.BasicETLProcess;
@ -54,6 +54,8 @@ import com.kingsrook.qqq.backend.core.processes.implementations.etl.basic.BasicE
public class TestUtils public class TestUtils
{ {
public static String DEFAULT_BACKEND_NAME = "default"; public static String DEFAULT_BACKEND_NAME = "default";
public static String PROCESS_NAME_GREET_PEOPLE = "greet";
public static String PROCESS_NAME_GREET_PEOPLE_INTERACTIVE = "greetInteractive";
@ -71,8 +73,12 @@ public class TestUtils
qInstance.addTable(defineTableIdAndNameOnly()); qInstance.addTable(defineTableIdAndNameOnly());
qInstance.addPossibleValueSource(defineStatesPossibleValueSource()); qInstance.addPossibleValueSource(defineStatesPossibleValueSource());
qInstance.addProcess(defineProcessGreetPeople()); qInstance.addProcess(defineProcessGreetPeople());
qInstance.addProcess(defineProcessGreetPeopleInteractive());
qInstance.addProcess(defineProcessAddToPeoplesAge()); qInstance.addProcess(defineProcessAddToPeoplesAge());
qInstance.addProcess(new BasicETLProcess().defineProcessMetaData()); qInstance.addProcess(new BasicETLProcess().defineProcessMetaData());
System.out.println(new QInstanceAdapter().qInstanceToJson(qInstance));
return (qInstance); return (qInstance);
} }
@ -176,12 +182,12 @@ public class TestUtils
private static QProcessMetaData defineProcessGreetPeople() private static QProcessMetaData defineProcessGreetPeople()
{ {
return new QProcessMetaData() return new QProcessMetaData()
.withName("greet") .withName(PROCESS_NAME_GREET_PEOPLE)
.withTableName("person") .withTableName("person")
.addFunction(new QFunctionMetaData() .addStep(new QBackendStepMetaData()
.withName("prepare") .withName("prepare")
.withCode(new QCodeReference() .withCode(new QCodeReference()
.withName(MockFunctionBody.class.getName()) .withName(MockBackendStep.class.getName())
.withCodeType(QCodeType.JAVA) .withCodeType(QCodeType.JAVA)
.withCodeUsage(QCodeUsage.FUNCTION)) // todo - needed, or implied in this context? .withCodeUsage(QCodeUsage.FUNCTION)) // todo - needed, or implied in this context?
.withInputData(new QFunctionInputMetaData() .withInputData(new QFunctionInputMetaData()
@ -196,9 +202,49 @@ public class TestUtils
.addField(new QFieldMetaData("fullGreeting", QFieldType.STRING)) .addField(new QFieldMetaData("fullGreeting", QFieldType.STRING))
) )
.withFieldList(List.of(new QFieldMetaData("outputMessage", QFieldType.STRING)))) .withFieldList(List.of(new QFieldMetaData("outputMessage", QFieldType.STRING))))
.withOutputView(new QOutputView() );
.withMessageField("outputMessage") }
.withRecordListView(new QRecordListView().withFieldNames(List.of("id", "firstName", "lastName", "fullGreeting"))))
/*******************************************************************************
** Define an interactive version of the 'greet people' process
*******************************************************************************/
private static QProcessMetaData defineProcessGreetPeopleInteractive()
{
return new QProcessMetaData()
.withName(PROCESS_NAME_GREET_PEOPLE_INTERACTIVE)
.withTableName("person")
.addStep(new QFrontendStepMetaData()
.withName("setup")
.withFormField(new QFieldMetaData("greetingPrefix", QFieldType.STRING))
.withFormField(new QFieldMetaData("greetingSuffix", QFieldType.STRING))
)
.addStep(new QBackendStepMetaData()
.withName("doWork")
.withCode(new QCodeReference()
.withName(MockBackendStep.class.getName())
.withCodeType(QCodeType.JAVA)
.withCodeUsage(QCodeUsage.FUNCTION)) // todo - needed, or implied in this context?
.withInputData(new QFunctionInputMetaData()
.withRecordListMetaData(new QRecordListMetaData().withTableName("person"))
.withFieldList(List.of(
new QFieldMetaData("greetingPrefix", QFieldType.STRING),
new QFieldMetaData("greetingSuffix", QFieldType.STRING)
)))
.withOutputMetaData(new QFunctionOutputMetaData()
.withRecordListMetaData(new QRecordListMetaData()
.withTableName("person")
.addField(new QFieldMetaData("fullGreeting", QFieldType.STRING))
)
.withFieldList(List.of(new QFieldMetaData("outputMessage", QFieldType.STRING))))
)
.addStep(new QFrontendStepMetaData()
.withName("results")
.withFormField(new QFieldMetaData("outputMessage", QFieldType.STRING))
); );
} }
@ -217,7 +263,7 @@ public class TestUtils
return new QProcessMetaData() return new QProcessMetaData()
.withName("addToPeoplesAge") .withName("addToPeoplesAge")
.withTableName("person") .withTableName("person")
.addFunction(new QFunctionMetaData() .addStep(new QBackendStepMetaData()
.withName("getAgeStatistics") .withName("getAgeStatistics")
.withCode(new QCodeReference() .withCode(new QCodeReference()
.withName("com.kingsrook.qqq.backend.core.actions.processes.person.addtopeoplesage.GetAgeStatistics") .withName("com.kingsrook.qqq.backend.core.actions.processes.person.addtopeoplesage.GetAgeStatistics")
@ -231,10 +277,8 @@ public class TestUtils
.addField(new QFieldMetaData("age", QFieldType.INTEGER))) .addField(new QFieldMetaData("age", QFieldType.INTEGER)))
.withFieldList(List.of( .withFieldList(List.of(
new QFieldMetaData("minAge", QFieldType.INTEGER), new QFieldMetaData("minAge", QFieldType.INTEGER),
new QFieldMetaData("maxAge", QFieldType.INTEGER)))) new QFieldMetaData("maxAge", QFieldType.INTEGER)))))
.withOutputView(new QOutputView() .addStep(new QBackendStepMetaData()
.withMessageField("outputMessage"))) // todo - wut?
.addFunction(new QFunctionMetaData()
.withName("addAge") .withName("addAge")
.withCode(new QCodeReference() .withCode(new QCodeReference()
.withName("com.kingsrook.qqq.backend.core.actions.processes.person.addtopeoplesage.AddAge") .withName("com.kingsrook.qqq.backend.core.actions.processes.person.addtopeoplesage.AddAge")