Initial checkin

This commit is contained in:
Darin Kelkhoff
2021-12-30 07:40:11 -06:00
parent 380bdbb43d
commit 0fa57d1a4e
21 changed files with 2314 additions and 0 deletions

View File

@ -0,0 +1,155 @@
/*
* Copyright © 2021-2021. Kingsrook LLC <contact@kingsrook.com>. All Rights Reserved.
*/
package com.kingsrook.qqq.backend.core.actions;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.interfaces.FunctionBody;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionRequest;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionResult;
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.metadata.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData;
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.QProcessMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QRecordListMetaData;
/*******************************************************************************
** Action handler for running q-functions.
*
*******************************************************************************/
public class RunFunctionAction
{
/*******************************************************************************
**
*******************************************************************************/
public RunFunctionResult execute(RunFunctionRequest runFunctionRequest) throws QException
{
///////////////////////////////////////////////////////
// todo - shouldn't meta-data validation catch this? //
///////////////////////////////////////////////////////
QProcessMetaData process = runFunctionRequest.getInstance().getProcess(runFunctionRequest.getProcessName());
if(process == null)
{
throw new QException("Process [" + runFunctionRequest.getProcessName() + "] is not defined in this instance.");
}
QFunctionMetaData function = process.getFunction(runFunctionRequest.getFunctionName());
if(function == null)
{
throw new QException("Function [" + runFunctionRequest.getFunctionName() + "] is not defined in the process [" + process.getName() + "]");
}
//////////////////////////////////////////////////////////////////////////////////////
// ensure input data is set as needed - use callback object to get anything missing //
//////////////////////////////////////////////////////////////////////////////////////
ensureRecordsAreInRequest(runFunctionRequest, function);
ensureInputFieldsAreInRequest(runFunctionRequest, function);
////////////////////////////////////////////////////////////////////
// load and run the user-defined code that actually does the work //
////////////////////////////////////////////////////////////////////
RunFunctionResult runFunctionResult = runFunctionBodyCode(function.getCode(), runFunctionRequest);
return (runFunctionResult);
}
/*******************************************************************************
** check if this function needs any input fields - and if so, if we need to get one
** via the callback
**
*******************************************************************************/
private void ensureInputFieldsAreInRequest(RunFunctionRequest runFunctionRequest, QFunctionMetaData function)
{
QFunctionInputMetaData inputMetaData = function.getInputMetaData();
List<QFieldMetaData> fieldsToGet = new ArrayList<>();
for(QFieldMetaData field : inputMetaData.getFieldList())
{
Serializable value = runFunctionRequest.getValue(field.getName());
if(value == null)
{
// todo - check if required?
fieldsToGet.add(field);
}
}
Map<String, Serializable> fieldValues = runFunctionRequest.getCallback().getFieldValues(fieldsToGet);
for(Map.Entry<String, Serializable> entry : fieldValues.entrySet())
{
runFunctionRequest.addValue(entry.getKey(), entry.getValue());
// todo - check to make sure got values back?
}
}
/*******************************************************************************
** check if this function uses a record list - and if so, if we need to get one
** via the callback
*******************************************************************************/
private void ensureRecordsAreInRequest(RunFunctionRequest runFunctionRequest, QFunctionMetaData function) throws QException
{
QFunctionInputMetaData inputMetaData = function.getInputMetaData();
QRecordListMetaData recordListMetaData = inputMetaData.getRecordListMetaData();
if(recordListMetaData != null)
{
if(runFunctionRequest.getRecords() == null)
{
QueryRequest queryRequest = new QueryRequest(runFunctionRequest.getInstance());
queryRequest.setTableName(inputMetaData.getRecordListMetaData().getTableName());
// 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
// then this function can re-run, hopefully with the needed data.
queryRequest.setFilter(runFunctionRequest.getCallback().getQueryFilter());
QueryResult queryResult = new QueryAction().execute(queryRequest);
runFunctionRequest.setRecords(queryResult.getRecords());
// todo - handle 0 results found?
}
}
}
/*******************************************************************************
**
*******************************************************************************/
private RunFunctionResult runFunctionBodyCode(QCodeReference code, RunFunctionRequest runFunctionRequest)
{
RunFunctionResult runFunctionResult;
try
{
Class<?> codeClass = Class.forName(code.getName());
Object codeObject = codeClass.getConstructor().newInstance();
if(!(codeObject instanceof FunctionBody))
{
throw (new QException("The supplied code [" + codeClass.getName() + "] is not an instance of FunctionBody"));
}
FunctionBody functionBodyCodeObject = (FunctionBody) codeObject;
runFunctionResult = functionBodyCodeObject.run(runFunctionRequest);
}
catch(Exception e)
{
runFunctionResult = new RunFunctionResult();
runFunctionResult.setError("Error running function code: " + e.getMessage());
e.printStackTrace();
}
return (runFunctionResult);
}
}

View File

