mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-22 23:18:45 +00:00
Compare commits
1 Commits
snapshot-f
...
snapshot-f
Author | SHA1 | Date | |
---|---|---|---|
0e7c55e108 |
@ -2,57 +2,16 @@
|
|||||||
== Joins
|
== Joins
|
||||||
include::../variables.adoc[]
|
include::../variables.adoc[]
|
||||||
|
|
||||||
A `QJoinMetaData` is a meta-data object that tells QQQ, essentially “it is possible for these 2 tables to join, here’s how to do it”.
|
#TODO#
|
||||||
|
|
||||||
Joins can be used then, in an application, in a number of possible ways:
|
|
||||||
|
|
||||||
* In a {link-table}, we can specify joins to be “exposed”, e.g., made available to users on a query screen
|
|
||||||
* Also in a Table, as part of an “Association”, which sets up one table as a “parent” of another,
|
|
||||||
such that you can store (and fetch) the child-records at the same time as the parent
|
|
||||||
** A common use-case here may be an order & lineItem table -
|
|
||||||
such that QQQ can generate an API uses to allow you to post an order and its lines in a single request, and they get stored all together
|
|
||||||
* In defining the security field (record lock) on a table,
|
|
||||||
sometimes, it isn’t a field directly on the table, but instead comes from a joined table (possibly even more than just 1 table away).
|
|
||||||
** For example, maybe a lineItem table, doesn't have a clientId, but needs secured by that field
|
|
||||||
- so its recordLock can specify a “joinNameChain” that describes how to get from lineItem to order.clientId
|
|
||||||
* The `QueryAction` can take (through its QueryInput object) zero or more QueryJoin objects,
|
|
||||||
which must make a reference (implicitly or explicitly) to a QJoinMetaData.
|
|
||||||
See the section on <<QueryJoin,QueryJoins>> for more details.
|
|
||||||
|
|
||||||
=== QJoinMetaData
|
=== QJoinMetaData
|
||||||
Joins are defined in a QQQ Instance in a `*QJoinMetaData*` object.
|
Joins are defined in a QQQ Instance in a `*QJoinMetaData*` object.
|
||||||
|
|
||||||
In this object, we have the concept of a "leftTable" and a "rightTable".
|
#TODO#
|
||||||
There isn't generally anything special about which table is on the "left" and which is on the "right".
|
|
||||||
But the remaining pieces of the QJoinMetaData do all need to line-up with these sides.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
* The Type (one-to-one, one-to-many, many-to-one) - where the leftTable comes first, and rightTable comes second
|
|
||||||
(e.g., a one-to-many means 1-row in leftTable has many-rows in rightTable associated with it)
|
|
||||||
* In a JoinOn object, the 1st field name given is from the leftTable;
|
|
||||||
the second fieldName from the rightTable.
|
|
||||||
|
|
||||||
*QJoinMetaData Properties:*
|
*QJoinMetaData Properties:*
|
||||||
|
|
||||||
* `name` - *String, Required* - Unique name for the join within the QQQ Instance.
|
* `name` - *String, Required* - Unique name for the join within the QQQ Instance. #todo infererences or conventions?#
|
||||||
** One convention is to name joins based on (leftTable + "Join" + rightTable).
|
|
||||||
** If you do not wish to define join names yourself, the method `withInferredName()`
|
#TODO#
|
||||||
can be called (which defers to
|
|
||||||
`public static String makeInferredJoinName(String leftTable, String rightTable)`),
|
|
||||||
to create a name for the join following the (leftTable + "Join" + rightTable) convention.
|
|
||||||
* `leftTable` - *String, Required* - Name of a {link-table} in the {link-instance}.
|
|
||||||
* `rightTable` - *String, Required* - Name of a {link-table} in the {link-instance}.
|
|
||||||
* `type` - *enum, Required* - cardinality between the two tables in the join.
|
|
||||||
** e.g., `ONE_TO_ONE`, `ONE_TO_MANY` (indicating 1 record in the left table may join
|
|
||||||
to many records in the right table), or `MANY_TO_ONE` (vice-versa).
|
|
||||||
** Note that there is no MANY_TO_MANY option, as a many-to-many is built as multiple QJoinMetaData's
|
|
||||||
going through the intermediary (intersection) table.
|
|
||||||
* `joinOns` - *List<JoinOn>, Required* - fields used to join the tables.
|
|
||||||
Note: In the 2-arg JoinOn constructor, the leftTable's field comes first.
|
|
||||||
Alternatively, the no-arg constructor can be used along with `.withLeftField().withRightField()`
|
|
||||||
* `orderBys` - *List<QFilterOrderBy>* - Optional list of order-by objects,
|
|
||||||
used in some framework-generated queries using the join.
|
|
||||||
The field names are assumed to come from the rightTable.
|
|
||||||
|
|
||||||
#TODO# what else do we need here?
|
|
||||||
|
@ -16,7 +16,6 @@ Processes are defined in a QQQ Instance in a `*QProcessMetaData*` object.
|
|||||||
In addition to directly building a `QProcessMetaData` object setting its properties directly, there are a few common process patterns that provide *Builder* objects for ease-of-use.
|
In addition to directly building a `QProcessMetaData` object setting its properties directly, there are a few common process patterns that provide *Builder* objects for ease-of-use.
|
||||||
See StreamedETLWithFrontendProcess below for a common example
|
See StreamedETLWithFrontendProcess below for a common example
|
||||||
|
|
||||||
[#_QProcessMetaData_Properties]
|
|
||||||
*QProcessMetaData Properties:*
|
*QProcessMetaData Properties:*
|
||||||
|
|
||||||
* `name` - *String, Required* - Unique name for the process within the QQQ Instance.
|
* `name` - *String, Required* - Unique name for the process within the QQQ Instance.
|
||||||
@ -31,13 +30,12 @@ See below for details.
|
|||||||
* `permissionRules` - *QPermissionRules object* - define the permission/access rules for the process.
|
* `permissionRules` - *QPermissionRules object* - define the permission/access rules for the process.
|
||||||
See {link-permissionRules} for details.
|
See {link-permissionRules} for details.
|
||||||
* `steps` and `stepList` - *Map of String → <<QStepMetaData>>* and *List of QStepMetaData* - Defines the <<QFrontendStepMetaData,screens>> and <<QBackendStepMetaData,backend code>> that makes up the process.
|
* `steps` and `stepList` - *Map of String → <<QStepMetaData>>* and *List of QStepMetaData* - Defines the <<QFrontendStepMetaData,screens>> and <<QBackendStepMetaData,backend code>> that makes up the process.
|
||||||
** `stepList` is the list of steps in the order that they will be executed
|
** `stepList` is the list of steps in the order that they will by default be executed.
|
||||||
(that is to say - this is the _default_ order of execution - but it can be customized - see <<_custom_process_flow>> for details).
|
** `steps` is a map, including all steps from `stepList`, but which may also include steps which can used by the process if its backend steps make the decision to do so, at run-time.
|
||||||
** `steps` is a map, including all steps from `stepList`, but which may also include steps which can used by the process if its backend steps make the decision to do so, at run-time (e.g., using <<_custom_process_flow>>).
|
|
||||||
** A process's steps are normally defined in one of two was:
|
** A process's steps are normally defined in one of two was:
|
||||||
*** 1) by a single call to `.withStepList(List<QStepMetaData>)`, which internally adds each step into the `steps` map.
|
*** 1) by a single call to `.withStepList(List<QStepMetaData>)`, which internally adds each step into the `steps` map.
|
||||||
*** 2) by multiple calls to `.addStep(QStepMetaData)`, which adds a step to both the `stepList` and `steps` map.
|
*** 2) by multiple calls to `.addStep(QStepMetaData)`, which adds a step to both the `stepList` and `steps` map.
|
||||||
** If a process also needs optional steps (for a <<_custom_process_flow>>), they should be added by a call to `.addOptionalStep(QStepMetaData)`, which only places them in the `steps` map.
|
** If a process also needs optional steps, they should be added by a call to `.addOptionalStep(QStepMetaData)`, which only places them in the `steps` map.
|
||||||
* `schedule` - *<<QScheduleMetaData>>* - set up the process to run automatically on the specified schedule.
|
* `schedule` - *<<QScheduleMetaData>>* - set up the process to run automatically on the specified schedule.
|
||||||
See below for details.
|
See below for details.
|
||||||
* `minInputRecords` - *Integer* - #not used...#
|
* `minInputRecords` - *Integer* - #not used...#
|
||||||
@ -216,112 +214,3 @@ But for some cases, doing page-level transactions can reduce long-transactions a
|
|||||||
* `withFields(List<QFieldMetaData> fieldList)` - Adds additional input fields to the preview step of the process.
|
* `withFields(List<QFieldMetaData> fieldList)` - Adds additional input fields to the preview step of the process.
|
||||||
* `withBasepullConfiguration(BasepullConfiguration basepullConfiguration)` - Add a <<BasepullConfiguration>> to the process.
|
* `withBasepullConfiguration(BasepullConfiguration basepullConfiguration)` - Add a <<BasepullConfiguration>> to the process.
|
||||||
* `withSchedule(QScheduleMetaData schedule)` - Add a <<QScheduleMetaData>> to the process.
|
* `withSchedule(QScheduleMetaData schedule)` - Add a <<QScheduleMetaData>> to the process.
|
||||||
|
|
||||||
[#_custom_process_flow]
|
|
||||||
==== Custom Process Flow
|
|
||||||
As referenced in the definition of the <<_QProcessMetaData_Properties,QProcessMetaData Properties>>, by default, a process
|
|
||||||
will execute each of its steps in-order, as defined in the `stepList` property.
|
|
||||||
However, a Backend Step can customize this flow #todo - write more clearly here...
|
|
||||||
|
|
||||||
There are generally 2 method to call (in a `BackendStep`) to do a dynamic flow:
|
|
||||||
|
|
||||||
* `RunBackendStepOutput.setOverrideLastStepName(String stepName)`
|
|
||||||
** QQQ's `RunProcessAction` keeps track of which step it "last" ran, e.g., to tell it which one to run next.
|
|
||||||
However, if a step sets the `OverrideLastStepName` property in its output object,
|
|
||||||
then the step named in that property becomes the effective "last" step,
|
|
||||||
thus determining which step comes next.
|
|
||||||
|
|
||||||
* `RunBackendStepOutput.updateStepList(List<String> stepNameList)`
|
|
||||||
** Calling this method changes the process's runtime definition of steps to be executed.
|
|
||||||
Thus allowing a completely custom flow.
|
|
||||||
It should be noted, that the "last" step name (as tracked by QQQ within `RunProcessAction`)
|
|
||||||
does need to be found in the new `stepNameList` - otherwise, the framework will not know where you were,
|
|
||||||
for figuring out where to go next.
|
|
||||||
|
|
||||||
[source,java]
|
|
||||||
.Example of a defining process that can use a flexible flow:
|
|
||||||
----
|
|
||||||
// for a case like this, it would be recommended to define all step names in constants:
|
|
||||||
public final static String STEP_START = "start";
|
|
||||||
public final static String STEP_A = "a";
|
|
||||||
public final static String STEP_B = "b";
|
|
||||||
public final static String STEP_C = "c";
|
|
||||||
public final static String STEP_1 = "1";
|
|
||||||
public final static String STEP_2 = "2";
|
|
||||||
public final static String STEP_3 = "3";
|
|
||||||
public final static String STEP_END = "end";
|
|
||||||
|
|
||||||
// also, to define the possible flows (lists of steps) in constants as well:
|
|
||||||
public final static List<String> LETTERS_STEP_LIST = List.of(
|
|
||||||
STEP_START, STEP_A, STEP_B, STEP_C, STEP_END);
|
|
||||||
|
|
||||||
public final static List<String> NUMBERS_STEP_LIST = List.of(
|
|
||||||
STEP_START, STEP_1, STEP_2, STEP_3, STEP_END);
|
|
||||||
|
|
||||||
// when we define the process's meta-data, we only give a "skeleton" stepList -
|
|
||||||
// we must at least have our starting step, and we may want at least one frontend step
|
|
||||||
// for the UI to show some placeholder(s):
|
|
||||||
QProcessMetaData process = new QProcessMetaData()
|
|
||||||
.withName(PROCESS_NAME)
|
|
||||||
.withStepList(List.of(
|
|
||||||
new QBackendStepMetaData().withName(STEP_START)
|
|
||||||
.withCode(new QCodeReference(/*...*/)),
|
|
||||||
new QFrontendStepMetaData()
|
|
||||||
.withName(STEP_END)
|
|
||||||
));
|
|
||||||
|
|
||||||
// the additional steps get added via `addOptionalStep`, which only puts them in
|
|
||||||
// the process's stepMap, not its stepList!
|
|
||||||
process.addOptionalStep(new QFrontendStepMetaData().withName(STEP_A));
|
|
||||||
process.addOptionalStep(new QBackendStepMetaData().withName(STEP_B)
|
|
||||||
.withCode(new QCodeReference(/*...*/)));
|
|
||||||
process.addOptionalStep(new QFrontendStepMetaData().withName(STEP_C));
|
|
||||||
|
|
||||||
process.addOptionalStep(new QBackendStepMetaData().withName(STEP_1)
|
|
||||||
.withCode(new QCodeReference(/*...*/)));
|
|
||||||
process.addOptionalStep(new QFrontendStepMetaData().withName(STEP_2));
|
|
||||||
process.addOptionalStep(new QBackendStepMetaData().withName(STEP_3)
|
|
||||||
.withCode(new QCodeReference(/*...*/)));
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
[source,java]
|
|
||||||
.Example of a process backend step adjusting the process's runtime flow:
|
|
||||||
----
|
|
||||||
/***************************************************************************
|
|
||||||
** look at the value named "which". if it's "letters", then make the process
|
|
||||||
** go through the stepList consisting of letters; else, update the step list
|
|
||||||
** to be the "numbers" steps.
|
|
||||||
**
|
|
||||||
** Also - if the "skipSomeSteps" value is give as true, then set the
|
|
||||||
** overrideLastStepName to skip again (in the letters case, skip past A, B
|
|
||||||
** and C; in the numbers case, skip past 1 and 2).
|
|
||||||
***************************************************************************/
|
|
||||||
public static class StartStep implements BackendStep
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
|
||||||
{
|
|
||||||
Boolean skipSomeSteps = runBackendStepInput.getValueBoolean("skipSomeSteps");
|
|
||||||
|
|
||||||
if(runBackendStepInput.getValueString("which").equals("letters"))
|
|
||||||
{
|
|
||||||
runBackendStepOutput.updateStepList(LETTERS_STEP_LIST);
|
|
||||||
if(BooleanUtils.isTrue(skipSomeSteps))
|
|
||||||
{
|
|
||||||
runBackendStepOutput.setOverrideLastStepName(STEP_C);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
runBackendStepOutput.updateStepList(NUMBERS_STEP_LIST);
|
|
||||||
if(BooleanUtils.isTrue(skipSomeSteps))
|
|
||||||
{
|
|
||||||
runBackendStepOutput.setOverrideLastStepName(STEP_2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,7 +29,6 @@ import java.util.List;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
import com.kingsrook.qqq.backend.core.instances.QMetaDataVariableInterpreter;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
@ -42,18 +41,8 @@ import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class JoinGraph
|
public class JoinGraph
|
||||||
{
|
{
|
||||||
private Set<Edge> edges = new HashSet<>();
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
private Set<Edge> edges = new HashSet<>();
|
||||||
// as an instance grows, with the number of joins (say, more than 50?), especially as they may have a lot of connections, //
|
|
||||||
// it can become very very slow to process a full join graph (e.g., 10 seconds, maybe much worse, per Big-O...) //
|
|
||||||
// also, it's not frequently useful to look at a join path that's more than a handful of tables long. //
|
|
||||||
// thus - this property exists - to limit the max length of a join path. Keeping it small keeps instance enrichment //
|
|
||||||
// and validation reasonably performant, at the possible cost of, some join-path that's longer than this limit may not //
|
|
||||||
// be found - but - chances are, you don't want some 12-element join path to be used anyway, thus, this makes sense. //
|
|
||||||
// but - it can be adjusted, per system property or ENV var. //
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
private int maxPathLength = new QMetaDataVariableInterpreter().getIntegerFromPropertyOrEnvironment("qqq.instance.joinGraph.maxPathLength", "QQQ_INSTANCE_JOIN_GRAPH_MAX_PATH_LENGTH", 3);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -314,13 +303,6 @@ public class JoinGraph
|
|||||||
|
|
||||||
if(otherTableName != null)
|
if(otherTableName != null)
|
||||||
{
|
{
|
||||||
if(newPath.size() > maxPathLength)
|
|
||||||
{
|
|
||||||
////////////////////////////////////////////////////////////////
|
|
||||||
// performance hack. see comment at maxPathLength definition //
|
|
||||||
////////////////////////////////////////////////////////////////
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
JoinConnectionList newConnectionList = connectionList.copy();
|
JoinConnectionList newConnectionList = connectionList.copy();
|
||||||
JoinConnection joinConnection = new JoinConnection(otherTableName, edge.joinName);
|
JoinConnection joinConnection = new JoinConnection(otherTableName, edge.joinName);
|
||||||
|
@ -190,25 +190,7 @@ public class RunProcessAction
|
|||||||
// Run backend steps //
|
// Run backend steps //
|
||||||
///////////////////////
|
///////////////////////
|
||||||
LOG.debug("Running backend step [" + step.getName() + "] in process [" + process.getName() + "]");
|
LOG.debug("Running backend step [" + step.getName() + "] in process [" + process.getName() + "]");
|
||||||
RunBackendStepOutput runBackendStepOutput = runBackendStep(runProcessInput, process, runProcessOutput, stateKey, backendStepMetaData, process, processState);
|
runBackendStep(runProcessInput, process, runProcessOutput, stateKey, backendStepMetaData, process, processState);
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// if the step returned an override lastStepName, use that to determine how we proceed //
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
if(runBackendStepOutput.getOverrideLastStepName() != null)
|
|
||||||
{
|
|
||||||
LOG.debug("Process step [" + lastStepName + "] returned an overrideLastStepName [" + runBackendStepOutput.getOverrideLastStepName() + "]!");
|
|
||||||
lastStepName = runBackendStepOutput.getOverrideLastStepName();
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// similarly, if the step produced an updatedFrontendStepList, propagate that data outward //
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
if(runBackendStepOutput.getUpdatedFrontendStepList() != null)
|
|
||||||
{
|
|
||||||
LOG.debug("Process step [" + lastStepName + "] generated an updatedFrontendStepList [" + runBackendStepOutput.getUpdatedFrontendStepList().stream().map(s -> s.getName()).toList() + "]!");
|
|
||||||
runProcessOutput.setUpdatedFrontendStepList(runBackendStepOutput.getUpdatedFrontendStepList());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -357,7 +339,7 @@ public class RunProcessAction
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Run a single backend step.
|
** Run a single backend step.
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private RunBackendStepOutput runBackendStep(RunProcessInput runProcessInput, QProcessMetaData process, RunProcessOutput runProcessOutput, UUIDAndTypeStateKey stateKey, QBackendStepMetaData backendStep, QProcessMetaData qProcessMetaData, ProcessState processState) throws Exception
|
private void runBackendStep(RunProcessInput runProcessInput, QProcessMetaData process, RunProcessOutput runProcessOutput, UUIDAndTypeStateKey stateKey, QBackendStepMetaData backendStep, QProcessMetaData qProcessMetaData, ProcessState processState) throws Exception
|
||||||
{
|
{
|
||||||
RunBackendStepInput runBackendStepInput = new RunBackendStepInput(processState);
|
RunBackendStepInput runBackendStepInput = new RunBackendStepInput(processState);
|
||||||
runBackendStepInput.setProcessName(process.getName());
|
runBackendStepInput.setProcessName(process.getName());
|
||||||
@ -386,16 +368,14 @@ public class RunProcessAction
|
|||||||
runBackendStepInput.setBasepullLastRunTime((Instant) runProcessInput.getValues().get(BASEPULL_LAST_RUNTIME_KEY));
|
runBackendStepInput.setBasepullLastRunTime((Instant) runProcessInput.getValues().get(BASEPULL_LAST_RUNTIME_KEY));
|
||||||
}
|
}
|
||||||
|
|
||||||
RunBackendStepOutput runBackendStepOutput = new RunBackendStepAction().execute(runBackendStepInput);
|
RunBackendStepOutput lastFunctionResult = new RunBackendStepAction().execute(runBackendStepInput);
|
||||||
storeState(stateKey, runBackendStepOutput.getProcessState());
|
storeState(stateKey, lastFunctionResult.getProcessState());
|
||||||
|
|
||||||
if(runBackendStepOutput.getException() != null)
|
if(lastFunctionResult.getException() != null)
|
||||||
{
|
{
|
||||||
runProcessOutput.setException(runBackendStepOutput.getException());
|
runProcessOutput.setException(lastFunctionResult.getException());
|
||||||
throw (runBackendStepOutput.getException());
|
throw (lastFunctionResult.getException());
|
||||||
}
|
}
|
||||||
|
|
||||||
return (runBackendStepOutput);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -515,15 +495,15 @@ public class RunProcessAction
|
|||||||
String basepullKeyValue = (basepullConfiguration.getKeyValue() != null) ? basepullConfiguration.getKeyValue() : process.getName();
|
String basepullKeyValue = (basepullConfiguration.getKeyValue() != null) ? basepullConfiguration.getKeyValue() : process.getName();
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// if process specifies that it uses variants, look for that data in the session and append to our basepull key //
|
// if backend specifies that it uses variants, look for that data in the session and append to our basepull key //
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
if(process.getVariantBackend() != null)
|
if(process.getSchedule() != null && process.getVariantBackend() != null)
|
||||||
{
|
{
|
||||||
QSession session = QContext.getQSession();
|
QSession session = QContext.getQSession();
|
||||||
QBackendMetaData backendMetaData = QContext.getQInstance().getBackend(process.getVariantBackend());
|
QBackendMetaData backendMetaData = QContext.getQInstance().getBackend(process.getVariantBackend());
|
||||||
if(session.getBackendVariants() == null || !session.getBackendVariants().containsKey(backendMetaData.getVariantOptionsTableTypeValue()))
|
if(session.getBackendVariants() == null || !session.getBackendVariants().containsKey(backendMetaData.getVariantOptionsTableTypeValue()))
|
||||||
{
|
{
|
||||||
LOG.warn("Could not find Backend Variant information for Backend '" + backendMetaData.getName() + "'");
|
LOG.info("Could not find Backend Variant information for Backend '" + backendMetaData.getName() + "'");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -157,17 +157,6 @@ public class ReplaceAction extends AbstractQActionFunction<ReplaceInput, Replace
|
|||||||
output.setDeleteOutput(deleteOutput);
|
output.setDeleteOutput(deleteOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(input.getSetPrimaryKeyInInsertedRecords())
|
|
||||||
{
|
|
||||||
for(int i = 0; i < insertList.size(); i++)
|
|
||||||
{
|
|
||||||
if(i < insertOutput.getRecords().size())
|
|
||||||
{
|
|
||||||
insertList.get(i).setValue(primaryKeyField, insertOutput.getRecords().get(i).getValue(primaryKeyField));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(weOwnTheTransaction)
|
if(weOwnTheTransaction)
|
||||||
{
|
{
|
||||||
transaction.commit();
|
transaction.commit();
|
||||||
|
@ -79,7 +79,7 @@ public class LogPair
|
|||||||
}
|
}
|
||||||
else if(value instanceof LogPair[] subLogPairs)
|
else if(value instanceof LogPair[] subLogPairs)
|
||||||
{
|
{
|
||||||
String subLogPairsString = Arrays.stream(subLogPairs).filter(Objects::nonNull).map(LogPair::toString).collect(Collectors.joining(","));
|
String subLogPairsString = Arrays.stream(subLogPairs).map(LogPair::toString).collect(Collectors.joining(","));
|
||||||
valueString = '{' + subLogPairsString + '}';
|
valueString = '{' + subLogPairsString + '}';
|
||||||
}
|
}
|
||||||
else if(value instanceof UnsafeSupplier<?, ?> us)
|
else if(value instanceof UnsafeSupplier<?, ?> us)
|
||||||
|
@ -22,7 +22,6 @@
|
|||||||
package com.kingsrook.qqq.backend.core.logging;
|
package com.kingsrook.qqq.backend.core.logging;
|
||||||
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -148,28 +147,6 @@ public class QLogger
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
public <T extends Throwable> T warnAndThrow(T t, LogPair... logPairs) throws T
|
|
||||||
{
|
|
||||||
warn(t.getMessage(), t, logPairs);
|
|
||||||
throw (t);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
public <T extends Throwable> T errorAndThrow(T t, LogPair... logPairs) throws T
|
|
||||||
{
|
|
||||||
error(t.getMessage(), t, logPairs);
|
|
||||||
throw (t);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -618,10 +595,7 @@ public class QLogger
|
|||||||
{
|
{
|
||||||
user = session.getUser().getIdReference();
|
user = session.getUser().getIdReference();
|
||||||
}
|
}
|
||||||
|
sessionLogPair = logPair("session", logPair("id", session.getUuid()), logPair("user", user));
|
||||||
LogPair variantsLogPair = getVariantsLogPair(session);
|
|
||||||
|
|
||||||
sessionLogPair = logPair("session", logPair("id", session.getUuid()), logPair("user", user), variantsLogPair);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -641,38 +615,6 @@ public class QLogger
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
private static LogPair getVariantsLogPair(QSession session)
|
|
||||||
{
|
|
||||||
LogPair variantsLogPair = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if(session.getBackendVariants() != null)
|
|
||||||
{
|
|
||||||
LogPair[] variants = new LogPair[session.getBackendVariants().size()];
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
for(Map.Entry<String, Serializable> entry : session.getBackendVariants().entrySet())
|
|
||||||
{
|
|
||||||
variants[i] = new LogPair(entry.getKey(), entry.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
variantsLogPair = new LogPair("variants", variants);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
////////////////
|
|
||||||
// leave null //
|
|
||||||
////////////////
|
|
||||||
}
|
|
||||||
return variantsLogPair;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -27,14 +27,10 @@ import java.math.BigDecimal;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QRuntimeException;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.audits.AuditInput;
|
import com.kingsrook.qqq.backend.core.model.actions.audits.AuditInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.audits.AuditSingleInput;
|
import com.kingsrook.qqq.backend.core.model.actions.audits.AuditSingleInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
|
||||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||||
|
|
||||||
|
|
||||||
@ -44,14 +40,9 @@ import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class RunBackendStepOutput extends AbstractActionOutput implements Serializable
|
public class RunBackendStepOutput extends AbstractActionOutput implements Serializable
|
||||||
{
|
{
|
||||||
private String processName;
|
|
||||||
|
|
||||||
private ProcessState processState;
|
private ProcessState processState;
|
||||||
private Exception exception; // todo - make optional
|
private Exception exception; // todo - make optional
|
||||||
|
|
||||||
private String overrideLastStepName;
|
|
||||||
private List<QFrontendStepMetaData> updatedFrontendStepList = null;
|
|
||||||
|
|
||||||
private List<AuditInput> auditInputList = new ArrayList<>();
|
private List<AuditInput> auditInputList = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
@ -87,7 +78,6 @@ public class RunBackendStepOutput extends AbstractActionOutput implements Serial
|
|||||||
public void seedFromRequest(RunBackendStepInput runBackendStepInput)
|
public void seedFromRequest(RunBackendStepInput runBackendStepInput)
|
||||||
{
|
{
|
||||||
this.processState = runBackendStepInput.getProcessState();
|
this.processState = runBackendStepInput.getProcessState();
|
||||||
this.processName = runBackendStepInput.getProcessName();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -322,122 +312,4 @@ public class RunBackendStepOutput extends AbstractActionOutput implements Serial
|
|||||||
auditInput.addAuditSingleInput(auditSingleInput);
|
auditInput.addAuditSingleInput(auditSingleInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for overrideLastStepName
|
|
||||||
*******************************************************************************/
|
|
||||||
public String getOverrideLastStepName()
|
|
||||||
{
|
|
||||||
return (this.overrideLastStepName);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for overrideLastStepName
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setOverrideLastStepName(String overrideLastStepName)
|
|
||||||
{
|
|
||||||
this.overrideLastStepName = overrideLastStepName;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for overrideLastStepName
|
|
||||||
*******************************************************************************/
|
|
||||||
public RunBackendStepOutput withOverrideLastStepName(String overrideLastStepName)
|
|
||||||
{
|
|
||||||
this.overrideLastStepName = overrideLastStepName;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
public void updateStepList(List<String> stepList)
|
|
||||||
{
|
|
||||||
getProcessState().setStepList(stepList);
|
|
||||||
|
|
||||||
if(processName == null)
|
|
||||||
{
|
|
||||||
throw (new QRuntimeException("ProcessName was not set in this object, therefore updateStepList cannot complete successfully. Try to manually call setProcessName as a work around."));
|
|
||||||
}
|
|
||||||
|
|
||||||
QProcessMetaData processMetaData = QContext.getQInstance().getProcess(processName);
|
|
||||||
|
|
||||||
ArrayList<QFrontendStepMetaData> updatedFrontendStepList = new ArrayList<>(stepList.stream()
|
|
||||||
.map(name -> processMetaData.getStep(name))
|
|
||||||
.filter(step -> step instanceof QFrontendStepMetaData)
|
|
||||||
.map(step -> (QFrontendStepMetaData) step)
|
|
||||||
.toList());
|
|
||||||
|
|
||||||
setUpdatedFrontendStepList(updatedFrontendStepList);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for processName
|
|
||||||
*******************************************************************************/
|
|
||||||
public String getProcessName()
|
|
||||||
{
|
|
||||||
return (this.processName);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for processName
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setProcessName(String processName)
|
|
||||||
{
|
|
||||||
this.processName = processName;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for processName
|
|
||||||
*******************************************************************************/
|
|
||||||
public RunBackendStepOutput withProcessName(String processName)
|
|
||||||
{
|
|
||||||
this.processName = processName;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for updatedFrontendStepList
|
|
||||||
*******************************************************************************/
|
|
||||||
public List<QFrontendStepMetaData> getUpdatedFrontendStepList()
|
|
||||||
{
|
|
||||||
return (this.updatedFrontendStepList);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for updatedFrontendStepList
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setUpdatedFrontendStepList(List<QFrontendStepMetaData> updatedFrontendStepList)
|
|
||||||
{
|
|
||||||
this.updatedFrontendStepList = updatedFrontendStepList;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for updatedFrontendStepList
|
|
||||||
*******************************************************************************/
|
|
||||||
public RunBackendStepOutput withUpdatedFrontendStepList(List<QFrontendStepMetaData> updatedFrontendStepList)
|
|
||||||
{
|
|
||||||
this.updatedFrontendStepList = updatedFrontendStepList;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,6 @@ import java.util.Map;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
|
|
||||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||||
|
|
||||||
|
|
||||||
@ -46,8 +45,6 @@ public class RunProcessOutput extends AbstractActionOutput implements Serializab
|
|||||||
private String processUUID;
|
private String processUUID;
|
||||||
private Optional<Exception> exception = Optional.empty();
|
private Optional<Exception> exception = Optional.empty();
|
||||||
|
|
||||||
private List<QFrontendStepMetaData> updatedFrontendStepList = null;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -330,36 +327,4 @@ public class RunProcessOutput extends AbstractActionOutput implements Serializab
|
|||||||
{
|
{
|
||||||
return exception;
|
return exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for updatedFrontendStepList
|
|
||||||
*******************************************************************************/
|
|
||||||
public List<QFrontendStepMetaData> getUpdatedFrontendStepList()
|
|
||||||
{
|
|
||||||
return (this.updatedFrontendStepList);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for updatedFrontendStepList
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setUpdatedFrontendStepList(List<QFrontendStepMetaData> updatedFrontendStepList)
|
|
||||||
{
|
|
||||||
this.updatedFrontendStepList = updatedFrontendStepList;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for updatedFrontendStepList
|
|
||||||
*******************************************************************************/
|
|
||||||
public RunProcessOutput withUpdatedFrontendStepList(List<QFrontendStepMetaData> updatedFrontendStepList)
|
|
||||||
{
|
|
||||||
this.updatedFrontendStepList = updatedFrontendStepList;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -112,7 +112,7 @@ public class InsertInput extends AbstractTableActionInput
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public InsertInput withRecordEntities(List<? extends QRecordEntity> recordEntityList)
|
public InsertInput withRecordEntities(List<QRecordEntity> recordEntityList)
|
||||||
{
|
{
|
||||||
for(QRecordEntity recordEntity : CollectionUtils.nonNullList(recordEntityList))
|
for(QRecordEntity recordEntity : CollectionUtils.nonNullList(recordEntityList))
|
||||||
{
|
{
|
||||||
|
@ -25,7 +25,6 @@ package com.kingsrook.qqq.backend.core.model.actions.tables.query;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
||||||
@ -38,7 +37,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.QueryOrGetInputInterf
|
|||||||
** Input data for the Query action
|
** Input data for the Query action
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class QueryInput extends AbstractTableActionInput implements QueryOrGetInputInterface, Cloneable
|
public class QueryInput extends AbstractTableActionInput implements QueryOrGetInputInterface
|
||||||
{
|
{
|
||||||
private QBackendTransaction transaction;
|
private QBackendTransaction transaction;
|
||||||
private QQueryFilter filter;
|
private QQueryFilter filter;
|
||||||
@ -110,40 +109,6 @@ public class QueryInput extends AbstractTableActionInput implements QueryOrGetIn
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
@Override
|
|
||||||
public QueryInput clone() throws CloneNotSupportedException
|
|
||||||
{
|
|
||||||
QueryInput clone = (QueryInput) super.clone();
|
|
||||||
|
|
||||||
if(fieldsToTranslatePossibleValues != null)
|
|
||||||
{
|
|
||||||
clone.fieldsToTranslatePossibleValues = new HashSet<>(fieldsToTranslatePossibleValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(queryJoins != null)
|
|
||||||
{
|
|
||||||
clone.queryJoins = new ArrayList<>(queryJoins);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(clone.associationNamesToInclude != null)
|
|
||||||
{
|
|
||||||
clone.associationNamesToInclude = new HashSet<>(associationNamesToInclude);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(queryHints != null)
|
|
||||||
{
|
|
||||||
clone.queryHints = EnumSet.noneOf(QueryHint.class);
|
|
||||||
clone.queryHints.addAll(queryHints);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (clone);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for filter
|
** Getter for filter
|
||||||
**
|
**
|
||||||
|
@ -39,9 +39,8 @@ public class ReplaceInput extends AbstractTableActionInput
|
|||||||
private UniqueKey key;
|
private UniqueKey key;
|
||||||
private List<QRecord> records;
|
private List<QRecord> records;
|
||||||
private QQueryFilter filter;
|
private QQueryFilter filter;
|
||||||
private boolean performDeletes = true;
|
private boolean performDeletes = true;
|
||||||
private boolean allowNullKeyValuesToEqual = false;
|
private boolean allowNullKeyValuesToEqual = false;
|
||||||
private boolean setPrimaryKeyInInsertedRecords = false;
|
|
||||||
|
|
||||||
private boolean omitDmlAudit = false;
|
private boolean omitDmlAudit = false;
|
||||||
|
|
||||||
@ -272,35 +271,4 @@ public class ReplaceInput extends AbstractTableActionInput
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for setPrimaryKeyInInsertedRecords
|
|
||||||
*******************************************************************************/
|
|
||||||
public boolean getSetPrimaryKeyInInsertedRecords()
|
|
||||||
{
|
|
||||||
return (this.setPrimaryKeyInInsertedRecords);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for setPrimaryKeyInInsertedRecords
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setSetPrimaryKeyInInsertedRecords(boolean setPrimaryKeyInInsertedRecords)
|
|
||||||
{
|
|
||||||
this.setPrimaryKeyInInsertedRecords = setPrimaryKeyInInsertedRecords;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for setPrimaryKeyInInsertedRecords
|
|
||||||
*******************************************************************************/
|
|
||||||
public ReplaceInput withSetPrimaryKeyInInsertedRecords(boolean setPrimaryKeyInInsertedRecords)
|
|
||||||
{
|
|
||||||
this.setPrimaryKeyInInsertedRecords = setPrimaryKeyInInsertedRecords;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -120,7 +120,7 @@ public class UpdateInput extends AbstractTableActionInput
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public UpdateInput withRecordEntities(List<? extends QRecordEntity> recordEntityList)
|
public UpdateInput withRecordEntities(List<QRecordEntity> recordEntityList)
|
||||||
{
|
{
|
||||||
for(QRecordEntity recordEntity : CollectionUtils.nonNullList(recordEntityList))
|
for(QRecordEntity recordEntity : CollectionUtils.nonNullList(recordEntityList))
|
||||||
{
|
{
|
||||||
|
@ -54,6 +54,7 @@ public class CompositeWidgetData extends AbstractBlockWidgetData<CompositeWidget
|
|||||||
/////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////
|
||||||
// note, these are used in QQQ FMD CompositeWidgetData.tsx //
|
// note, these are used in QQQ FMD CompositeWidgetData.tsx //
|
||||||
/////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////
|
||||||
|
FLEX_COLUMN,
|
||||||
FLEX_ROW_WRAPPED,
|
FLEX_ROW_WRAPPED,
|
||||||
FLEX_ROW_SPACE_BETWEEN,
|
FLEX_ROW_SPACE_BETWEEN,
|
||||||
TABLE_SUB_ROW_DETAILS,
|
TABLE_SUB_ROW_DETAILS,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* QQQ - Low-code Application Framework for Engineers.
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
* Copyright (C) 2021-2024. Kingsrook, LLC
|
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||||
* contact@kingsrook.com
|
* contact@kingsrook.com
|
||||||
* https://github.com/Kingsrook/
|
* https://github.com/Kingsrook/
|
||||||
@ -19,95 +19,78 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.kingsrook.qqq.backend.module.api.exceptions;
|
package com.kingsrook.qqq.backend.core.model.dashboard.widgets;
|
||||||
|
|
||||||
|
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import java.util.List;
|
||||||
import com.kingsrook.qqq.backend.module.api.actions.QHttpResponse;
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Exception thrown when an API HTTP request failed due to a bad status code.
|
** Model containing datastructure expected by frontend bar chart widget
|
||||||
** This exception includes the status code as a field, as well as the full
|
**
|
||||||
** response object.
|
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class QBadHttpResponseStatusException extends QException
|
public class MultiTableData extends QWidgetData
|
||||||
{
|
{
|
||||||
private int statusCode;
|
List<TableData> tableDataList;
|
||||||
private QHttpResponse response;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QBadHttpResponseStatusException(String message, QHttpResponse response)
|
public MultiTableData()
|
||||||
{
|
{
|
||||||
super(message);
|
|
||||||
|
|
||||||
this.statusCode = response.getStatusCode();
|
|
||||||
this.response = response;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for statusCode
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public int getStatusCode()
|
public MultiTableData(List<TableData> tableDataList)
|
||||||
{
|
{
|
||||||
return (this.statusCode);
|
setTableDataList(tableDataList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Setter for statusCode
|
** Getter for type
|
||||||
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public void setStatusCode(int statusCode)
|
public String getType()
|
||||||
{
|
{
|
||||||
this.statusCode = statusCode;
|
return WidgetType.MULTI_TABLE.getType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Fluent setter for statusCode
|
** Getter for tableDataList
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QBadHttpResponseStatusException withStatusCode(int statusCode)
|
public List<TableData> getTableDataList()
|
||||||
{
|
{
|
||||||
this.statusCode = statusCode;
|
return (this.tableDataList);
|
||||||
return (this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for response
|
** Setter for tableDataList
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QHttpResponse getResponse()
|
public void setTableDataList(List<TableData> tableDataList)
|
||||||
{
|
{
|
||||||
return (this.response);
|
this.tableDataList = tableDataList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Setter for response
|
** Fluent setter for tableDataList
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public void setResponse(QHttpResponse response)
|
public MultiTableData withTableDataList(List<TableData> tableDataList)
|
||||||
{
|
{
|
||||||
this.response = response;
|
this.tableDataList = tableDataList;
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for response
|
|
||||||
*******************************************************************************/
|
|
||||||
public QBadHttpResponseStatusException withResponse(QHttpResponse response)
|
|
||||||
{
|
|
||||||
this.response = response;
|
|
||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
@ -42,6 +42,7 @@ public enum WidgetType
|
|||||||
SMALL_LINE_CHART("smallLineChart"),
|
SMALL_LINE_CHART("smallLineChart"),
|
||||||
LOCATION("location"),
|
LOCATION("location"),
|
||||||
MULTI_STATISTICS("multiStatistics"),
|
MULTI_STATISTICS("multiStatistics"),
|
||||||
|
MULTI_TABLE("multiTable"),
|
||||||
PIE_CHART("pieChart"),
|
PIE_CHART("pieChart"),
|
||||||
QUICK_SIGHT_CHART("quickSightChart"),
|
QUICK_SIGHT_CHART("quickSightChart"),
|
||||||
STATISTICS("statistics"),
|
STATISTICS("statistics"),
|
||||||
|
@ -30,7 +30,6 @@ import java.util.Comparator;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import com.google.common.reflect.ClassPath;
|
import com.google.common.reflect.ClassPath;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
@ -51,8 +50,6 @@ public class MetaDataProducerHelper
|
|||||||
private static Map<Class<?>, Integer> comparatorValuesByType = new HashMap<>();
|
private static Map<Class<?>, Integer> comparatorValuesByType = new HashMap<>();
|
||||||
private static Integer defaultComparatorValue;
|
private static Integer defaultComparatorValue;
|
||||||
|
|
||||||
private static ImmutableSet<ClassPath.ClassInfo> topLevelClasses;
|
|
||||||
|
|
||||||
static
|
static
|
||||||
{
|
{
|
||||||
////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -73,6 +70,8 @@ public class MetaDataProducerHelper
|
|||||||
comparatorValuesByType.put(QAppMetaData.class, 23);
|
comparatorValuesByType.put(QAppMetaData.class, 23);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Recursively find all classes in the given package, that implement MetaDataProducerInterface
|
** Recursively find all classes in the given package, that implement MetaDataProducerInterface
|
||||||
** run them, and add their output to the given qInstance.
|
** run them, and add their output to the given qInstance.
|
||||||
@ -187,7 +186,7 @@ public class MetaDataProducerHelper
|
|||||||
List<Class<?>> classes = new ArrayList<>();
|
List<Class<?>> classes = new ArrayList<>();
|
||||||
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
||||||
|
|
||||||
for(ClassPath.ClassInfo info : getTopLevelClasses(loader))
|
for(ClassPath.ClassInfo info : ClassPath.from(loader).getTopLevelClasses())
|
||||||
{
|
{
|
||||||
if(info.getName().startsWith(packageName))
|
if(info.getName().startsWith(packageName))
|
||||||
{
|
{
|
||||||
@ -198,29 +197,4 @@ public class MetaDataProducerHelper
|
|||||||
return (classes);
|
return (classes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
private static ImmutableSet<ClassPath.ClassInfo> getTopLevelClasses(ClassLoader loader) throws IOException
|
|
||||||
{
|
|
||||||
if(topLevelClasses == null)
|
|
||||||
{
|
|
||||||
topLevelClasses = ClassPath.from(loader).getTopLevelClasses();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (topLevelClasses);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
public static void clearTopLevelClassCache()
|
|
||||||
{
|
|
||||||
topLevelClasses = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -736,14 +736,4 @@ public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissi
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for the full map of all steps (not the step list!)
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
public Map<String, QStepMetaData> getAllSteps()
|
|
||||||
{
|
|
||||||
return steps;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,6 @@
|
|||||||
package com.kingsrook.qqq.backend.core;
|
package com.kingsrook.qqq.backend.core;
|
||||||
|
|
||||||
|
|
||||||
import java.time.ZoneId;
|
|
||||||
import java.util.TimeZone;
|
|
||||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
@ -44,10 +42,7 @@ public class BaseTest
|
|||||||
|
|
||||||
public static final String DEFAULT_USER_ID = "001";
|
public static final String DEFAULT_USER_ID = "001";
|
||||||
|
|
||||||
static
|
|
||||||
{
|
|
||||||
TimeZone.setDefault(TimeZone.getTimeZone(ZoneId.of("UTC")));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
|
@ -1,211 +0,0 @@
|
|||||||
/*
|
|
||||||
* QQQ - Low-code Application Framework for Engineers.
|
|
||||||
* Copyright (C) 2021-2024. Kingsrook, LLC
|
|
||||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
|
||||||
* contact@kingsrook.com
|
|
||||||
* https://github.com/Kingsrook/
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.kingsrook.qqq.backend.core.actions.processes;
|
|
||||||
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
|
||||||
import com.kingsrook.qqq.backend.core.actions.processes.RunProcessTest.NoopBackendStep;
|
|
||||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.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.model.metadata.code.QCodeReference;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
|
||||||
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
|
||||||
import org.apache.commons.lang.BooleanUtils;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
public class RunProcessUpdateStepListTest extends BaseTest
|
|
||||||
{
|
|
||||||
private static final String PROCESS_NAME = RunProcessUpdateStepListTest.class.getSimpleName();
|
|
||||||
|
|
||||||
private final static String STEP_START = "start";
|
|
||||||
private final static String STEP_A = "a";
|
|
||||||
private final static String STEP_B = "b";
|
|
||||||
private final static String STEP_C = "c";
|
|
||||||
private final static String STEP_1 = "1";
|
|
||||||
private final static String STEP_2 = "2";
|
|
||||||
private final static String STEP_3 = "3";
|
|
||||||
private final static String STEP_END = "end";
|
|
||||||
|
|
||||||
private final static List<String> LETTERS_STEP_LIST = List.of(
|
|
||||||
STEP_START,
|
|
||||||
STEP_A,
|
|
||||||
STEP_B,
|
|
||||||
STEP_C,
|
|
||||||
STEP_END
|
|
||||||
);
|
|
||||||
|
|
||||||
private final static List<String> NUMBERS_STEP_LIST = List.of(
|
|
||||||
STEP_START,
|
|
||||||
STEP_1,
|
|
||||||
STEP_2,
|
|
||||||
STEP_3,
|
|
||||||
STEP_END
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
@Test
|
|
||||||
void testGoingLettersPath() throws QException
|
|
||||||
{
|
|
||||||
QContext.getQInstance().addProcess(defineProcess());
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
// start the process, telling it to go the "letters" path //
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
RunProcessInput runProcessInput = new RunProcessInput();
|
|
||||||
runProcessInput.setProcessName(PROCESS_NAME);
|
|
||||||
runProcessInput.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.BREAK);
|
|
||||||
runProcessInput.setValues(MapBuilder.of("which", "letters"));
|
|
||||||
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// assert that we got back the next-step name of A, and the updated list of frontend steps (A, C) //
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
Optional<String> nextStepName = runProcessOutput.getProcessState().getNextStepName();
|
|
||||||
assertTrue(nextStepName.isPresent());
|
|
||||||
assertEquals(STEP_A, nextStepName.get());
|
|
||||||
assertEquals(List.of(STEP_A, STEP_C, STEP_END), runProcessOutput.getUpdatedFrontendStepList().stream().map(s -> s.getName()).toList());
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
// resume the process after that frontend step //
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
runProcessInput.setProcessUUID(runProcessOutput.getProcessUUID());
|
|
||||||
runProcessInput.setStartAfterStep(nextStepName.get());
|
|
||||||
runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// assert we got back C as the next-step now, and no updated frontend list this time //
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
nextStepName = runProcessOutput.getProcessState().getNextStepName();
|
|
||||||
assertTrue(nextStepName.isPresent());
|
|
||||||
assertEquals(STEP_C, nextStepName.get());
|
|
||||||
assertNull(runProcessOutput.getUpdatedFrontendStepList());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
@Test
|
|
||||||
void testGoingNumbersPathAndSkippingAhead() throws QException
|
|
||||||
{
|
|
||||||
QContext.getQInstance().addProcess(defineProcess());
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// start the process, telling it to go the "numbers" path, and to skip ahead some //
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
RunProcessInput runProcessInput = new RunProcessInput();
|
|
||||||
runProcessInput.setProcessName(PROCESS_NAME);
|
|
||||||
runProcessInput.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.BREAK);
|
|
||||||
runProcessInput.setValues(MapBuilder.of("which", "numbers", "skipSomeSteps", true));
|
|
||||||
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// assert that we got back the next-step name of 2, and the updated list of frontend steps (1, 3) //
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
Optional<String> nextStepName = runProcessOutput.getProcessState().getNextStepName();
|
|
||||||
assertTrue(nextStepName.isPresent());
|
|
||||||
assertEquals(STEP_END, nextStepName.get());
|
|
||||||
assertEquals(List.of(STEP_2, STEP_END), runProcessOutput.getUpdatedFrontendStepList().stream().map(s -> s.getName()).toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
public QProcessMetaData defineProcess()
|
|
||||||
{
|
|
||||||
QProcessMetaData process = new QProcessMetaData()
|
|
||||||
.withName(PROCESS_NAME)
|
|
||||||
.withStepList(List.of(
|
|
||||||
new QBackendStepMetaData()
|
|
||||||
.withName(STEP_START)
|
|
||||||
.withCode(new QCodeReference(StartStep.class)),
|
|
||||||
new QFrontendStepMetaData()
|
|
||||||
.withName(STEP_END)
|
|
||||||
));
|
|
||||||
|
|
||||||
process.addOptionalStep(new QFrontendStepMetaData().withName(STEP_A));
|
|
||||||
process.addOptionalStep(new QBackendStepMetaData().withName(STEP_B).withCode(new QCodeReference(NoopBackendStep.class)));
|
|
||||||
process.addOptionalStep(new QFrontendStepMetaData().withName(STEP_C));
|
|
||||||
|
|
||||||
process.addOptionalStep(new QBackendStepMetaData().withName(STEP_1).withCode(new QCodeReference(NoopBackendStep.class)));
|
|
||||||
process.addOptionalStep(new QFrontendStepMetaData().withName(STEP_2));
|
|
||||||
process.addOptionalStep(new QBackendStepMetaData().withName(STEP_3).withCode(new QCodeReference(NoopBackendStep.class)));
|
|
||||||
|
|
||||||
return (process);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
public static class StartStep implements BackendStep
|
|
||||||
{
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
@Override
|
|
||||||
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
|
||||||
{
|
|
||||||
boolean skipSomeSteps = BooleanUtils.isTrue(runBackendStepInput.getValueBoolean("skipSomeSteps"));
|
|
||||||
|
|
||||||
if(runBackendStepInput.getValueString("which").equals("letters"))
|
|
||||||
{
|
|
||||||
runBackendStepOutput.updateStepList(LETTERS_STEP_LIST);
|
|
||||||
if(skipSomeSteps)
|
|
||||||
{
|
|
||||||
runBackendStepOutput.setOverrideLastStepName(STEP_C);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
runBackendStepOutput.updateStepList(NUMBERS_STEP_LIST);
|
|
||||||
if(skipSomeSteps)
|
|
||||||
{
|
|
||||||
runBackendStepOutput.setOverrideLastStepName(STEP_2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -24,7 +24,6 @@ package com.kingsrook.qqq.backend.core.actions.tables;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
|
||||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
||||||
@ -39,11 +38,8 @@ import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.UniqueKey;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.UniqueKey;
|
||||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
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.assertNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -85,27 +81,12 @@ class ReplaceActionTest extends BaseTest
|
|||||||
replaceInput.setOmitDmlAudit(true);
|
replaceInput.setOmitDmlAudit(true);
|
||||||
replaceInput.setRecords(newPeople);
|
replaceInput.setRecords(newPeople);
|
||||||
replaceInput.setFilter(null);
|
replaceInput.setFilter(null);
|
||||||
replaceInput.setSetPrimaryKeyInInsertedRecords(false);
|
|
||||||
ReplaceOutput replaceOutput = new ReplaceAction().execute(replaceInput);
|
ReplaceOutput replaceOutput = new ReplaceAction().execute(replaceInput);
|
||||||
|
|
||||||
assertEquals(1, replaceOutput.getInsertOutput().getRecords().size());
|
assertEquals(1, replaceOutput.getInsertOutput().getRecords().size());
|
||||||
assertEquals(1, replaceOutput.getUpdateOutput().getRecords().size());
|
assertEquals(1, replaceOutput.getUpdateOutput().getRecords().size());
|
||||||
assertEquals(1, replaceOutput.getDeleteOutput().getDeletedRecordCount());
|
assertEquals(1, replaceOutput.getDeleteOutput().getDeletedRecordCount());
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// due to false for SetPrimaryKeyInInsertedRecords, make sure primary keys aren't on the records that got inserted //
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
Optional<QRecord> ned = newPeople.stream().filter(r -> r.getValueString("firstName").equals("Ned")).findFirst();
|
|
||||||
assertThat(ned).isPresent();
|
|
||||||
assertNull(ned.get().getValue("id"), "the record that got inserted should not have its primary key set");
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// but note, homer (who was updated) would have had its primary key set too, as part of the internal processing that does the update. //
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
Optional<QRecord> homer = newPeople.stream().filter(r -> r.getValueString("firstName").equals("Homer")).findFirst();
|
|
||||||
assertThat(homer).isPresent();
|
|
||||||
assertNotNull(homer.get().getValue("id"), "the record that got updated should have its primary key set");
|
|
||||||
|
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
// assert homer was updated //
|
// assert homer was updated //
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
@ -155,18 +136,12 @@ class ReplaceActionTest extends BaseTest
|
|||||||
replaceInput.setOmitDmlAudit(true);
|
replaceInput.setOmitDmlAudit(true);
|
||||||
replaceInput.setRecords(newPeople);
|
replaceInput.setRecords(newPeople);
|
||||||
replaceInput.setFilter(null);
|
replaceInput.setFilter(null);
|
||||||
replaceInput.setSetPrimaryKeyInInsertedRecords(true);
|
|
||||||
ReplaceOutput replaceOutput = new ReplaceAction().execute(replaceInput);
|
ReplaceOutput replaceOutput = new ReplaceAction().execute(replaceInput);
|
||||||
|
|
||||||
assertEquals(2, replaceOutput.getInsertOutput().getRecords().size());
|
assertEquals(2, replaceOutput.getInsertOutput().getRecords().size());
|
||||||
assertEquals(0, replaceOutput.getUpdateOutput().getRecords().size());
|
assertEquals(0, replaceOutput.getUpdateOutput().getRecords().size());
|
||||||
assertEquals(2, replaceOutput.getDeleteOutput().getDeletedRecordCount());
|
assertEquals(2, replaceOutput.getDeleteOutput().getDeletedRecordCount());
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// due to true for SetPrimaryKeyInInsertedRecords, make sure primary keys ARE on all the records that got inserted //
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
assertTrue(newPeople.stream().allMatch(r -> r.getValue("id") != null), "All inserted records should have their primary key");
|
|
||||||
|
|
||||||
///////////////////////////////////////
|
///////////////////////////////////////
|
||||||
// assert homer & marge were deleted //
|
// assert homer & marge were deleted //
|
||||||
///////////////////////////////////////
|
///////////////////////////////////////
|
||||||
|
@ -43,7 +43,6 @@ import org.junit.jupiter.api.BeforeEach;
|
|||||||
import org.junit.jupiter.api.Disabled;
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -68,50 +67,6 @@ class QLoggerTest extends BaseTest
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
@Test
|
|
||||||
void testLogAndThrowMethods() throws QException
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
LOG.info("Some info");
|
|
||||||
LOG.warnAndThrow(new QException("Something failed"), new LogPair("something", 1));
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
//////////////
|
|
||||||
// ok, done //
|
|
||||||
//////////////
|
|
||||||
}
|
|
||||||
|
|
||||||
assertThatThrownBy(() ->
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
methodThatThrows();
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
throw LOG.errorAndThrow(new QException("I caught, now i errorAndThrow", e), new LogPair("iLove", "logPairs"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
).isInstanceOf(QException.class).hasMessageContaining("I caught").rootCause().hasMessageContaining("See, I throw");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
private void methodThatThrows() throws QException
|
|
||||||
{
|
|
||||||
throw (new QException("See, I throw"));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -69,7 +69,6 @@ import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
|||||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||||
import com.kingsrook.qqq.backend.module.api.exceptions.OAuthCredentialsException;
|
import com.kingsrook.qqq.backend.module.api.exceptions.OAuthCredentialsException;
|
||||||
import com.kingsrook.qqq.backend.module.api.exceptions.OAuthExpiredTokenException;
|
import com.kingsrook.qqq.backend.module.api.exceptions.OAuthExpiredTokenException;
|
||||||
import com.kingsrook.qqq.backend.module.api.exceptions.QBadHttpResponseStatusException;
|
|
||||||
import com.kingsrook.qqq.backend.module.api.exceptions.RateLimitException;
|
import com.kingsrook.qqq.backend.module.api.exceptions.RateLimitException;
|
||||||
import com.kingsrook.qqq.backend.module.api.exceptions.RetryableServerErrorException;
|
import com.kingsrook.qqq.backend.module.api.exceptions.RetryableServerErrorException;
|
||||||
import com.kingsrook.qqq.backend.module.api.model.AuthorizationType;
|
import com.kingsrook.qqq.backend.module.api.model.AuthorizationType;
|
||||||
@ -594,7 +593,7 @@ public class BaseAPIActionUtil
|
|||||||
}
|
}
|
||||||
|
|
||||||
String warningMessage = "HTTP " + request.getMethod() + " for table [" + table.getName() + "] failed with status " + statusCode + ": " + resultString;
|
String warningMessage = "HTTP " + request.getMethod() + " for table [" + table.getName() + "] failed with status " + statusCode + ": " + resultString;
|
||||||
throw (new QBadHttpResponseStatusException(warningMessage, response));
|
throw (new QException(warningMessage));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -103,7 +103,6 @@ import com.kingsrook.qqq.backend.core.model.actions.values.SearchPossibleValueSo
|
|||||||
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput;
|
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerHelper;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType;
|
||||||
@ -284,14 +283,6 @@ public class QJavalinImplementation
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// clear the cache of classes in this class, so that new classes can be found //
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
MetaDataProducerHelper.clearTopLevelClassCache();
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
// try to get a new instance from the supplier //
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
QInstance newQInstance = qInstanceHotSwapSupplier.get();
|
QInstance newQInstance = qInstanceHotSwapSupplier.get();
|
||||||
if(newQInstance == null)
|
if(newQInstance == null)
|
||||||
{
|
{
|
||||||
@ -299,9 +290,6 @@ public class QJavalinImplementation
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// validate the instance, and only if it passes, then set it in our static field //
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////
|
|
||||||
new QInstanceValidator().validate(newQInstance);
|
new QInstanceValidator().validate(newQInstance);
|
||||||
QJavalinImplementation.qInstance = newQInstance;
|
QJavalinImplementation.qInstance = newQInstance;
|
||||||
LOG.info("Swapped qInstance");
|
LOG.info("Swapped qInstance");
|
||||||
|
@ -72,7 +72,6 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.storage.StorageInput;
|
|||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
@ -449,12 +448,6 @@ public class QJavalinProcessHandler
|
|||||||
}
|
}
|
||||||
resultForCaller.put("values", runProcessOutput.getValues());
|
resultForCaller.put("values", runProcessOutput.getValues());
|
||||||
runProcessOutput.getProcessState().getNextStepName().ifPresent(nextStep -> resultForCaller.put("nextStep", nextStep));
|
runProcessOutput.getProcessState().getNextStepName().ifPresent(nextStep -> resultForCaller.put("nextStep", nextStep));
|
||||||
|
|
||||||
List<QFrontendStepMetaData> updatedFrontendStepList = runProcessOutput.getUpdatedFrontendStepList();
|
|
||||||
if(updatedFrontendStepList != null)
|
|
||||||
{
|
|
||||||
resultForCaller.put("updatedFrontendStepList", updatedFrontendStepList);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user