mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-17 20:50:44 +00:00
Merge pull request #158 from Kingsrook/feature/process-tracers
Feature/process tracers
This commit is contained in:
@ -32,6 +32,7 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
|
||||
import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.NoCodeWidgetRenderer;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||
@ -53,6 +54,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.NoCodeWidgetFrontendComponentMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
||||
@ -63,6 +65,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.processes.QStateMachineStep
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.basepull.BasepullConfiguration;
|
||||
import com.kingsrook.qqq.backend.core.processes.tracing.ProcessTracerInterface;
|
||||
import com.kingsrook.qqq.backend.core.state.InMemoryStateProvider;
|
||||
import com.kingsrook.qqq.backend.core.state.StateProviderInterface;
|
||||
import com.kingsrook.qqq.backend.core.state.StateType;
|
||||
@ -71,6 +74,7 @@ import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -87,12 +91,16 @@ public class RunProcessAction
|
||||
public static final String BASEPULL_TIMESTAMP_FIELD = "basepullTimestampField";
|
||||
public static final String BASEPULL_CONFIGURATION = "basepullConfiguration";
|
||||
|
||||
public static final String PROCESS_TRACER_CODE_REFERENCE_FIELD = "processTracerCodeReference";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// indicator that the timestamp field should be updated - e.g., the execute step is finished. //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
public static final String BASEPULL_READY_TO_UPDATE_TIMESTAMP_FIELD = "basepullReadyToUpdateTimestamp";
|
||||
public static final String BASEPULL_DID_QUERY_USING_TIMESTAMP_FIELD = "basepullDidQueryUsingTimestamp";
|
||||
|
||||
private ProcessTracerInterface processTracer;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -119,6 +127,8 @@ public class RunProcessAction
|
||||
}
|
||||
runProcessOutput.setProcessUUID(runProcessInput.getProcessUUID());
|
||||
|
||||
traceStartOrResume(runProcessInput, process);
|
||||
|
||||
UUIDAndTypeStateKey stateKey = new UUIDAndTypeStateKey(UUID.fromString(runProcessInput.getProcessUUID()), StateType.PROCESS_STATUS);
|
||||
ProcessState processState = primeProcessState(runProcessInput, stateKey, process);
|
||||
|
||||
@ -166,6 +176,7 @@ public class RunProcessAction
|
||||
////////////////////////////////////////////////////////////
|
||||
// upon exception (e.g., one thrown by a step), throw it. //
|
||||
////////////////////////////////////////////////////////////
|
||||
traceBreakOrFinish(runProcessInput, runProcessOutput, qe);
|
||||
throw (qe);
|
||||
}
|
||||
catch(Exception e)
|
||||
@ -173,6 +184,7 @@ public class RunProcessAction
|
||||
////////////////////////////////////////////////////////////
|
||||
// upon exception (e.g., one thrown by a step), throw it. //
|
||||
////////////////////////////////////////////////////////////
|
||||
traceBreakOrFinish(runProcessInput, runProcessOutput, e);
|
||||
throw (new QException("Error running process", e));
|
||||
}
|
||||
finally
|
||||
@ -183,6 +195,8 @@ public class RunProcessAction
|
||||
runProcessOutput.setProcessState(processState);
|
||||
}
|
||||
|
||||
traceBreakOrFinish(runProcessInput, runProcessOutput, null);
|
||||
|
||||
return (runProcessOutput);
|
||||
}
|
||||
|
||||
@ -630,6 +644,7 @@ public class RunProcessAction
|
||||
runBackendStepInput.setCallback(runProcessInput.getCallback());
|
||||
runBackendStepInput.setFrontendStepBehavior(runProcessInput.getFrontendStepBehavior());
|
||||
runBackendStepInput.setAsyncJobCallback(runProcessInput.getAsyncJobCallback());
|
||||
runBackendStepInput.setProcessTracer(processTracer);
|
||||
|
||||
runBackendStepInput.setTableName(process.getTableName());
|
||||
if(!StringUtils.hasContent(runBackendStepInput.getTableName()))
|
||||
@ -651,9 +666,13 @@ public class RunProcessAction
|
||||
runBackendStepInput.setBasepullLastRunTime((Instant) runProcessInput.getValues().get(BASEPULL_LAST_RUNTIME_KEY));
|
||||
}
|
||||
|
||||
traceStepStart(runBackendStepInput);
|
||||
|
||||
RunBackendStepOutput runBackendStepOutput = new RunBackendStepAction().execute(runBackendStepInput);
|
||||
storeState(stateKey, runBackendStepOutput.getProcessState());
|
||||
|
||||
traceStepFinish(runBackendStepInput, runBackendStepOutput);
|
||||
|
||||
if(runBackendStepOutput.getException() != null)
|
||||
{
|
||||
runProcessOutput.setException(runBackendStepOutput.getException());
|
||||
@ -931,4 +950,153 @@ public class RunProcessAction
|
||||
runProcessInput.getValues().put(BASEPULL_TIMESTAMP_FIELD, basepullConfiguration.getTimestampField());
|
||||
runProcessInput.getValues().put(BASEPULL_CONFIGURATION, basepullConfiguration);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private void setupProcessTracer(RunProcessInput runProcessInput, QProcessMetaData process)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(process.getProcessTracerCodeReference() != null)
|
||||
{
|
||||
processTracer = QCodeLoader.getAdHoc(ProcessTracerInterface.class, process.getProcessTracerCodeReference());
|
||||
}
|
||||
|
||||
Serializable processTracerCodeReference = runProcessInput.getValue(PROCESS_TRACER_CODE_REFERENCE_FIELD);
|
||||
if(processTracerCodeReference != null)
|
||||
{
|
||||
if(processTracerCodeReference instanceof QCodeReference codeReference)
|
||||
{
|
||||
processTracer = QCodeLoader.getAdHoc(ProcessTracerInterface.class, codeReference);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn("Error setting up processTracer", e, logPair("processName", runProcessInput.getProcessName()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private void traceStartOrResume(RunProcessInput runProcessInput, QProcessMetaData process)
|
||||
{
|
||||
setupProcessTracer(runProcessInput, process);
|
||||
|
||||
try
|
||||
{
|
||||
if(processTracer != null)
|
||||
{
|
||||
if(StringUtils.hasContent(runProcessInput.getStartAfterStep()) || StringUtils.hasContent(runProcessInput.getStartAtStep()))
|
||||
{
|
||||
processTracer.handleProcessResume(runProcessInput);
|
||||
}
|
||||
else
|
||||
{
|
||||
processTracer.handleProcessStart(runProcessInput);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.info("Error in traceStart", e, logPair("processName", runProcessInput.getProcessName()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private void traceBreakOrFinish(RunProcessInput runProcessInput, RunProcessOutput runProcessOutput, Exception processException)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(processTracer != null)
|
||||
{
|
||||
ProcessState processState = runProcessOutput.getProcessState();
|
||||
boolean isBreak = true;
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
// if there's no next step, that means the process is done //
|
||||
/////////////////////////////////////////////////////////////
|
||||
if(processState.getNextStepName().isEmpty())
|
||||
{
|
||||
isBreak = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// or if the next step is the last index, then we're also done //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
String nextStepName = processState.getNextStepName().get();
|
||||
int nextStepIndex = processState.getStepList().indexOf(nextStepName);
|
||||
if(nextStepIndex == processState.getStepList().size() - 1)
|
||||
{
|
||||
isBreak = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(isBreak)
|
||||
{
|
||||
processTracer.handleProcessBreak(runProcessInput, runProcessOutput, processException);
|
||||
}
|
||||
else
|
||||
{
|
||||
processTracer.handleProcessFinish(runProcessInput, runProcessOutput, processException);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.info("Error in traceProcessFinish", e, logPair("processName", runProcessInput.getProcessName()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private void traceStepStart(RunBackendStepInput runBackendStepInput)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(processTracer != null)
|
||||
{
|
||||
processTracer.handleStepStart(runBackendStepInput);
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.info("Error in traceStepFinish", e, logPair("processName", runBackendStepInput.getProcessName()), logPair("stepName", runBackendStepInput.getStepName()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private void traceStepFinish(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(processTracer != null)
|
||||
{
|
||||
processTracer.handleStepFinish(runBackendStepInput, runBackendStepOutput);
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.info("Error in traceStepFinish", e, logPair("processName", runBackendStepInput.getProcessName()), logPair("stepName", runBackendStepInput.getStepName()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -31,16 +31,12 @@ import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Supplier;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.instances.QMetaDataVariableInterpreter;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
@ -54,12 +50,10 @@ import com.kingsrook.qqq.backend.core.model.querystats.QueryStatCriteriaField;
|
||||
import com.kingsrook.qqq.backend.core.model.querystats.QueryStatJoinTable;
|
||||
import com.kingsrook.qqq.backend.core.model.querystats.QueryStatOrderByField;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTable;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTablesMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTableTableManager;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.PrefixedDefaultThreadFactory;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
@ -371,7 +365,7 @@ public class QueryStatManager
|
||||
//////////////////////
|
||||
// set the table id //
|
||||
//////////////////////
|
||||
Integer qqqTableId = getQQQTableId(queryStat.getTableName());
|
||||
Integer qqqTableId = QQQTableTableManager.getQQQTableId(getInstance().qInstance, queryStat.getTableName());
|
||||
queryStat.setQqqTableId(qqqTableId);
|
||||
|
||||
//////////////////////////////
|
||||
@ -382,7 +376,7 @@ public class QueryStatManager
|
||||
List<QueryStatJoinTable> queryStatJoinTableList = new ArrayList<>();
|
||||
for(String joinTableName : queryStat.getJoinTableNames())
|
||||
{
|
||||
queryStatJoinTableList.add(new QueryStatJoinTable().withQqqTableId(getQQQTableId(joinTableName)));
|
||||
queryStatJoinTableList.add(new QueryStatJoinTable().withQqqTableId(QQQTableTableManager.getQQQTableId(getInstance().qInstance, joinTableName)));
|
||||
}
|
||||
queryStat.setQueryStatJoinTableList(queryStatJoinTableList);
|
||||
}
|
||||
@ -460,7 +454,7 @@ public class QueryStatManager
|
||||
String[] parts = fieldName.split("\\.");
|
||||
if(parts.length > 1)
|
||||
{
|
||||
queryStatCriteriaField.setQqqTableId(getQQQTableId(parts[0]));
|
||||
queryStatCriteriaField.setQqqTableId(QQQTableTableManager.getQQQTableId(getInstance().qInstance, parts[0]));
|
||||
queryStatCriteriaField.setName(parts[1]);
|
||||
}
|
||||
}
|
||||
@ -498,7 +492,7 @@ public class QueryStatManager
|
||||
String[] parts = fieldName.split("\\.");
|
||||
if(parts.length > 1)
|
||||
{
|
||||
queryStatOrderByField.setQqqTableId(getQQQTableId(parts[0]));
|
||||
queryStatOrderByField.setQqqTableId(QQQTableTableManager.getQQQTableId(getInstance().qInstance, parts[0]));
|
||||
queryStatOrderByField.setName(parts[1]);
|
||||
}
|
||||
}
|
||||
@ -512,50 +506,6 @@ public class QueryStatManager
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static Integer getQQQTableId(String tableName) throws QException
|
||||
{
|
||||
/////////////////////////////
|
||||
// look in the cache table //
|
||||
/////////////////////////////
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName(QQQTablesMetaDataProvider.QQQ_TABLE_CACHE_TABLE_NAME);
|
||||
getInput.setUniqueKey(MapBuilder.of("name", tableName));
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
|
||||
////////////////////////
|
||||
// upon cache miss... //
|
||||
////////////////////////
|
||||
if(getOutput.getRecord() == null)
|
||||
{
|
||||
QTableMetaData tableMetaData = getInstance().qInstance.getTable(tableName);
|
||||
if(tableMetaData == null)
|
||||
{
|
||||
LOG.info("No such table", logPair("tableName", tableName));
|
||||
return (null);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// insert the record (into the table, not the cache) //
|
||||
///////////////////////////////////////////////////////
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName(QQQTable.TABLE_NAME);
|
||||
insertInput.setRecords(List.of(new QRecord().withValue("name", tableName).withValue("label", tableMetaData.getLabel())));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
|
||||
///////////////////////////////////
|
||||
// repeat the get from the cache //
|
||||
///////////////////////////////////
|
||||
getOutput = new GetAction().execute(getInput);
|
||||
}
|
||||
|
||||
return getOutput.getRecord().getValueInteger("id");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -27,17 +27,22 @@ import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import com.kingsrook.qqq.backend.core.actions.async.AsyncJobCallback;
|
||||
import com.kingsrook.qqq.backend.core.actions.async.AsyncJobStatus;
|
||||
import com.kingsrook.qqq.backend.core.actions.async.NonPersistedAsyncJobCallback;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.QProcessCallback;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.processes.tracing.ProcessTracerInterface;
|
||||
import com.kingsrook.qqq.backend.core.processes.tracing.ProcessTracerMessage;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -46,6 +51,8 @@ import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
*******************************************************************************/
|
||||
public class RunBackendStepInput extends AbstractActionInput
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(RunBackendStepInput.class);
|
||||
|
||||
private ProcessState processState;
|
||||
private String processName;
|
||||
private String tableName;
|
||||
@ -55,12 +62,13 @@ public class RunBackendStepInput extends AbstractActionInput
|
||||
private RunProcessInput.FrontendStepBehavior frontendStepBehavior;
|
||||
private Instant basepullLastRunTime;
|
||||
|
||||
private ProcessTracerInterface processTracer;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// note - new fields should generally be added in method: cloneFieldsInto //
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -96,6 +104,7 @@ public class RunBackendStepInput extends AbstractActionInput
|
||||
target.setAsyncJobCallback(getAsyncJobCallback());
|
||||
target.setFrontendStepBehavior(getFrontendStepBehavior());
|
||||
target.setValues(getValues());
|
||||
target.setProcessTracer(getProcessTracer().orElse(null));
|
||||
}
|
||||
|
||||
|
||||
@ -535,4 +544,54 @@ public class RunBackendStepInput extends AbstractActionInput
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for processTracer
|
||||
*******************************************************************************/
|
||||
public void setProcessTracer(ProcessTracerInterface processTracer)
|
||||
{
|
||||
this.processTracer = processTracer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for processTracer
|
||||
*******************************************************************************/
|
||||
public RunBackendStepInput withProcessTracer(ProcessTracerInterface processTracer)
|
||||
{
|
||||
this.processTracer = processTracer;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public Optional<ProcessTracerInterface> getProcessTracer()
|
||||
{
|
||||
return Optional.ofNullable(processTracer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public void traceMessage(ProcessTracerMessage message)
|
||||
{
|
||||
if(processTracer != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
processTracer.handleMessage(this, message);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn("Error tracing message", e, logPair("message", message));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -530,6 +530,7 @@ public abstract class QRecordEntity
|
||||
// todo - more types!!
|
||||
return (returnType.equals(String.class)
|
||||
|| returnType.equals(Integer.class)
|
||||
|| returnType.equals(Long.class)
|
||||
|| returnType.equals(int.class)
|
||||
|| returnType.equals(Boolean.class)
|
||||
|| returnType.equals(boolean.class)
|
||||
|
@ -31,6 +31,7 @@ import java.util.Set;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.TopLevelMetaDataInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||
@ -70,6 +71,8 @@ public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissi
|
||||
private VariantRunStrategy variantRunStrategy;
|
||||
private String variantBackend;
|
||||
|
||||
private QCodeReference processTracerCodeReference;
|
||||
|
||||
private Map<String, QSupplementalProcessMetaData> supplementalMetaData;
|
||||
|
||||
|
||||
@ -866,4 +869,35 @@ public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissi
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for processTracerCodeReference
|
||||
*******************************************************************************/
|
||||
public QCodeReference getProcessTracerCodeReference()
|
||||
{
|
||||
return (this.processTracerCodeReference);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for processTracerCodeReference
|
||||
*******************************************************************************/
|
||||
public void setProcessTracerCodeReference(QCodeReference processTracerCodeReference)
|
||||
{
|
||||
this.processTracerCodeReference = processTracerCodeReference;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for processTracerCodeReference
|
||||
*******************************************************************************/
|
||||
public QProcessMetaData withProcessTracerCodeReference(QCodeReference processTracerCodeReference)
|
||||
{
|
||||
this.processTracerCodeReference = processTracerCodeReference;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,230 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.processes;
|
||||
|
||||
|
||||
import java.time.Instant;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.ValueTooLongBehavior;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** QRecord Entity for QQQProcess table - e.g., table that stores an id, name
|
||||
** and the label for all processes in the QQQ application. Useful as a foreign
|
||||
** key from other logging type tables.
|
||||
*******************************************************************************/
|
||||
public class QQQProcess extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "qqqProcess";
|
||||
|
||||
@QField(isEditable = false)
|
||||
private Integer id;
|
||||
|
||||
@QField(isEditable = false)
|
||||
private Instant createDate;
|
||||
|
||||
@QField(isEditable = false)
|
||||
private Instant modifyDate;
|
||||
|
||||
@QField(maxLength = 250, valueTooLongBehavior = ValueTooLongBehavior.ERROR)
|
||||
private String name;
|
||||
|
||||
@QField(maxLength = 250, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
|
||||
private String label;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Default constructor
|
||||
*******************************************************************************/
|
||||
public QQQProcess()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor that takes a QRecord
|
||||
*******************************************************************************/
|
||||
public QQQProcess(QRecord record)
|
||||
{
|
||||
populateFromQRecord(record);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return (this.id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id
|
||||
*******************************************************************************/
|
||||
public QQQProcess withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for createDate
|
||||
*******************************************************************************/
|
||||
public Instant getCreateDate()
|
||||
{
|
||||
return (this.createDate);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for createDate
|
||||
*******************************************************************************/
|
||||
public void setCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for createDate
|
||||
*******************************************************************************/
|
||||
public QQQProcess withCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for modifyDate
|
||||
*******************************************************************************/
|
||||
public Instant getModifyDate()
|
||||
{
|
||||
return (this.modifyDate);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for modifyDate
|
||||
*******************************************************************************/
|
||||
public void setModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for modifyDate
|
||||
*******************************************************************************/
|
||||
public QQQProcess withModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for name
|
||||
*******************************************************************************/
|
||||
public String getName()
|
||||
{
|
||||
return (this.name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for name
|
||||
*******************************************************************************/
|
||||
public void setName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for name
|
||||
*******************************************************************************/
|
||||
public QQQProcess withName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for label
|
||||
*******************************************************************************/
|
||||
public String getLabel()
|
||||
{
|
||||
return (this.label);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for label
|
||||
*******************************************************************************/
|
||||
public void setLabel(String label)
|
||||
{
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for label
|
||||
*******************************************************************************/
|
||||
public QQQProcess withLabel(String label)
|
||||
{
|
||||
this.label = label;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.processes;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput;
|
||||
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;
|
||||
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Utility class for accessing QQQProcess records (well, just their ids at this time)
|
||||
** Takes care of inserting upon a miss, and dealing with the cache table.
|
||||
*******************************************************************************/
|
||||
public class QQQProcessTableManager
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(QQQProcessTableManager.class);
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static Integer getQQQProcessId(QInstance qInstance, String processName) throws QException
|
||||
{
|
||||
/////////////////////////////
|
||||
// look in the cache table //
|
||||
/////////////////////////////
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName(QQQProcessesMetaDataProvider.QQQ_PROCESS_CACHE_TABLE_NAME);
|
||||
getInput.setUniqueKey(MapBuilder.of("name", processName));
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
|
||||
////////////////////////
|
||||
// upon cache miss... //
|
||||
////////////////////////
|
||||
if(getOutput.getRecord() == null)
|
||||
{
|
||||
QProcessMetaData processMetaData = qInstance.getProcess(processName);
|
||||
if(processMetaData == null)
|
||||
{
|
||||
LOG.info("No such process", logPair("processName", processName));
|
||||
return (null);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// insert the record (into the table, not the cache) //
|
||||
///////////////////////////////////////////////////////
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName(QQQProcess.TABLE_NAME);
|
||||
insertInput.setRecords(List.of(new QRecord().withValue("name", processName).withValue("label", processMetaData.getLabel())));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
|
||||
///////////////////////////////////
|
||||
// repeat the get from the cache //
|
||||
///////////////////////////////////
|
||||
getOutput = new GetAction().execute(getInput);
|
||||
}
|
||||
|
||||
return getOutput.getRecord().getValueInteger("id");
|
||||
}
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.processes;
|
||||
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.audits.AuditLevel;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.audits.QAuditRules;
|
||||
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.tables.Capability;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.UniqueKey;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.cache.CacheOf;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.cache.CacheUseCase;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Provides meta data for the QQQProcess table, PVS, and a cache table.
|
||||
*******************************************************************************/
|
||||
public class QQQProcessesMetaDataProvider
|
||||
{
|
||||
public static final String QQQ_PROCESS_CACHE_TABLE_NAME = QQQProcess.TABLE_NAME + "Cache";
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void defineAll(QInstance instance, String persistentBackendName, String cacheBackendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
||||
{
|
||||
instance.addTable(defineQQQProcess(persistentBackendName, backendDetailEnricher));
|
||||
instance.addTable(defineQQQProcessCache(cacheBackendName, backendDetailEnricher));
|
||||
instance.addPossibleValueSource(defineQQQProcessPossibleValueSource());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableMetaData defineQQQProcess(String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
||||
{
|
||||
QTableMetaData table = new QTableMetaData()
|
||||
.withName(QQQProcess.TABLE_NAME)
|
||||
.withLabel("Process")
|
||||
.withBackendName(backendName)
|
||||
.withAuditRules(new QAuditRules().withAuditLevel(AuditLevel.NONE))
|
||||
.withRecordLabelFormat("%s")
|
||||
.withRecordLabelFields("label")
|
||||
.withPrimaryKeyField("id")
|
||||
.withUniqueKey(new UniqueKey("name"))
|
||||
.withFieldsFromEntity(QQQProcess.class)
|
||||
.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE);
|
||||
|
||||
if(backendDetailEnricher != null)
|
||||
{
|
||||
backendDetailEnricher.accept(table);
|
||||
}
|
||||
|
||||
return (table);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableMetaData defineQQQProcessCache(String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
||||
{
|
||||
QTableMetaData table = new QTableMetaData()
|
||||
.withName(QQQ_PROCESS_CACHE_TABLE_NAME)
|
||||
.withBackendName(backendName)
|
||||
.withAuditRules(new QAuditRules().withAuditLevel(AuditLevel.NONE))
|
||||
.withRecordLabelFormat("%s")
|
||||
.withRecordLabelFields("label")
|
||||
.withPrimaryKeyField("id")
|
||||
.withUniqueKey(new UniqueKey("name"))
|
||||
.withFieldsFromEntity(QQQProcess.class)
|
||||
.withCacheOf(new CacheOf()
|
||||
.withSourceTable(QQQProcess.TABLE_NAME)
|
||||
.withUseCase(new CacheUseCase()
|
||||
.withType(CacheUseCase.Type.UNIQUE_KEY_TO_UNIQUE_KEY)
|
||||
.withCacheSourceMisses(false)
|
||||
.withCacheUniqueKey(new UniqueKey("name"))
|
||||
.withSourceUniqueKey(new UniqueKey("name"))
|
||||
.withDoCopySourcePrimaryKeyToCache(true)
|
||||
)
|
||||
);
|
||||
|
||||
if(backendDetailEnricher != null)
|
||||
{
|
||||
backendDetailEnricher.accept(table);
|
||||
}
|
||||
|
||||
return (table);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QPossibleValueSource defineQQQProcessPossibleValueSource()
|
||||
{
|
||||
return (new QPossibleValueSource()
|
||||
.withType(QPossibleValueSourceType.TABLE)
|
||||
.withName(QQQProcess.TABLE_NAME)
|
||||
.withTableName(QQQProcess.TABLE_NAME))
|
||||
.withOrderByField("label");
|
||||
}
|
||||
|
||||
}
|
@ -30,7 +30,9 @@ import com.kingsrook.qqq.backend.core.model.metadata.fields.ValueTooLongBehavior
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** QRecord Entity for QQQTable table
|
||||
** QRecord Entity for QQQTable table - e.g., table that stores an id, name
|
||||
** and the label for all tables in the QQQ application. Useful as a foreign
|
||||
** key from other logging type tables.
|
||||
*******************************************************************************/
|
||||
public class QQQTable extends QRecordEntity
|
||||
{
|
||||
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.tables;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput;
|
||||
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.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Utility class for accessing QQQTable records (well, just their ids at this time)
|
||||
** Takes care of inserting upon a miss, and dealing with the cache table.
|
||||
*******************************************************************************/
|
||||
public class QQQTableTableManager
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(QQQTableTableManager.class);
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static Integer getQQQTableId(QInstance qInstance, String tableName) throws QException
|
||||
{
|
||||
/////////////////////////////
|
||||
// look in the cache table //
|
||||
/////////////////////////////
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName(QQQTablesMetaDataProvider.QQQ_TABLE_CACHE_TABLE_NAME);
|
||||
getInput.setUniqueKey(MapBuilder.of("name", tableName));
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
|
||||
////////////////////////
|
||||
// upon cache miss... //
|
||||
////////////////////////
|
||||
if(getOutput.getRecord() == null)
|
||||
{
|
||||
QTableMetaData tableMetaData = qInstance.getTable(tableName);
|
||||
if(tableMetaData == null)
|
||||
{
|
||||
LOG.info("No such table", logPair("tableName", tableName));
|
||||
return (null);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// insert the record (into the table, not the cache) //
|
||||
///////////////////////////////////////////////////////
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName(QQQTable.TABLE_NAME);
|
||||
insertInput.setRecords(List.of(new QRecord().withValue("name", tableName).withValue("label", tableMetaData.getLabel())));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
|
||||
///////////////////////////////////
|
||||
// repeat the get from the cache //
|
||||
///////////////////////////////////
|
||||
getOutput = new GetAction().execute(getInput);
|
||||
}
|
||||
|
||||
return getOutput.getRecord().getValueInteger("id");
|
||||
}
|
||||
}
|
@ -37,7 +37,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.cache.CacheUseCase;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Provides meta data for the QQQTable table, PVS, and a cache table.
|
||||
*******************************************************************************/
|
||||
public class QQQTablesMetaDataProvider
|
||||
{
|
||||
|
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.processes.tracing;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.logging.LogPair;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLine;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLineInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessOutput;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.StreamedETLWithFrontendProcess;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Implementation of ProcessTracerInterface that writes messages to the Logger.
|
||||
*******************************************************************************/
|
||||
public class LoggingProcessTracer implements ProcessTracerInterface
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(LoggingProcessTracer.class);
|
||||
|
||||
private long startMillis;
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void handleProcessStart(RunProcessInput runProcessInput)
|
||||
{
|
||||
startMillis = System.currentTimeMillis();
|
||||
LOG.info("Starting process", logPair("name", runProcessInput.getProcessName()), logPair("uuid", runProcessInput.getProcessUUID()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void handleProcessResume(RunProcessInput runProcessInput)
|
||||
{
|
||||
String atOrAfter = "after";
|
||||
String atOrAfterStep = runProcessInput.getStartAfterStep();
|
||||
if(StringUtils.hasContent(runProcessInput.getStartAtStep()))
|
||||
{
|
||||
atOrAfter = "at";
|
||||
atOrAfterStep = runProcessInput.getStartAtStep();
|
||||
}
|
||||
LOG.info("Resuming process", logPair("name", runProcessInput.getProcessName()), logPair("uuid", runProcessInput.getProcessUUID()), logPair(atOrAfter, atOrAfterStep));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void handleStepStart(RunBackendStepInput runBackendStepInput)
|
||||
{
|
||||
LOG.info("Starting process step", runBackendStepInput.getStepName());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void handleMessage(RunBackendStepInput runBackendStepInput, ProcessTracerMessage message)
|
||||
{
|
||||
LOG.info("Message from process", logPair("message", message.getMessage()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void handleStepFinish(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput)
|
||||
{
|
||||
LOG.info("Finished process step", logPair("name", runBackendStepInput.getStepName()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void handleProcessBreak(RunProcessInput runProcessInput, RunProcessOutput runProcessOutput, Exception processException)
|
||||
{
|
||||
LOG.info("Breaking process", logPair("name", runProcessInput.getProcessName()), logPair("uuid", runProcessInput.getProcessUUID()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void handleProcessFinish(RunProcessInput runProcessInput, RunProcessOutput runProcessOutput, Exception processException)
|
||||
{
|
||||
long finishMillis = System.currentTimeMillis();
|
||||
|
||||
List<LogPair> summaryLogPairs = new ArrayList<>();
|
||||
Serializable processSummary = runProcessOutput.getValue(StreamedETLWithFrontendProcess.FIELD_PROCESS_SUMMARY);
|
||||
if(processSummary instanceof List)
|
||||
{
|
||||
List<? extends ProcessSummaryLineInterface> processSummaryLines = (List<? extends ProcessSummaryLineInterface>) processSummary;
|
||||
for(ProcessSummaryLineInterface processSummaryLineInterface : processSummaryLines)
|
||||
{
|
||||
if(processSummaryLineInterface instanceof ProcessSummaryLine processSummaryLine)
|
||||
{
|
||||
summaryLogPairs.add(logPair(String.valueOf(summaryLogPairs.size()), logPair("status", processSummaryLine.getStatus()), logPair("count", processSummaryLine.getCount()), logPair("message", processSummaryLine.getMessage())));
|
||||
}
|
||||
else
|
||||
{
|
||||
summaryLogPairs.add(logPair(String.valueOf(summaryLogPairs.size()), logPair("message", processSummaryLineInterface.getMessage())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOG.info("Finished process", logPair("name", runProcessInput.getProcessName()), logPair("uuid", runProcessInput.getProcessUUID()), logPair("millis", finishMillis - startMillis), logPair("summary", summaryLogPairs));
|
||||
}
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.processes.tracing;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessOutput;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Implementation of ProcessTracerInterface that does nothing (no-op).
|
||||
*******************************************************************************/
|
||||
public class NoopProcessTracer implements ProcessTracerInterface
|
||||
{
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void handleProcessStart(RunProcessInput runProcessInput)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void handleProcessResume(RunProcessInput runProcessInput)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void handleStepStart(RunBackendStepInput runBackendStepInput)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void handleMessage(RunBackendStepInput runBackendStepInput, ProcessTracerMessage message)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void handleStepFinish(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void handleProcessBreak(RunProcessInput runProcessInput, RunProcessOutput runProcessOutput, Exception processException)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void handleProcessFinish(RunProcessInput runProcessInput, RunProcessOutput runProcessOutput, Exception processException)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.processes.tracing;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessOutput;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Interface that can be plugged into the execution of a QProcess, that gets
|
||||
** callbacks from QQQ for events in the lifecycle of a process, which one may
|
||||
** wish to log or otherwise be aware of.
|
||||
*******************************************************************************/
|
||||
public interface ProcessTracerInterface
|
||||
{
|
||||
/***************************************************************************
|
||||
** Called when a new process is started.
|
||||
***************************************************************************/
|
||||
void handleProcessStart(RunProcessInput runProcessInput);
|
||||
|
||||
/***************************************************************************
|
||||
** Called when a process is resumed, e.g., after a "break" occurs between
|
||||
** backend steps and frontend steps.
|
||||
***************************************************************************/
|
||||
void handleProcessResume(RunProcessInput runProcessInput);
|
||||
|
||||
/***************************************************************************
|
||||
** Called when a (backend) step is started.
|
||||
***************************************************************************/
|
||||
void handleStepStart(RunBackendStepInput runBackendStepInput);
|
||||
|
||||
/***************************************************************************
|
||||
** Called when the (application, custom) process step code itself decides to
|
||||
** trace something. We imagine various subclasses of ProcessTracerMessage
|
||||
** to be created, to communicate more specific data for the tracer implementation.
|
||||
***************************************************************************/
|
||||
void handleMessage(RunBackendStepInput runBackendStepInput, ProcessTracerMessage message);
|
||||
|
||||
/***************************************************************************
|
||||
** Called when a (backend) step finishes.
|
||||
***************************************************************************/
|
||||
void handleStepFinish(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput);
|
||||
|
||||
/***************************************************************************
|
||||
** Called when a process break occurs, e.g., between backend and frontend
|
||||
** steps (but only if there are no more backend steps in the queue).
|
||||
***************************************************************************/
|
||||
void handleProcessBreak(RunProcessInput runProcessInput, RunProcessOutput runProcessOutput, Exception processException);
|
||||
|
||||
/***************************************************************************
|
||||
** Called after the last (backend) step of a process.
|
||||
***************************************************************************/
|
||||
void handleProcessFinish(RunProcessInput runProcessInput, RunProcessOutput runProcessOutput, Exception processException);
|
||||
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.processes.tracing;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Basic class that can be passed in to ProcessTracerInterface.handleMessage.
|
||||
** This class just provides for a string message. We anticipate subclasses
|
||||
** that may have more specific data, that specific tracer implementations may
|
||||
** be aware of.
|
||||
*******************************************************************************/
|
||||
public class ProcessTracerMessage
|
||||
{
|
||||
private String message;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ProcessTracerMessage()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ProcessTracerMessage(String message)
|
||||
{
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for message
|
||||
*******************************************************************************/
|
||||
public String getMessage()
|
||||
{
|
||||
return (this.message);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for message
|
||||
*******************************************************************************/
|
||||
public void setMessage(String message)
|
||||
{
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for message
|
||||
*******************************************************************************/
|
||||
public ProcessTracerMessage withMessage(String message)
|
||||
{
|
||||
this.message = message;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.processes;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
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.assertNull;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for QQQProcessTableManager
|
||||
*******************************************************************************/
|
||||
class QQQProcessTableManagerTest extends BaseTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testProcessesGetInsertedUponRequest() throws QException
|
||||
{
|
||||
new QQQProcessesMetaDataProvider().defineAll(QContext.getQInstance(), TestUtils.MEMORY_BACKEND_NAME, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
|
||||
Integer greetPeopleProcessId = QQQProcessTableManager.getQQQProcessId(QContext.getQInstance(), TestUtils.PROCESS_NAME_GREET_PEOPLE);
|
||||
assertEquals(1, greetPeopleProcessId);
|
||||
|
||||
assertEquals(1, QueryAction.execute(QQQProcessesMetaDataProvider.QQQ_PROCESS_CACHE_TABLE_NAME, new QQueryFilter()).size());
|
||||
assertEquals(1, QueryAction.execute(QQQProcess.TABLE_NAME, new QQueryFilter()).size());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testExistingProcessComesBack() throws QException
|
||||
{
|
||||
new QQQProcessesMetaDataProvider().defineAll(QContext.getQInstance(), TestUtils.MEMORY_BACKEND_NAME, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
|
||||
new InsertAction().execute(new InsertInput(QQQProcess.TABLE_NAME).withRecordEntity(new QQQProcess().withName(TestUtils.PROCESS_NAME_GREET_PEOPLE)));
|
||||
new InsertAction().execute(new InsertInput(QQQProcess.TABLE_NAME).withRecordEntity(new QQQProcess().withName(TestUtils.PROCESS_NAME_ADD_TO_PEOPLES_AGE)));
|
||||
|
||||
assertEquals(0, QueryAction.execute(QQQProcessesMetaDataProvider.QQQ_PROCESS_CACHE_TABLE_NAME, new QQueryFilter()).size());
|
||||
assertEquals(2, QueryAction.execute(QQQProcess.TABLE_NAME, new QQueryFilter()).size());
|
||||
|
||||
assertEquals(2, QQQProcessTableManager.getQQQProcessId(QContext.getQInstance(), TestUtils.PROCESS_NAME_ADD_TO_PEOPLES_AGE));
|
||||
|
||||
assertEquals(1, QueryAction.execute(QQQProcessesMetaDataProvider.QQQ_PROCESS_CACHE_TABLE_NAME, new QQueryFilter()).size());
|
||||
assertEquals(2, QueryAction.execute(QQQProcess.TABLE_NAME, new QQueryFilter()).size());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testBogusProcessName() throws QException
|
||||
{
|
||||
new QQQProcessesMetaDataProvider().defineAll(QContext.getQInstance(), TestUtils.MEMORY_BACKEND_NAME, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
assertNull(QQQProcessTableManager.getQQQProcessId(QContext.getQInstance(), "not a process"));
|
||||
assertEquals(0, QueryAction.execute(QQQProcessesMetaDataProvider.QQQ_PROCESS_CACHE_TABLE_NAME, new QQueryFilter()).size());
|
||||
assertEquals(0, QueryAction.execute(QQQProcess.TABLE_NAME, new QQueryFilter()).size());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.tables;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
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.assertNull;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for QQQTableTableManager
|
||||
*******************************************************************************/
|
||||
class QQQTableTableManagerTest extends BaseTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testTablesGetInsertedUponRequest() throws QException
|
||||
{
|
||||
new QQQTablesMetaDataProvider().defineAll(QContext.getQInstance(), TestUtils.MEMORY_BACKEND_NAME, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
|
||||
Integer personMemoryTableId = QQQTableTableManager.getQQQTableId(QContext.getQInstance(), TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
assertEquals(1, personMemoryTableId);
|
||||
|
||||
assertEquals(1, QueryAction.execute(QQQTablesMetaDataProvider.QQQ_TABLE_CACHE_TABLE_NAME, new QQueryFilter()).size());
|
||||
assertEquals(1, QueryAction.execute(QQQTable.TABLE_NAME, new QQueryFilter()).size());
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testExistingTableComesBack() throws QException
|
||||
{
|
||||
new QQQTablesMetaDataProvider().defineAll(QContext.getQInstance(), TestUtils.MEMORY_BACKEND_NAME, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
|
||||
new InsertAction().execute(new InsertInput(QQQTable.TABLE_NAME).withRecordEntity(new QQQTable().withName(TestUtils.TABLE_NAME_SHAPE)));
|
||||
new InsertAction().execute(new InsertInput(QQQTable.TABLE_NAME).withRecordEntity(new QQQTable().withName(TestUtils.TABLE_NAME_ID_AND_NAME_ONLY)));
|
||||
|
||||
assertEquals(0, QueryAction.execute(QQQTablesMetaDataProvider.QQQ_TABLE_CACHE_TABLE_NAME, new QQueryFilter()).size());
|
||||
assertEquals(2, QueryAction.execute(QQQTable.TABLE_NAME, new QQueryFilter()).size());
|
||||
|
||||
assertEquals(2, QQQTableTableManager.getQQQTableId(QContext.getQInstance(), TestUtils.TABLE_NAME_ID_AND_NAME_ONLY));
|
||||
|
||||
assertEquals(1, QueryAction.execute(QQQTablesMetaDataProvider.QQQ_TABLE_CACHE_TABLE_NAME, new QQueryFilter()).size());
|
||||
assertEquals(2, QueryAction.execute(QQQTable.TABLE_NAME, new QQueryFilter()).size());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testBogusTableName() throws QException
|
||||
{
|
||||
new QQQTablesMetaDataProvider().defineAll(QContext.getQInstance(), TestUtils.MEMORY_BACKEND_NAME, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
assertNull(QQQTableTableManager.getQQQTableId(QContext.getQInstance(), "not a table"));
|
||||
assertEquals(0, QueryAction.execute(QQQTablesMetaDataProvider.QQQ_TABLE_CACHE_TABLE_NAME, new QQueryFilter()).size());
|
||||
assertEquals(0, QueryAction.execute(QQQTable.TABLE_NAME, new QQueryFilter()).size());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.processes.tracing;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.QProcessCallbackFactory;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.RunProcessAction;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QCollectingLogger;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionAssert;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for LoggingProcessTracer
|
||||
*******************************************************************************/
|
||||
class LoggingProcessTracerTest extends BaseTest
|
||||
{
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@AfterEach
|
||||
void afterEach()
|
||||
{
|
||||
QLogger.deactivateCollectingLoggerForClass(LoggingProcessTracer.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** this test is based on RunProcessTest#testBreakOnFrontendSteps
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void test() throws QException
|
||||
{
|
||||
/////////////////////////////////////////////////////
|
||||
// activate the tracer for this run of the process //
|
||||
/////////////////////////////////////////////////////
|
||||
RunProcessInput input = new RunProcessInput();
|
||||
input.addValue(RunProcessAction.PROCESS_TRACER_CODE_REFERENCE_FIELD, new QCodeReference(LoggingProcessTracer.class));
|
||||
QCollectingLogger collectingLogger = QLogger.activateCollectingLoggerForClass(LoggingProcessTracer.class);
|
||||
|
||||
input.setProcessName(TestUtils.PROCESS_NAME_GREET_PEOPLE_INTERACTIVE);
|
||||
input.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.BREAK);
|
||||
input.setCallback(QProcessCallbackFactory.forRecord(new QRecord().withValue("id", 1)));
|
||||
RunProcessOutput result0 = new RunProcessAction().execute(input);
|
||||
assertNotNull(result0);
|
||||
|
||||
CollectionAssert.assertThat(collectingLogger.getCollectedMessages())
|
||||
.matchesAll(List.of("Starting process", "Breaking process"),
|
||||
(s, clm) -> clm.getMessageAsJSONObject().getString("message").equals(s));
|
||||
collectingLogger.clear();
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
// now re-run (resume) to the end of the process //
|
||||
///////////////////////////////////////////////////
|
||||
input.setStartAfterStep(result0.getProcessState().getNextStepName().get());
|
||||
RunProcessOutput result1 = new RunProcessAction().execute(input);
|
||||
CollectionAssert.assertThat(collectingLogger.getCollectedMessages())
|
||||
.matchesAll(List.of("Resuming process", "Starting process step", "Finished process step", "Finished process"),
|
||||
(s, clm) -> clm.getMessageAsJSONObject().getString("message").equals(s));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.processes.tracing;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.QProcessCallbackFactory;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.RunProcessAction;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for NoopProcessTracer ... kinda a BS test, but here to prevent
|
||||
** a missing-class for code coverage...
|
||||
*******************************************************************************/
|
||||
class NoopProcessTracerTest extends BaseTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void test() throws QException
|
||||
{
|
||||
//////////////////////////////////////////////////////////
|
||||
// activate the noop tracer for this run of the process //
|
||||
//////////////////////////////////////////////////////////
|
||||
RunProcessInput input = new RunProcessInput();
|
||||
input.addValue(RunProcessAction.PROCESS_TRACER_CODE_REFERENCE_FIELD, new QCodeReference(NoopProcessTracer.class));
|
||||
|
||||
input.setProcessName(TestUtils.PROCESS_NAME_GREET_PEOPLE_INTERACTIVE);
|
||||
input.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.BREAK);
|
||||
input.setCallback(QProcessCallbackFactory.forRecord(new QRecord().withValue("id", 1)));
|
||||
new RunProcessAction().execute(input);
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user