@ -0,0 +1,68 @@
/*
* Copyright © 2021-2021. Kingsrook LLC <contact@kingsrook.com>. All Rights Reserved.
*/
package com.kingsrook.qqq.backend.core.actions;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.interfaces.FunctionBody;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionRequest;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionResult;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessRequest;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessResult;
import com.kingsrook.qqq.backend.core.model.actions.query.QueryRequest;
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.QFieldMetaData;
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.QProcessMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QRecordListMetaData;
/*******************************************************************************
** Action handler for running q-processes (which are a sequence of q-functions).
*
*******************************************************************************/
public class RunProcessAction
{
/*******************************************************************************
**
*******************************************************************************/
public RunProcessResult execute(RunProcessRequest runProcessRequest) throws QException
{
///////////////////////////////////////////////////////
// todo - shouldn't meta-data validation catch this? //
///////////////////////////////////////////////////////
QProcessMetaData process = runProcessRequest.getInstance().getProcess(runProcessRequest.getProcessName());
if(process == null)
{
throw new QException("Process [" + runProcessRequest.getProcessName() + "] is not defined in this instance.");
}
RunProcessResult runProcessResult = runProcessFunctions(process);
return (runProcessResult);
}
/*******************************************************************************
**
*******************************************************************************/
private RunProcessResult runProcessFunctions(QProcessMetaData process)
{
List<QFunctionMetaData> functionList = process.getFunctionList();
for(QFunctionMetaData qFunctionMetaData : functionList)
{
// todo - custom routing?
}
}
}

View File

@ -0,0 +1,24 @@
/*
* Copyright © 2021-2021. Kingsrook LLC <contact@kingsrook.com>. All Rights Reserved.
*/
package com.kingsrook.qqq.backend.core.callbacks;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import com.kingsrook.qqq.backend.core.model.actions.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData;
/*******************************************************************************
** When a process/function can't run because it's missing data, this interface
** defines how the core framework goes back to a middleware (and possibly to a
** frontend) to get the data.
*******************************************************************************/
public interface QProcessCallback
{
QQueryFilter getQueryFilter();
Map<String, Serializable> getFieldValues(List<QFieldMetaData> fields);
}

View File

@ -0,0 +1,18 @@
/*
* Copyright © 2021-2021. Kingsrook LLC <contact@kingsrook.com>. All Rights Reserved.
*/
package com.kingsrook.qqq.backend.core.interfaces;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionRequest;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionResult;
/*******************************************************************************
**
*******************************************************************************/
public interface FunctionBody
{
RunFunctionResult run(RunFunctionRequest runFunctionRequest);
}

View File

@ -0,0 +1,33 @@
/*
* Copyright © 2021-2021. Kingsrook LLC <contact@kingsrook.com>. All Rights Reserved.
*/
package com.kingsrook.qqq.backend.core.interfaces.mock;
import com.kingsrook.qqq.backend.core.interfaces.FunctionBody;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionRequest;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunFunctionResult;
/*******************************************************************************
** Mock implementation of a FunctionBody.
**
** Basically just passes data from the request to the response.
*******************************************************************************/
public class MockFunctionBody implements FunctionBody
{
@Override
public RunFunctionResult run(RunFunctionRequest runFunctionRequest)
{
RunFunctionResult runFunctionResult = new RunFunctionResult();
runFunctionResult.setRecords(runFunctionRequest.getRecords());
runFunctionResult.getRecords().forEach(r -> r.setValue("mockValue", "Ha ha!"));
runFunctionResult.setValues(runFunctionRequest.getValues());
runFunctionResult.addValue("mockValue", "You so silly");
return (runFunctionResult);
}
}

View File

@ -0,0 +1,283 @@
/*
* Copyright © 2021-2021. Kingsrook LLC <contact@kingsrook.com>. All Rights Reserved.
*/
package com.kingsrook.qqq.backend.core.model.actions.processes;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.kingsrook.qqq.backend.core.callbacks.QProcessCallback;
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.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionMetaData;
/*******************************************************************************
** Request data container for the RunFunction action
**
*******************************************************************************/
public class RunFunctionRequest extends AbstractQRequest
{
private String processName;
private String functionName;
private List<QRecord> records;
private Map<String, Serializable> values;
private QProcessCallback callback;
/*******************************************************************************
**
*******************************************************************************/
public RunFunctionRequest()
{
}
/*******************************************************************************
**
*******************************************************************************/
public RunFunctionRequest(QInstance instance)
{
super(instance);
}
/*******************************************************************************
**
*******************************************************************************/
public QFunctionMetaData getFunctionMetaData()
{
return (instance.getFunction(getProcessName(), getFunctionName()));
}
/*******************************************************************************
** Getter for processName
**
*******************************************************************************/
public String getProcessName()
{
return processName;
}
/*******************************************************************************
** Setter for processName
**
*******************************************************************************/
public void setProcessName(String processName)
{
this.processName = processName;
}
/*******************************************************************************
** Setter for processName
**
*******************************************************************************/
public RunFunctionRequest withProcessName(String processName)
{
this.processName = processName;
return (this);
}
/*******************************************************************************
** Getter for functionName
**
*******************************************************************************/
public String getFunctionName()
{
return functionName;
}
/*******************************************************************************
** Setter for functionName
**
*******************************************************************************/
public void setFunctionName(String functionName)
{
this.functionName = functionName;
}
/*******************************************************************************
** Setter for functionName
**
*******************************************************************************/
public RunFunctionRequest withFunctionName(String functionName)
{
this.functionName = functionName;
return (this);
}
/*******************************************************************************
** Getter for records
**
*******************************************************************************/
public List<QRecord> getRecords()
{
return records;
}
/*******************************************************************************
** Setter for records
**
*******************************************************************************/
public void setRecords(List<QRecord> records)
{
this.records = records;
}
/*******************************************************************************
** Setter for records
**
*******************************************************************************/
public RunFunctionRequest withRecords(List<QRecord> records)
{
this.records = records;
return (this);
}
/*******************************************************************************
** Getter for values
**
*******************************************************************************/
public Map<String, Serializable> getValues()
{
return values;
}
/*******************************************************************************
** Setter for values
**
*******************************************************************************/
public void setValues(Map<String, Serializable> values)
{
this.values = values;
}
/*******************************************************************************
** Setter for values
**
*******************************************************************************/
public RunFunctionRequest withValues(Map<String, Serializable> values)
{
this.values = values;
return (this);
}
/*******************************************************************************
** Setter for values
**
*******************************************************************************/
public RunFunctionRequest addValue(String fieldName, Serializable value)
{
if(this.values == null)
{
this.values = new HashMap<>();
}
this.values.put(fieldName, value);
return (this);
}
/*******************************************************************************
** Getter for callback
**
*******************************************************************************/
public QProcessCallback getCallback()
{
return callback;
}
/*******************************************************************************
** Setter for callback
**
*******************************************************************************/
public void setCallback(QProcessCallback callback)
{
this.callback = callback;
}
/*******************************************************************************
** Setter for callback
**
*******************************************************************************/
public RunFunctionRequest withCallback(QProcessCallback callback)
{
this.callback = callback;
return (this);
}
/*******************************************************************************
** Getter for a single field's value
**
*******************************************************************************/
public Serializable getValue(String fieldName)
{
if(values == null)
{
return (null);
}
return (values.get(fieldName));
}
/*******************************************************************************
** Getter for a single field's value
**
*******************************************************************************/
public String getValueString(String fieldName)
{
return ((String) getValue(fieldName));
}
/*******************************************************************************
** Getter for a single field's value
**
*******************************************************************************/
public Integer getValueInteger(String fieldName)
{
return ((Integer) getValue(fieldName));
}
}

View File

@ -0,0 +1,140 @@
/*
* Copyright © 2021-2021. Kingsrook LLC <contact@kingsrook.com>. All Rights Reserved.
*/
package com.kingsrook.qqq.backend.core.model.actions.processes;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.kingsrook.qqq.backend.core.model.actions.AbstractQResult;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
/*******************************************************************************
** Result data container for the RunFunction action
**
*******************************************************************************/
public class RunFunctionResult extends AbstractQResult
{
private List<QRecord> records;
private Map<String, Serializable> values;
private String error;
/*******************************************************************************
**
*******************************************************************************/
public RunFunctionResult()
{
}
/*******************************************************************************
** Getter for records
**
*******************************************************************************/
public List<QRecord> getRecords()
{
return records;
}
/*******************************************************************************
** Setter for records
**
*******************************************************************************/
public void setRecords(List<QRecord> records)
{
this.records = records;
}
/*******************************************************************************
** Setter for records
**
*******************************************************************************/
public RunFunctionResult withRecords(List<QRecord> records)
{
this.records = records;
return (this);
}
/*******************************************************************************
** Getter for values
**
*******************************************************************************/
public Map<String, Serializable> getValues()
{
return values;
}
/*******************************************************************************
** Setter for values
**
*******************************************************************************/
public void setValues(Map<String, Serializable> values)
{
this.values = values;
}
/*******************************************************************************
** Setter for values
**
*******************************************************************************/
public RunFunctionResult withValues(Map<String, Serializable> values)
{
this.values = values;
return (this);
}
/*******************************************************************************
** Setter for values
**
*******************************************************************************/
public RunFunctionResult addValue(String fieldName, Serializable value)
{
if(this.values == null)
{
this.values = new HashMap<>();
}
this.values.put(fieldName, value);
return (this);
}
/*******************************************************************************
** Getter for error
**
*******************************************************************************/
public String getError()
{
return error;
}
/*******************************************************************************
** Setter for error
**
*******************************************************************************/
public void setError(String error)
{
this.error = error;
}
}

View File

@ -0,0 +1,248 @@
/*
* Copyright © 2021-2021. Kingsrook LLC <contact@kingsrook.com>. All Rights Reserved.
*/
package com.kingsrook.qqq.backend.core.model.actions.processes;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.kingsrook.qqq.backend.core.callbacks.QProcessCallback;
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.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
/*******************************************************************************
** Request data container for the RunProcess action
**
*******************************************************************************/
public class RunProcessRequest extends AbstractQRequest
{
private String processName;
private List<QRecord> records;
private Map<String, Serializable> values;
private QProcessCallback callback;
/*******************************************************************************
**
*******************************************************************************/
public RunProcessRequest()
{
}
/*******************************************************************************
**
*******************************************************************************/
public RunProcessRequest(QInstance instance)
{
super(instance);
}
/*******************************************************************************
**
*******************************************************************************/
public QProcessMetaData getProcessMetaData()
{
return (instance.getProcess(getProcessName()));
}
/*******************************************************************************
** Getter for processName
**
*******************************************************************************/
public String getProcessName()
{
return processName;
}
/*******************************************************************************
** Setter for processName
**
*******************************************************************************/
public void setProcessName(String processName)
{
this.processName = processName;
}
/*******************************************************************************
** Setter for processName
**
*******************************************************************************/
public RunProcessRequest withProcessName(String processName)
{
this.processName = processName;
return (this);
}
/*******************************************************************************
** Getter for records
**
*******************************************************************************/
public List<QRecord> getRecords()
{
return records;
}
/*******************************************************************************
** Setter for records
**
*******************************************************************************/
public void setRecords(List<QRecord> records)
{
this.records = records;
}
/*******************************************************************************
** Setter for records
**
*******************************************************************************/
public RunProcessRequest withRecords(List<QRecord> records)
{
this.records = records;
return (this);
}
/*******************************************************************************
** Getter for values
**
*******************************************************************************/
public Map<String, Serializable> getValues()
{
return values;
}
/*******************************************************************************
** Setter for values
**
*******************************************************************************/
public void setValues(Map<String, Serializable> values)
{
this.values = values;
}
/*******************************************************************************
** Setter for values
**
*******************************************************************************/
public RunProcessRequest withValues(Map<String, Serializable> values)
{
this.values = values;
return (this);
}
/*******************************************************************************
** Setter for values
**
*******************************************************************************/
public RunProcessRequest addValue(String fieldName, Serializable value)
{
if(this.values == null)
{
this.values = new HashMap<>();
}
this.values.put(fieldName, value);
return (this);
}
/*******************************************************************************
** Getter for callback
**
*******************************************************************************/
public QProcessCallback getCallback()
{
return callback;
}
/*******************************************************************************
** Setter for callback
**
*******************************************************************************/
public void setCallback(QProcessCallback callback)
{
this.callback = callback;
}
/*******************************************************************************
** Setter for callback
**
*******************************************************************************/
public RunProcessRequest withCallback(QProcessCallback callback)
{
this.callback = callback;
return (this);
}
/*******************************************************************************
** Getter for a single field's value
**
*******************************************************************************/
public Serializable getValue(String fieldName)
{
if(values == null)
{
return (null);
}
return (values.get(fieldName));
}
/*******************************************************************************
** Getter for a single field's value
**
*******************************************************************************/
public String getValueString(String fieldName)
{
return ((String) getValue(fieldName));
}
/*******************************************************************************
** Getter for a single field's value
**
*******************************************************************************/
public Integer getValueInteger(String fieldName)
{
return ((Integer) getValue(fieldName));
}
}

View File

@ -0,0 +1,140 @@
/*
* Copyright © 2021-2021. Kingsrook LLC <contact@kingsrook.com>. All Rights Reserved.
*/
package com.kingsrook.qqq.backend.core.model.actions.processes;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.kingsrook.qqq.backend.core.model.actions.AbstractQResult;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
/*******************************************************************************
** Result data container for the RunProcess action
**
*******************************************************************************/
public class RunProcessResult extends AbstractQResult
{
private List<QRecord> records;
private Map<String, Serializable> values;
private String error;
/*******************************************************************************
**
*******************************************************************************/
public RunProcessResult()
{
}
/*******************************************************************************
** Getter for records
**
*******************************************************************************/
public List<QRecord> getRecords()
{
return records;
}
/*******************************************************************************
** Setter for records
**
*******************************************************************************/
public void setRecords(List<QRecord> records)
{
this.records = records;
}
/*******************************************************************************
** Setter for records
**
*******************************************************************************/
public RunProcessResult withRecords(List<QRecord> records)
{
this.records = records;
return (this);
}
/*******************************************************************************
** Getter for values
**
*******************************************************************************/
public Map<String, Serializable> getValues()
{
return values;
}
/*******************************************************************************
** Setter for values
**
*******************************************************************************/
public void setValues(Map<String, Serializable> values)
{
this.values = values;
}
/*******************************************************************************
** Setter for values
**
*******************************************************************************/
public RunProcessResult withValues(Map<String, Serializable> values)
{
this.values = values;
return (this);
}
/*******************************************************************************
** Setter for values
**
*******************************************************************************/
public RunProcessResult addValue(String fieldName, Serializable value)
{
if(this.values == null)
{
this.values = new HashMap<>();
}
this.values.put(fieldName, value);
return (this);
}
/*******************************************************************************
** Getter for error
**
*******************************************************************************/
public String getError()
{
return error;
}
/*******************************************************************************
** Setter for error
**
*******************************************************************************/
public void setError(String error)
{
this.error = error;
}
}

View File

@ -0,0 +1,119 @@
/*
* Copyright © 2021-2021. Kingsrook LLC <contact@kingsrook.com>. All Rights Reserved.
*/
package com.kingsrook.qqq.backend.core.model.metadata;
/*******************************************************************************
**
*******************************************************************************/
public class QCodeReference
{
private String name;
private QCodeType codeType;
private QCodeUsage codeUsage;
/*******************************************************************************
** Getter for name
**
*******************************************************************************/
public String getName()
{
return name;
}
/*******************************************************************************
** Setter for name
**
*******************************************************************************/
public void setName(String name)
{
this.name = name;
}
/*******************************************************************************
** Setter for name
**
*******************************************************************************/
public QCodeReference withName(String name)
{
this.name = name;
return (this);
}
/*******************************************************************************
** Getter for codeType
**
*******************************************************************************/
public QCodeType getCodeType()
{
return codeType;
}
/*******************************************************************************
** Setter for codeType
**
*******************************************************************************/
public void setCodeType(QCodeType codeType)
{
this.codeType = codeType;
}
/*******************************************************************************
** Setter for codeType
**
*******************************************************************************/
public QCodeReference withCodeType(QCodeType codeType)
{
this.codeType = codeType;
return (this);
}
/*******************************************************************************
** Getter for codeUsage
**
*******************************************************************************/
public QCodeUsage getCodeUsage()
{
return codeUsage;
}
/*******************************************************************************
** Setter for codeUsage
**
*******************************************************************************/
public void setCodeUsage(QCodeUsage codeUsage)
{
this.codeUsage = codeUsage;
}
/*******************************************************************************
** Setter for codeUsage
**
*******************************************************************************/
public QCodeReference withCodeUsage(QCodeUsage codeUsage)
{
this.codeUsage = codeUsage;
return (this);
}
}

View File

@ -0,0 +1,15 @@
/*
* Copyright © 2021-2021. Kingsrook LLC <contact@kingsrook.com>. All Rights Reserved.
*/
package com.kingsrook.qqq.backend.core.model.metadata;
/*******************************************************************************
** Possible types for Q-Code entities
**
*******************************************************************************/
public enum QCodeType
{
JAVA
}

View File

@ -0,0 +1,15 @@
/*
* Copyright © 2021-2021. Kingsrook LLC <contact@kingsrook.com>. All Rights Reserved.
*/
package com.kingsrook.qqq.backend.core.model.metadata;
/*******************************************************************************
** Possible usages for Q-Code entities
**
*******************************************************************************/
public enum QCodeUsage
{
FUNCTION
}

View File

@ -5,10 +5,14 @@
package com.kingsrook.qqq.backend.core.model.metadata; package com.kingsrook.qqq.backend.core.model.metadata;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; 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.processes.QFunctionMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
/******************************************************************************* /*******************************************************************************
@ -24,6 +28,7 @@ public class QInstance
private Map<String, QBackendMetaData> backends = new HashMap<>(); private Map<String, QBackendMetaData> backends = new HashMap<>();
private Map<String, QTableMetaData> tables = new HashMap<>(); private Map<String, QTableMetaData> tables = new HashMap<>();
private Map<String, QProcessMetaData> processes = new HashMap<>();
// todo - lock down the object (no more changes allowed) after it's been validated? // todo - lock down the object (no more changes allowed) after it's been validated?
@JsonIgnore @JsonIgnore
@ -52,6 +57,22 @@ public class QInstance
} }
/*******************************************************************************
**
*******************************************************************************/
public List<QProcessMetaData> getProcessesForTable(String tableName)
{
List<QProcessMetaData> rs = new ArrayList<>();
for(QProcessMetaData process : processes.values())
{
if (tableName.equals(process.getTableName()))
{
rs.add(process);
}
}
return (rs);
}
/******************************************************************************* /*******************************************************************************
** Setter for hasBeenValidated ** Setter for hasBeenValidated
@ -104,6 +125,16 @@ public class QInstance
/*******************************************************************************
**
*******************************************************************************/
public void addProcess(QProcessMetaData process)
{
this.processes.put(process.getName(), process);
}
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
@ -124,6 +155,32 @@ public class QInstance
/*******************************************************************************
**
*******************************************************************************/
public QFunctionMetaData getFunction(String processName, String functionName)
{
QProcessMetaData qProcessMetaData = this.processes.get(processName);
if(qProcessMetaData == null)
{
return (null);
}
return (qProcessMetaData.getFunction(functionName));
}
/*******************************************************************************
**
*******************************************************************************/
public QProcessMetaData getProcess(String name)
{
return (this.processes.get(name));
}
/******************************************************************************* /*******************************************************************************
** Getter for backends ** Getter for backends
** **
@ -168,6 +225,28 @@ public class QInstance
/*******************************************************************************
** Getter for processes
**
*******************************************************************************/
public Map<String, QProcessMetaData> getProcesses()
{
return processes;
}
/*******************************************************************************
** Setter for processes
**
*******************************************************************************/
public void setProcesses(Map<String, QProcessMetaData> processes)
{
this.processes = processes;
}
/******************************************************************************* /*******************************************************************************
** Getter for hasBeenValidated ** Getter for hasBeenValidated
** **
@ -177,4 +256,5 @@ public class QInstance
return hasBeenValidated; return hasBeenValidated;
} }
} }

View File

@ -0,0 +1,106 @@
/*
* Copyright © 2021-2021. Kingsrook LLC <contact@kingsrook.com>. All Rights Reserved.
*/
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 the Input Data for a QQQ Function
**
*******************************************************************************/
public class QFunctionInputMetaData
{
private QRecordListMetaData recordListMetaData;
private List<QFieldMetaData> fieldList;
/*******************************************************************************
** Getter for recordListMetaData
**
*******************************************************************************/
public QRecordListMetaData getRecordListMetaData()
{
return recordListMetaData;
}
/*******************************************************************************
** Setter for recordListMetaData
**
*******************************************************************************/
public void setRecordListMetaData(QRecordListMetaData recordListMetaData)
{
this.recordListMetaData = recordListMetaData;
}
/*******************************************************************************
** Setter for recordListMetaData
**
*******************************************************************************/
public QFunctionInputMetaData withRecordListMetaData(QRecordListMetaData recordListMetaData)
{
this.recordListMetaData = recordListMetaData;
return (this);
}
/*******************************************************************************
** Getter for fieldList
**
*******************************************************************************/
public List<QFieldMetaData> getFieldList()
{
return fieldList;
}
/*******************************************************************************
** Setter for fieldList
**
*******************************************************************************/
public void setFieldList(List<QFieldMetaData> fieldList)
{
this.fieldList = fieldList;
}
/*******************************************************************************
** Setter for fieldList
**
*******************************************************************************/
public QFunctionInputMetaData withFieldList(List<QFieldMetaData> fieldList)
{
this.fieldList = fieldList;
return (this);
}
/*******************************************************************************
** Setter for fieldList
**
*******************************************************************************/
public QFunctionInputMetaData addField(QFieldMetaData field)
{
if(this.fieldList == null)
{
this.fieldList = new ArrayList<>();
}
this.fieldList.add(field);
return (this);
}
}

View File

@ -0,0 +1,227 @@
/*
* Copyright © 2021-2021. Kingsrook LLC <contact@kingsrook.com>. All Rights Reserved.
*/
package com.kingsrook.qqq.backend.core.model.metadata.processes;
import com.kingsrook.qqq.backend.core.model.metadata.QCodeReference;
/*******************************************************************************
** Meta-Data to define a function in a QQQ instance.
**
*******************************************************************************/
public class QFunctionMetaData
{
private String name;
private String label;
private QFunctionInputMetaData inputMetaData;
private QFunctionOutputMetaData outputMetaData;
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;
}
/*******************************************************************************
** Setter for label
**
*******************************************************************************/
public void setLabel(String label)
{
this.label = label;
}
/*******************************************************************************
** Setter for label
**
*******************************************************************************/
public QFunctionMetaData withLabel(String label)
{
this.label = label;
return (this);
}
/*******************************************************************************
** Getter for inputData
**
*******************************************************************************/
public QFunctionInputMetaData getInputMetaData()
{
return inputMetaData;
}
/*******************************************************************************
** Setter for inputData
**
*******************************************************************************/
public void setInputMetaData(QFunctionInputMetaData inputMetaData)
{
this.inputMetaData = inputMetaData;
}
/*******************************************************************************
** Setter for inputData
**
*******************************************************************************/
public QFunctionMetaData withInputData(QFunctionInputMetaData inputData)
{
this.inputMetaData = inputData;
return (this);
}
/*******************************************************************************
** Getter for outputData
**
*******************************************************************************/
public QFunctionOutputMetaData getOutputMetaData()
{
return outputMetaData;
}
/*******************************************************************************
** Setter for outputData
**
*******************************************************************************/
public void setOutputMetaData(QFunctionOutputMetaData outputMetaData)
{
this.outputMetaData = outputMetaData;
}
/*******************************************************************************
** Setter for outputData
**
*******************************************************************************/
public QFunctionMetaData withOutputMetaData(QFunctionOutputMetaData outputMetaData)
{
this.outputMetaData = outputMetaData;
return(this);
}
/*******************************************************************************
** Getter for code
**
*******************************************************************************/
public QCodeReference getCode()
{
return code;
}
/*******************************************************************************
** Setter for code
**
*******************************************************************************/
public void setCode(QCodeReference code)
{
this.code = code;
}
/*******************************************************************************
** Setter for code
**
*******************************************************************************/
public QFunctionMetaData withCode(QCodeReference code)
{
this.code = code;
return (this);
}
/*******************************************************************************
** 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);
}
}

View File

@ -0,0 +1,107 @@
/*
* Copyright © 2021-2021. Kingsrook LLC <contact@kingsrook.com>. All Rights Reserved.
*/
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 the Output Data for a QQQ Function
**
*******************************************************************************/
public class QFunctionOutputMetaData
{
private QRecordListMetaData recordListMetaData;
private List<QFieldMetaData> fieldList;
/*******************************************************************************
** Getter for recordListMetaData
**
*******************************************************************************/
public QRecordListMetaData getRecordListMetaData()
{
return recordListMetaData;
}
/*******************************************************************************
** Setter for recordListMetaData
**
*******************************************************************************/
public void setRecordListMetaData(QRecordListMetaData recordListMetaData)
{
this.recordListMetaData = recordListMetaData;
}
/*******************************************************************************
** Setter for recordListMetaData
**
*******************************************************************************/
public QFunctionOutputMetaData withRecordListMetaData(QRecordListMetaData recordListMetaData)
{
this.recordListMetaData = recordListMetaData;
return (this);
}
/*******************************************************************************
** Getter for fieldList
**
*******************************************************************************/
public List<QFieldMetaData> getFieldList()
{
return fieldList;
}
/*******************************************************************************
** Setter for fieldList
**
*******************************************************************************/
public void setFieldList(List<QFieldMetaData> fieldList)
{
this.fieldList = fieldList;
}
/*******************************************************************************
** Setter for fieldList
**
*******************************************************************************/
public QFunctionOutputMetaData withFieldList(List<QFieldMetaData> fieldList)
{
this.fieldList = fieldList;
return (this);
}
/*******************************************************************************
** Setter for fieldList
**
*******************************************************************************/
public QFunctionOutputMetaData addField(QFieldMetaData field)
{
if(this.fieldList == null)
{
this.fieldList = new ArrayList<>();
}
this.fieldList.add(field);
return (this);
}
}

View File

@ -0,0 +1,86 @@
/*
* Copyright © 2021-2021. Kingsrook LLC <contact@kingsrook.com>. All Rights Reserved.
*/
package com.kingsrook.qqq.backend.core.model.metadata.processes;
/*******************************************************************************
** Meta-Data to define the Output View for a QQQ Function
**
*******************************************************************************/
public class QOutputView
{
private String messageField;
private QRecordListView recordListView;
/*******************************************************************************
** Getter for message
**
*******************************************************************************/
public String getMessageField()
{
return messageField;
}
/*******************************************************************************
** Setter for message
**
*******************************************************************************/
public void setMessage(String message)
{
this.messageField = message;
}
/*******************************************************************************
** Setter for message
**
*******************************************************************************/
public QOutputView withMessageField(String messageField)
{
this.messageField = messageField;
return(this);
}
/*******************************************************************************
** Getter for recordListView
**
*******************************************************************************/
public QRecordListView getRecordListView()
{
return recordListView;
}
/*******************************************************************************
** Setter for recordListView
**
*******************************************************************************/
public void setRecordListView(QRecordListView recordListView)
{
this.recordListView = recordListView;
}
/*******************************************************************************
** Setter for recordListView
**
*******************************************************************************/
public QOutputView withRecordListView(QRecordListView recordListView)
{
this.recordListView = recordListView;
return(this);
}
}

View File

@ -0,0 +1,155 @@
/*
* Copyright © 2021-2021. Kingsrook LLC <contact@kingsrook.com>. All Rights Reserved.
*/
package com.kingsrook.qqq.backend.core.model.metadata.processes;
import java.util.ArrayList;
import java.util.List;
/*******************************************************************************
** Meta-Data to define a process in a QQQ instance.
**
*******************************************************************************/
public class QProcessMetaData
{
private String name;
private String tableName;
private List<QFunctionMetaData> functionList;
/*******************************************************************************
** Getter for name
**
*******************************************************************************/
public String getName()
{
return name;
}
/*******************************************************************************
** Setter for name
**
*******************************************************************************/
public void setName(String name)
{
this.name = name;
}
/*******************************************************************************
** Setter for name
**
*******************************************************************************/
public QProcessMetaData withName(String name)
{
this.name = name;
return (this);
}
/*******************************************************************************
** Getter for tableName
**
*******************************************************************************/
public String getTableName()
{
return tableName;
}
/*******************************************************************************
** Setter for tableName
**
*******************************************************************************/
public void setTableName(String tableName)
{
this.tableName = tableName;
}
/*******************************************************************************
** Setter for tableName
**
*******************************************************************************/
public QProcessMetaData withTableName(String tableName)
{
this.tableName = tableName;
return (this);
}
/*******************************************************************************
** Getter for functionList
**
*******************************************************************************/
public List<QFunctionMetaData> getFunctionList()
{
return functionList;
}
/*******************************************************************************
** Setter for functionList
**
*******************************************************************************/
public QProcessMetaData withFunctionList(List<QFunctionMetaData> functionList)
{
this.functionList = functionList;
return (this);
}
/*******************************************************************************
** Setter for functionList
**
*******************************************************************************/
public QProcessMetaData addFunction(QFunctionMetaData function)
{
if(this.functionList == null)
{
this.functionList = new ArrayList<>();
}
this.functionList.add(function);
return (this);
}
/*******************************************************************************
** Setter for functionList
**
*******************************************************************************/
public void setFunctionList(List<QFunctionMetaData> functionList)
{
this.functionList = functionList;
}
/*******************************************************************************
**
*******************************************************************************/
public QFunctionMetaData getFunction(String functionName)
{
for(QFunctionMetaData function : functionList)
{
if(function.getName().equals(functionName))
{
return (function);
}
}
return (null);
}
}

View File

@ -0,0 +1,123 @@
/*
* Copyright © 2021-2021. Kingsrook LLC <contact@kingsrook.com>. All Rights Reserved.
*/
package com.kingsrook.qqq.backend.core.model.metadata.processes;
import java.util.LinkedHashMap;
import java.util.Map;
import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData;
/*******************************************************************************
**
*******************************************************************************/
public class QRecordListMetaData
{
private String tableName;
private Map<String, QFieldMetaData> fields;
/*******************************************************************************
**
*******************************************************************************/
public QFieldMetaData getField(String fieldName)
{
if(fields == null)
{
return (null);
}
QFieldMetaData field = getFields().get(fieldName);
if(field == null)
{
throw (new IllegalArgumentException("Field [" + fieldName + "] was not found."));
}
return (field);
}
/*******************************************************************************
** Getter for tableName
**
*******************************************************************************/
public String getTableName()
{
return tableName;
}
/*******************************************************************************
** Setter for tableName
**
*******************************************************************************/
public void setTableName(String tableName)
{
this.tableName = tableName;
}
/*******************************************************************************
** Setter for tableName
**
*******************************************************************************/
public QRecordListMetaData withTableName(String tableName)
{
this.tableName = tableName;
return (this);
}
/*******************************************************************************
**
*******************************************************************************/
public Map<String, QFieldMetaData> getFields()
{
return fields;
}
/*******************************************************************************
** Setter for fields
**
*******************************************************************************/
public void setFields(Map<String, QFieldMetaData> fields)
{
this.fields = fields;
}
/*******************************************************************************
**
*******************************************************************************/
public QRecordListMetaData withFields(Map<String, QFieldMetaData> fields)
{
this.fields = fields;
return (this);
}
/*******************************************************************************
**
*******************************************************************************/
public QRecordListMetaData addField(QFieldMetaData field)
{
if(this.fields == null)
{
this.fields = new LinkedHashMap<>();
}
this.fields.put(field.getName(), field);
return (this);
}
}

View File

@ -0,0 +1,70 @@
/*
* Copyright © 2021-2021. Kingsrook LLC <contact@kingsrook.com>. All Rights Reserved.
*/
package com.kingsrook.qqq.backend.core.model.metadata.processes;
import java.util.ArrayList;
import java.util.List;
/*******************************************************************************
** Meta-Data to define how to view a qqq record list (e.g., what fields to show).
**
*******************************************************************************/
public class QRecordListView
{
private List<String> fieldNames;
/*******************************************************************************
** Getter for fieldNames
**
*******************************************************************************/
public List<String> getFieldNames()
{
return fieldNames;
}
/*******************************************************************************
** Setter for fieldNames
**
*******************************************************************************/
public void setFieldNames(List<String> fieldNames)
{
this.fieldNames = fieldNames;
}
/*******************************************************************************
** Setter for fieldNames
**
*******************************************************************************/
public QRecordListView withFieldNames(List<String> fieldNames)
{
this.fieldNames = fieldNames;
return (this);
}
/*******************************************************************************
** Setter for fieldNames
**
*******************************************************************************/
public QRecordListView addFieldName(String fieldName)
{
if(this.fieldNames == null)
{
this.fieldNames = new ArrayList<>();
}
this.fieldNames.add(fieldName);
return (this);
}
}

View File

@ -0,0 +1,102 @@
/*
* Copyright © 2021-2021. Kingsrook LLC <contact@kingsrook.com>. All Rights Reserved.
*/
package com.kingsrook.qqq.backend.core.actions;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.kingsrook.qqq.backend.core.callbacks.QProcessCallback;
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.RunFunctionResult;
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.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.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
/*******************************************************************************
**
*******************************************************************************/
public class RunFunctionTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test() throws QException
{
TestCallback callback = new TestCallback();
RunFunctionRequest request = new RunFunctionRequest(TestUtils.defineInstance());
request.setProcessName("greet");
request.setFunctionName("prepare");
request.setCallback(callback);
RunFunctionResult result = new RunFunctionAction().execute(request);
assertNotNull(result);
assertNull(result.getError());
assertTrue(result.getRecords().stream().allMatch(r -> r.getValues().containsKey("mockValue")), "records should have a mock value");
assertTrue(result.getValues().containsKey("mockValue"), "result object should have a mock value");
assertEquals("ABC", result.getValues().get("greetingPrefix"), "result object should have value from our callback");
assertTrue(callback.wasCalledForQueryFilter, "callback was used for query filter");
assertTrue(callback.wasCalledForFieldValues, "callback was used for field values");
}
/*******************************************************************************
**
*******************************************************************************/
private static class TestCallback implements QProcessCallback
{
private boolean wasCalledForQueryFilter = false;
private boolean wasCalledForFieldValues = false;
/*******************************************************************************
**
*******************************************************************************/
@Override
public QQueryFilter getQueryFilter()
{
wasCalledForQueryFilter = true;
return (new QQueryFilter());
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public Map<String, Serializable> getFieldValues(List<QFieldMetaData> fields)
{
wasCalledForFieldValues = true;
Map<String, Serializable> rs = new HashMap<>();
for(QFieldMetaData field : fields)
{
rs.put(field.getName(), switch(field.getType())
{
case STRING -> "ABC";
case INTEGER -> 42;
case DECIMAL -> new BigDecimal("47");
case DATE, DATE_TIME -> null;
case TEXT -> """
ABC
XYZ""";
case HTML -> "<b>Oh my</b>";
case PASSWORD -> "myPa**word";
});
}
return (rs);
}
}
}