Compare commits

..

5 Commits

399 changed files with 1253 additions and 40468 deletions

View File

@ -1,51 +1,27 @@
#!/bin/bash #!/bin/bash
############################################################################ if [ -z "$CIRCLE_BRANCH" ] && [ -z "$CIRCLE_TAG" ]; then
## adjust-pom.version.sh echo "Error: env vars CIRCLE_BRANCH and CIRCLE_TAG were not set."
## During CircleCI builds - edit the qqq parent pom.xml, to set the exit 1;
## <revision> value such that: fi
## - feature-branch builds, tagged as snapshot-*, deploy with a version
## number that includes that tag's name (minus the snapshot- part)
## - integration-branch builds deploy with a version number that includes
## the branch name slugified
## - we never deploy -SNAPSHOT versions any more - because we don't believe
## it is ever valid to not know exactly what versions you are getting
## (perhaps because we are too loose with our versioning?)
############################################################################
POM=$(dirname $0)/../pom.xml if [ "$CIRCLE_BRANCH" == "dev" ] || [ "$CIRCLE_BRANCH" == "staging" ] || [ "$CIRCLE_BRANCH" == "main" ] || [ \! -z $(echo "$CIRCLE_TAG" | grep "^version-") ]; then
echo "On branch: $CIRCLE_BRANCH, tag: $CIRCLE_TAG..." echo "On a primary branch or tag [${CIRCLE_BRANCH}${CIRCLE_TAG}] - will not edit the pom version.";
echo "(or do we need to do this for dev...?)"
###################################################################### echo "(maybe we do this if the current version is a SNAPSHOT?)"
## ## only do anything if the committed pom has a -SNAPSHOT version ##
######################################################################
REVISION=$(grep '<revision>' $POM | sed 's/.*<revision>//;s/<.*//');
echo "<revision> in pom.xml is: $REVISION"
if [ \! $(echo "$REVISION" | grep SNAPSHOT) ]; then
echo "Not on a SNAPSHOT revision, so nothing to do here."
exit 0; exit 0;
fi fi
################################################################################## if [ -n "$CIRCLE_BRANCH" ]; then
## ## figure out if we need a SLUG: a snapshot- tag, or an integration/ branch ## SLUG=$(echo $CIRCLE_BRANCH | sed 's/[^a-zA-Z0-9]/-/g')
################################################################################## else
SLUG="" SLUG=$(echo $CIRCLE_TAG | sed 's/^snapshot-//g')
if [ $(echo "$CIRCLE_TAG" | grep ^snapshot-) ]; then
SLUG=$(echo "$CIRCLE_TAG" | sed "s/^snapshot-//")-
echo "Using slug [$SLUG] from tag [$CIRCLE_TAG]"
elif [ $(echo "$CIRCLE_BRANCH" | grep ^integration/) ]; then
SLUG=$(echo "$CIRCLE_BRANCH" | sed "s,/,-,g")-
echo "Using slug [$SLUG] from branch [$CIRCLE_BRANCH]"
fi fi
################################################################ POM=$(dirname $0)/../pom.xml
## ## build the replcaement for -SNAPSHOT, and update the pom ## UNIQ=$(date +%Y%m%d-%H%M%S)-$(git rev-parse --short HEAD)
################################################################ SLUG="${SLUG}-${UNIQ}"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
REPLACEMENT=${SLUG}${TIMESTAMP}
echo "Updating $POM -SNAPSHOT to: -$REPLACEMENT" echo "Updating $POM <revision> to: $SLUG"
sed -i "s/-SNAPSHOT<\/revision>/-$REPLACEMENT<\/revision>/" $POM sed -i "s/<revision>.*/<revision>$SLUG<\/revision>/" $POM
git diff $POM git diff $POM

View File

@ -79,19 +79,6 @@ commands:
- ~/.m2 - ~/.m2
key: v1-dependencies-{{ checksum "pom.xml" }} key: v1-dependencies-{{ checksum "pom.xml" }}
check_middleware_api_versions:
steps:
- checkout
- restore_cache:
keys:
- v1-dependencies-{{ checksum "pom.xml" }}
- run:
name: Build and Run ValidateApiVersions
command: |
mvn -s .circleci/mvn-settings.xml -T4 install -DskipTests
mvn -s .circleci/mvn-settings.xml -pl qqq-middleware-javalin package appassembler:assemble -DskipTests
qqq-middleware-javalin/target/appassembler/bin/ValidateApiVersions -r $(pwd)
mvn_jar_deploy: mvn_jar_deploy:
steps: steps:
- checkout - checkout
@ -143,7 +130,6 @@ jobs:
## - localstack/startup ## - localstack/startup
- install_java17 - install_java17
- mvn_verify - mvn_verify
- check_middleware_api_versions
mvn_deploy: mvn_deploy:
executor: localstack/default executor: localstack/default
@ -151,7 +137,6 @@ jobs:
## - localstack/startup ## - localstack/startup
- install_java17 - install_java17
- mvn_verify - mvn_verify
- check_middleware_api_versions
- mvn_jar_deploy - mvn_jar_deploy
publish_asciidoc: publish_asciidoc:

View File

@ -48,3 +48,5 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License 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/>. along with this program. If not, see <https://www.gnu.org/licenses/>.

View File

@ -33,6 +33,9 @@ If the {link-table} has a `POST_QUERY_CUSTOMIZER` defined, then after records ar
* `table` - *String, Required* - Name of the table being queried against. * `table` - *String, Required* - Name of the table being queried against.
* `filter` - *<<QQueryFilter>> object* - Specification for what records should be returned, based on *<<QFilterCriteria>>* objects, and how they should be sorted, based on *<<QFilterOrderBy>>* objects. * `filter` - *<<QQueryFilter>> object* - Specification for what records should be returned, based on *<<QFilterCriteria>>* objects, and how they should be sorted, based on *<<QFilterOrderBy>>* objects.
If a `filter` is not given, then all rows in the table will be returned by the query. If a `filter` is not given, then all rows in the table will be returned by the query.
* `skip` - *Integer* - Optional number of records to be skipped at the beginning of the result set.
e.g., for implementing pagination.
* `limit` - *Integer* - Optional maximum number of records to be returned by the query.
* `transaction` - *QBackendTransaction object* - Optional transaction object. * `transaction` - *QBackendTransaction object* - Optional transaction object.
** Behavior for this object is backend-dependant. ** Behavior for this object is backend-dependant.
In an RDBMS backend, this object is generally needed if you want your query to see data that may have been modified within the same transaction. In an RDBMS backend, this object is generally needed if you want your query to see data that may have been modified within the same transaction.
@ -52,14 +55,6 @@ But if running a query to provide data as part of a process, then this can gener
* `shouldMaskPassword` - *boolean, default: true* - Controls whether or not fields with `type` = `PASSWORD` should be masked, or if their actual values should be returned. * `shouldMaskPassword` - *boolean, default: true* - Controls whether or not fields with `type` = `PASSWORD` should be masked, or if their actual values should be returned.
* `queryJoins` - *List of <<QueryJoin>> objects* - Optional list of tables to be joined with the main table being queried. * `queryJoins` - *List of <<QueryJoin>> objects* - Optional list of tables to be joined with the main table being queried.
See QueryJoin below for further details. See QueryJoin below for further details.
* `fieldNamesToInclude` - *Set of String* - Optional set of field names to be included in the records.
** Fields from a queryJoin must be prefixed by the join table's name or alias, and a period.
Field names from the table being queried should not have any sort of prefix.
** A `null` set here (default) means to include all fields from the table and any queryJoins set as select=true.
** An empty set will cause an error, as well any unrecognized field names.
** `QueryAction` will validate the set of field names, and throw an exception if any unrecognized names are given.
** _Note that this is an optional feature, which some backend modules may not implement.
Meaning, they would always return all fields._
==== QQueryFilter ==== QQueryFilter
A key component of *<<QueryInput>>*, a *QQueryFilter* defines both what records should be included in a query's results (e.g., an SQL `WHERE`), as well as how those results should be sorted (SQL `ORDER BY`). A key component of *<<QueryInput>>*, a *QQueryFilter* defines both what records should be included in a query's results (e.g., an SQL `WHERE`), as well as how those results should be sorted (SQL `ORDER BY`).
@ -73,9 +68,6 @@ In general, multiple *orderBys* can be given (depending on backend implementatio
** Each *subFilter* can include its own additional *subFilters*. ** Each *subFilter* can include its own additional *subFilters*.
** Each *subFilter* can specify a different *booleanOperator*. ** Each *subFilter* can specify a different *booleanOperator*.
** For example, consider the following *QQueryFilter*, that uses two *subFilters*, and a mix of *booleanOperators* ** For example, consider the following *QQueryFilter*, that uses two *subFilters*, and a mix of *booleanOperators*
* `skip` - *Integer* - Optional number of records to be skipped at the beginning of the result set.
e.g., for implementing pagination.
* `limit` - *Integer* - Optional maximum number of records to be returned by the query.
[source,java] [source,java]
---- ----

View File

@ -38,13 +38,6 @@ See {link-permissionRules} for details.
*** 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 (for a <<_custom_process_flow>>), they should be added by a call to `.addOptionalStep(QStepMetaData)`, which only places them in the `steps` map.
* `stepFlow` - *enum, default LINEAR* - specifies the the flow-control logic between steps. Possible values are:
** `LINEAR` - steps are executed in-order, through the `stepList`.
A backend step _can_ customize the `nextStepName` or re-order the `stepList`, if needed.
In a frontend step, a user may be given the option to go _back_ to a previous step as well.
** `STATE_MACHINE` - steps are executed as a Fine State Machine, starting with the first step in `stepList`,
but then proceeding based on the `nextStepName` specified by the previous step.
Thus allowing much more flexible flows.
* `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...#
@ -74,11 +67,6 @@ For processes with a user-interface, they must define one or more "screens" in t
* `formFields` - *List of String* - list of field names used by the screen as form-inputs. * `formFields` - *List of String* - list of field names used by the screen as form-inputs.
* `viewFields` - *List of String* - list of field names used by the screen as visible outputs. * `viewFields` - *List of String* - list of field names used by the screen as visible outputs.
* `recordListFields` - *List of String* - list of field names used by the screen in a record listing. * `recordListFields` - *List of String* - list of field names used by the screen in a record listing.
* `format` - *Optional String* - directive for a frontend to use specialized formatting for the display of the process.
** Consult frontend documentation for supported values and their meanings.
* `backStepName` - *Optional String* - For processes using `LINEAR` flow, if this value is given,
then the frontend should offer a control that the user can take (e.g., a button) to move back to an
earlier step in the process.
==== QFrontendComponentMetaData ==== QFrontendComponentMetaData
@ -102,13 +90,10 @@ Expects a process value named `html`.
Expects process values named `downloadFileName` and `serverFilePath`. Expects process values named `downloadFileName` and `serverFilePath`.
** `GOOGLE_DRIVE_SELECT_FOLDER` - Special form that presents a UI from Google Drive, where the user can select a folder (e.g., as a target for uploading files in a subsequent backend step). ** `GOOGLE_DRIVE_SELECT_FOLDER` - Special form that presents a UI from Google Drive, where the user can select a folder (e.g., as a target for uploading files in a subsequent backend step).
** `BULK_EDIT_FORM` - For use by the standard QQQ Bulk Edit process. ** `BULK_EDIT_FORM` - For use by the standard QQQ Bulk Edit process.
** `BULK_LOAD_FILE_MAPPING_FORM`, `BULK_LOAD_VALUE_MAPPING_FORM`, or `BULK_LOAD_PROFILE_FORM` - For use by the standard QQQ Bulk Load process.
** `VALIDATION_REVIEW_SCREEN` - For use by the QQQ Streamed ETL With Frontend process family of processes. ** `VALIDATION_REVIEW_SCREEN` - For use by the QQQ Streamed ETL With Frontend process family of processes.
Displays a component prompting the user to run full validation or to skip it, or, if full validation has been ran, then showing the results of that validation. Displays a component prompting the user to run full validation or to skip it, or, if full validation has been ran, then showing the results of that validation.
** `PROCESS_SUMMARY_RESULTS` - For use by the QQQ Streamed ETL With Frontend process family of processes. ** `PROCESS_SUMMARY_RESULTS` - For use by the QQQ Streamed ETL With Frontend process family of processes.
Displays the summary results of running the process. Displays the summary results of running the process.
** `WIDGET` - Render a QQQ Widget.
Requires that `widgetName` be given as a value for the component.
** `RECORD_LIST` - _Deprecated. ** `RECORD_LIST` - _Deprecated.
Showed a grid with a list of records as populated by the process._ Showed a grid with a list of records as populated by the process._
* `values` - *Map of String → Serializable* - Key=value pairs, with different expectations based on the component's `type`. * `values` - *Map of String → Serializable* - Key=value pairs, with different expectations based on the component's `type`.
@ -131,27 +116,6 @@ It can be used, however, for example, to cause a `defaultValue` to be applied to
It can also be used to cause the process to throw an error, if a field is marked as `isRequired`, but a value is not present. It can also be used to cause the process to throw an error, if a field is marked as `isRequired`, but a value is not present.
** `recordListMetaData` - *RecordListMetaData object* - _Not used at this time._ ** `recordListMetaData` - *RecordListMetaData object* - _Not used at this time._
==== QStateMachineStep
Processes that use `flow = STATE_MACHINE` should use process steps of type `QStateMachineStep`.
A common pattern seen in state-machine processes, is that they will present a frontend-step to a user,
then always run a given backend-step in response to that screen which the user submitted.
Inside that backend-step, custom application logic will determine the next state to go to,
which is typically another frontend-step (which would then submit data to its corresponding backend-step,
and continue the FSM).
To help facilitate this pattern, factory methods exist on `QStateMachineStep`,
for constructing the commonly-expected types of state-machine steps:
* `frontendThenBackend(name, frontendStep, backendStep)` - for the frontend-then-backend pattern described above.
* `backendOnly(name, backendStep)` - for a state that only has a backend step.
This might be useful as a “reset” step, to run before restarting a state-loop.
* `frontendOnly(name, frontendStep)` - for a state that only has a frontend step,
which would always be followed by another state, which must be specified as the `defaultNextStepName`
on the `QStateMachineStep`.
==== BasepullConfiguration ==== BasepullConfiguration
A "Basepull" process is a common pattern where an application needs to perform some action on all new (or updated) records from a particular data source. A "Basepull" process is a common pattern where an application needs to perform some action on all new (or updated) records from a particular data source.
@ -254,10 +218,12 @@ But for some cases, doing page-level transactions can reduce long-transactions a
* `withSchedule(QScheduleMetaData schedule)` - Add a <<QScheduleMetaData>> to the process. * `withSchedule(QScheduleMetaData schedule)` - Add a <<QScheduleMetaData>> to the process.
[#_custom_process_flow] [#_custom_process_flow]
==== How to customize a Linear process flow ==== Custom Process Flow
As referenced in the definition of the <<_QProcessMetaData_Properties,QProcessMetaData Properties>>, by default, As referenced in the definition of the <<_QProcessMetaData_Properties,QProcessMetaData Properties>>, by default, a process
(with `flow = LINEAR`) a process will execute each of its steps in-order, as defined in the `stepList` property. will execute each of its steps in-order, as defined in the `stepList` property.
However, a Backend Step can customize this flow as follows: 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)` * `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. ** QQQ's `RunProcessAction` keeps track of which step it "last" ran, e.g., to tell it which one to run next.
@ -273,7 +239,7 @@ does need to be found in the new `stepNameList` - otherwise, the framework will
for figuring out where to go next. for figuring out where to go next.
[source,java] [source,java]
.Example of a defining process that can use a customized linear flow: .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: // 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_START = "start";
@ -358,21 +324,4 @@ public static class StartStep implements BackendStep
} }
---- ----
[#_process_back]
==== How to allow a process to go back
The simplest option to allow a process to present a "Back" button to users,
thus allowing them to move backward through a process
(e.g., from a review screen back to an earlier input screen), is to set the property `backStepName`
on a `QFrontendStepMetaData`.
If the step that is executed after the user hits "Back" is a backend step, then within that
step, `runBackendStepInput.getIsStepBack()` will return `true` (but ONLY within that first step after
the user hits "Back"). It may be necessary within individual processes to be aware that the user
has chosen to go back, to reset certain values in the process's state.
Alternatively, if a frontend step's "Back" behavior needs to be dynamic (e.g., sometimes not available,
or sometimes targeting different steps in the process), then in a backend step that runs before the
frontend step, a call to `runBackendStepOutput.getProcessState().setBackStepName()` can be made,
to customize the value which would otherwise come from the `QFrontendStepMetaData`.

View File

@ -36,7 +36,6 @@
<module>qqq-backend-module-rdbms</module> <module>qqq-backend-module-rdbms</module>
<module>qqq-backend-module-mongodb</module> <module>qqq-backend-module-mongodb</module>
<module>qqq-language-support-javascript</module> <module>qqq-language-support-javascript</module>
<module>qqq-openapi</module>
<module>qqq-middleware-picocli</module> <module>qqq-middleware-picocli</module>
<module>qqq-middleware-javalin</module> <module>qqq-middleware-javalin</module>
<module>qqq-middleware-lambda</module> <module>qqq-middleware-lambda</module>
@ -47,7 +46,7 @@
</modules> </modules>
<properties> <properties>
<revision>0.24.0-SNAPSHOT</revision> <revision>0.21.0-SNAPSHOT</revision>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

View File

@ -102,11 +102,6 @@
<artifactId>fastexcel</artifactId> <artifactId>fastexcel</artifactId>
<version>0.12.15</version> <version>0.12.15</version>
</dependency> </dependency>
<dependency>
<groupId>org.dhatim</groupId>
<artifactId>fastexcel-reader</artifactId>
<version>0.18.4</version>
</dependency>
<dependency> <dependency>
<groupId>org.apache.poi</groupId> <groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId> <artifactId>poi</artifactId>
@ -117,14 +112,6 @@
<artifactId>poi-ooxml</artifactId> <artifactId>poi-ooxml</artifactId>
<version>5.2.5</version> <version>5.2.5</version>
</dependency> </dependency>
<!-- adding to help FastExcel -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.16.0</version>
</dependency>
<dependency> <dependency>
<groupId>com.auth0</groupId> <groupId>com.auth0</groupId>
<artifactId>auth0</artifactId> <artifactId>auth0</artifactId>

View File

@ -225,13 +225,7 @@ public class AuditAction extends AbstractQActionFunction<AuditInput, AuditOutput
{ {
if(auditSingleInput.getSecurityKeyValues() == null || !auditSingleInput.getSecurityKeyValues().containsKey(recordSecurityLock.getSecurityKeyType())) if(auditSingleInput.getSecurityKeyValues() == null || !auditSingleInput.getSecurityKeyValues().containsKey(recordSecurityLock.getSecurityKeyType()))
{ {
/////////////////////////////////////////////////////// throw (new QException("Missing securityKeyValue [" + recordSecurityLock.getSecurityKeyType() + "] in audit request for table " + auditSingleInput.getAuditTableName()));
// originally, this case threw... //
// but i think it's better to record the audit, just //
// missing its security key value, then to fail... //
///////////////////////////////////////////////////////
// throw (new QException("Missing securityKeyValue [" + recordSecurityLock.getSecurityKeyType() + "] in audit request for table " + auditSingleInput.getAuditTableName()));
LOG.info("Missing securityKeyValue in audit request", logPair("table", auditSingleInput.getAuditTableName()), logPair("securityKey", recordSecurityLock.getSecurityKeyType()));
} }
} }
@ -278,7 +272,7 @@ public class AuditAction extends AbstractQActionFunction<AuditInput, AuditOutput
List<QRecord> auditDetailRecords = new ArrayList<>(); List<QRecord> auditDetailRecords = new ArrayList<>();
for(AuditSingleInput auditSingleInput : CollectionUtils.nonNullList(input.getAuditSingleInputList())) for(AuditSingleInput auditSingleInput : CollectionUtils.nonNullList(input.getAuditSingleInputList()))
{ {
Long auditId = insertOutput.getRecords().get(i++).getValueLong("id"); Integer auditId = insertOutput.getRecords().get(i++).getValueInteger("id");
if(auditId == null) if(auditId == null)
{ {
LOG.warn("Missing an id for inserted audit - so won't be able to store its child details..."); LOG.warn("Missing an id for inserted audit - so won't be able to store its child details...");

View File

@ -24,12 +24,10 @@ package com.kingsrook.qqq.backend.core.actions.customizers;
import java.io.Serializable; import java.io.Serializable;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
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.actions.tables.update.UpdateInput;
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.fields.QFieldMetaData; import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
@ -145,19 +143,4 @@ public interface RecordCustomizerUtilityInterface
} }
} }
/*******************************************************************************
**
*******************************************************************************/
default Map<Serializable, QRecord> getOldRecordMap(List<QRecord> oldRecordList, UpdateInput updateInput)
{
Map<Serializable, QRecord> oldRecordMap = new HashMap<>();
for(QRecord qRecord : oldRecordList)
{
oldRecordMap.put(qRecord.getValue(updateInput.getTable().getPrimaryKeyField()), qRecord);
}
return (oldRecordMap);
}
} }

View File

@ -301,9 +301,6 @@ public class ChildRecordListRenderer extends AbstractWidgetRenderer
} }
} }
widgetData.setAllowRecordEdit(BooleanUtils.isTrue(ValueUtils.getValueAsBoolean(input.getQueryParams().get("allowRecordEdit"))));
widgetData.setAllowRecordDelete(BooleanUtils.isTrue(ValueUtils.getValueAsBoolean(input.getQueryParams().get("allowRecordDelete"))));
return (new RenderWidgetOutput(widgetData)); return (new RenderWidgetOutput(widgetData));
} }
catch(Exception e) catch(Exception e)

View File

@ -23,11 +23,11 @@ package com.kingsrook.qqq.backend.core.actions.dashboard.widgets;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.MetaDataProducerInterface;
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.dashboard.widgets.AlertData; import com.kingsrook.qqq.backend.core.model.dashboard.widgets.AlertData;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.WidgetType; import com.kingsrook.qqq.backend.core.model.dashboard.widgets.WidgetType;
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerInterface;
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.code.QCodeReference; import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaData; import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaData;
@ -40,9 +40,9 @@ import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaData;
** - alertType - name of entry in AlertType enum (ERROR, WARNING, SUCCESS) ** - alertType - name of entry in AlertType enum (ERROR, WARNING, SUCCESS)
** - alertHtml - html to display inside the alert (other than its icon) ** - alertHtml - html to display inside the alert (other than its icon)
*******************************************************************************/ *******************************************************************************/
public class AlertWidgetRenderer extends AbstractWidgetRenderer implements MetaDataProducerInterface<QWidgetMetaData> public class ProcessAlertWidget extends AbstractWidgetRenderer implements MetaDataProducerInterface<QWidgetMetaData>
{ {
public static final String NAME = "AlertWidgetRenderer"; public static final String NAME = "ProcessAlertWidget";

View File

@ -1,92 +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.metadata;
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput;
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataInterface;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
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.tables.QTableMetaData;
/*******************************************************************************
** a default implementation of MetaDataFilterInterface, that allows all the things
*******************************************************************************/
public class AllowAllMetaDataFilter implements MetaDataFilterInterface
{
/***************************************************************************
**
***************************************************************************/
@Override
public boolean allowTable(MetaDataInput input, QTableMetaData table)
{
return (true);
}
/***************************************************************************
**
***************************************************************************/
@Override
public boolean allowProcess(MetaDataInput input, QProcessMetaData process)
{
return (true);
}
/***************************************************************************
**
***************************************************************************/
@Override
public boolean allowReport(MetaDataInput input, QReportMetaData report)
{
return (true);
}
/***************************************************************************
**
***************************************************************************/
@Override
public boolean allowApp(MetaDataInput input, QAppMetaData app)
{
return (true);
}
/***************************************************************************
**
***************************************************************************/
@Override
public boolean allowWidget(MetaDataInput input, QWidgetMetaDataInterface widget)
{
return (true);
}
}

View File

@ -28,18 +28,13 @@ import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.kingsrook.qqq.backend.core.actions.ActionHelper; import com.kingsrook.qqq.backend.core.actions.ActionHelper;
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionCheckResult; import com.kingsrook.qqq.backend.core.actions.permissions.PermissionCheckResult;
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionsHelper; import com.kingsrook.qqq.backend.core.actions.permissions.PermissionsHelper;
import com.kingsrook.qqq.backend.core.context.QContext; import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.exceptions.QRuntimeException;
import com.kingsrook.qqq.backend.core.logging.QLogger;
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput; import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput;
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataOutput; import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataOutput;
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.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataInterface; import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataInterface;
import com.kingsrook.qqq.backend.core.model.metadata.frontend.AppTreeNode; import com.kingsrook.qqq.backend.core.model.metadata.frontend.AppTreeNode;
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendAppMetaData; import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendAppMetaData;
@ -54,7 +49,6 @@ 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;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.memoization.Memoization;
/******************************************************************************* /*******************************************************************************
@ -63,12 +57,6 @@ import com.kingsrook.qqq.backend.core.utils.memoization.Memoization;
*******************************************************************************/ *******************************************************************************/
public class MetaDataAction public class MetaDataAction
{ {
private static final QLogger LOG = QLogger.getLogger(MetaDataAction.class);
private static Memoization<QInstance, MetaDataFilterInterface> metaDataFilterMemoization = new Memoization<>();
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
@ -76,10 +64,10 @@ public class MetaDataAction
{ {
ActionHelper.validateSession(metaDataInput); ActionHelper.validateSession(metaDataInput);
MetaDataOutput metaDataOutput = new MetaDataOutput(); // todo pre-customization - just get to modify the request?
Map<String, AppTreeNode> treeNodes = new LinkedHashMap<>(); MetaDataOutput metaDataOutput = new MetaDataOutput();
MetaDataFilterInterface filter = getMetaDataFilter(); Map<String, AppTreeNode> treeNodes = new LinkedHashMap<>();
///////////////////////////////////// /////////////////////////////////////
// map tables to frontend metadata // // map tables to frontend metadata //
@ -90,11 +78,6 @@ public class MetaDataAction
String tableName = entry.getKey(); String tableName = entry.getKey();
QTableMetaData table = entry.getValue(); QTableMetaData table = entry.getValue();
if(!filter.allowTable(metaDataInput, table))
{
continue;
}
PermissionCheckResult permissionResult = PermissionsHelper.getPermissionCheckResult(metaDataInput, table); PermissionCheckResult permissionResult = PermissionsHelper.getPermissionCheckResult(metaDataInput, table);
if(permissionResult.equals(PermissionCheckResult.DENY_HIDE)) if(permissionResult.equals(PermissionCheckResult.DENY_HIDE))
{ {
@ -119,11 +102,6 @@ public class MetaDataAction
String processName = entry.getKey(); String processName = entry.getKey();
QProcessMetaData process = entry.getValue(); QProcessMetaData process = entry.getValue();
if(!filter.allowProcess(metaDataInput, process))
{
continue;
}
PermissionCheckResult permissionResult = PermissionsHelper.getPermissionCheckResult(metaDataInput, process); PermissionCheckResult permissionResult = PermissionsHelper.getPermissionCheckResult(metaDataInput, process);
if(permissionResult.equals(PermissionCheckResult.DENY_HIDE)) if(permissionResult.equals(PermissionCheckResult.DENY_HIDE))
{ {
@ -144,11 +122,6 @@ public class MetaDataAction
String reportName = entry.getKey(); String reportName = entry.getKey();
QReportMetaData report = entry.getValue(); QReportMetaData report = entry.getValue();
if(!filter.allowReport(metaDataInput, report))
{
continue;
}
PermissionCheckResult permissionResult = PermissionsHelper.getPermissionCheckResult(metaDataInput, report); PermissionCheckResult permissionResult = PermissionsHelper.getPermissionCheckResult(metaDataInput, report);
if(permissionResult.equals(PermissionCheckResult.DENY_HIDE)) if(permissionResult.equals(PermissionCheckResult.DENY_HIDE))
{ {
@ -169,11 +142,6 @@ public class MetaDataAction
String widgetName = entry.getKey(); String widgetName = entry.getKey();
QWidgetMetaDataInterface widget = entry.getValue(); QWidgetMetaDataInterface widget = entry.getValue();
if(!filter.allowWidget(metaDataInput, widget))
{
continue;
}
PermissionCheckResult permissionResult = PermissionsHelper.getPermissionCheckResult(metaDataInput, widget); PermissionCheckResult permissionResult = PermissionsHelper.getPermissionCheckResult(metaDataInput, widget);
if(permissionResult.equals(PermissionCheckResult.DENY_HIDE)) if(permissionResult.equals(PermissionCheckResult.DENY_HIDE))
{ {
@ -206,19 +174,9 @@ public class MetaDataAction
continue; continue;
} }
if(!filter.allowApp(metaDataInput, app)) apps.put(appName, new QFrontendAppMetaData(app, metaDataOutput));
{ treeNodes.put(appName, new AppTreeNode(app));
continue;
}
//////////////////////////////////////
// build the frontend-app meta-data //
//////////////////////////////////////
QFrontendAppMetaData frontendAppMetaData = new QFrontendAppMetaData(app, metaDataOutput);
/////////////////////////////////////////
// add children (if they're permitted) //
/////////////////////////////////////////
if(CollectionUtils.nullSafeHasContents(app.getChildren())) if(CollectionUtils.nullSafeHasContents(app.getChildren()))
{ {
for(QAppChildMetaData child : app.getChildren()) for(QAppChildMetaData child : app.getChildren())
@ -232,42 +190,9 @@ public class MetaDataAction
} }
} }
////////////////////////////////////////////////////////////////////////////////////////////////////// apps.get(appName).addChild(new AppTreeNode(child));
// if the child was filtered away, so it isn't in its corresponding map, then don't include it here //
//////////////////////////////////////////////////////////////////////////////////////////////////////
if(child instanceof QTableMetaData table && !tables.containsKey(table.getName()))
{
continue;
}
if(child instanceof QProcessMetaData process && !processes.containsKey(process.getName()))
{
continue;
}
if(child instanceof QReportMetaData report && !reports.containsKey(report.getName()))
{
continue;
}
if(child instanceof QAppMetaData childApp && !apps.containsKey(childApp.getName()))
{
// continue;
}
frontendAppMetaData.addChild(new AppTreeNode(child));
} }
} }
//////////////////////////////////////////////////////////////////////////////////////////////////////
// if the app ended up having no children, then discard it //
// todo - i think this was wrong, because it didn't take into account ... something nested maybe... //
//////////////////////////////////////////////////////////////////////////////////////////////////////
if(CollectionUtils.nullSafeIsEmpty(frontendAppMetaData.getChildren()) && CollectionUtils.nullSafeIsEmpty(frontendAppMetaData.getWidgets()))
{
// LOG.debug("Discarding empty app", logPair("name", frontendAppMetaData.getName()));
// continue;
}
apps.put(appName, frontendAppMetaData);
treeNodes.put(appName, new AppTreeNode(app));
} }
metaDataOutput.setApps(apps); metaDataOutput.setApps(apps);
@ -303,33 +228,6 @@ public class MetaDataAction
/***************************************************************************
**
***************************************************************************/
private MetaDataFilterInterface getMetaDataFilter()
{
return metaDataFilterMemoization.getResult(QContext.getQInstance(), i ->
{
MetaDataFilterInterface filter = null;
QCodeReference metaDataFilterReference = QContext.getQInstance().getMetaDataFilter();
if(metaDataFilterReference != null)
{
filter = QCodeLoader.getAdHoc(MetaDataFilterInterface.class, metaDataFilterReference);
LOG.debug("Using new meta-data filter of type: " + filter.getClass().getSimpleName());
}
if(filter == null)
{
filter = new AllowAllMetaDataFilter();
LOG.debug("Using new default (allow-all) meta-data filter");
}
return (filter);
}).orElseThrow(() -> new QRuntimeException("Error getting metaDataFilter"));
}
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/

View File

@ -1,64 +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.metadata;
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput;
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataInterface;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
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.tables.QTableMetaData;
/*******************************************************************************
**
*******************************************************************************/
public interface MetaDataFilterInterface
{
/***************************************************************************
**
***************************************************************************/
boolean allowTable(MetaDataInput input, QTableMetaData table);
/***************************************************************************
**
***************************************************************************/
boolean allowProcess(MetaDataInput input, QProcessMetaData process);
/***************************************************************************
**
***************************************************************************/
boolean allowReport(MetaDataInput input, QReportMetaData report);
/***************************************************************************
**
***************************************************************************/
boolean allowApp(MetaDataInput input, QAppMetaData app);
/***************************************************************************
**
***************************************************************************/
boolean allowWidget(MetaDataInput input, QWidgetMetaDataInterface widget);
}

View File

@ -40,7 +40,6 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference; import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReferenceLambda;
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.QBackendStepMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionInputMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionInputMetaData;
@ -258,20 +257,11 @@ public class RunBackendStepAction
{ {
runBackendStepOutput.seedFromRequest(runBackendStepInput); runBackendStepOutput.seedFromRequest(runBackendStepInput);
Object codeObject; Class<?> codeClass = Class.forName(code.getName());
if(code instanceof QCodeReferenceLambda<?> qCodeReferenceLambda) Object codeObject = codeClass.getConstructor().newInstance();
{
codeObject = qCodeReferenceLambda.getLambda();
}
else
{
Class<?> codeClass = Class.forName(code.getName());
codeObject = codeClass.getConstructor().newInstance();
}
if(!(codeObject instanceof BackendStep backendStepCodeObject)) if(!(codeObject instanceof BackendStep backendStepCodeObject))
{ {
throw (new QException("The supplied codeReference [" + code + "] is not a reference to a BackendStep")); throw (new QException("The supplied code [" + codeClass.getName() + "] is not an instance of BackendStep"));
} }
backendStepCodeObject.run(runBackendStepInput, runBackendStepOutput); backendStepCodeObject.run(runBackendStepInput, runBackendStepOutput);

View File

@ -28,7 +28,6 @@ import java.time.temporal.ChronoUnit;
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 java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import com.kingsrook.qqq.backend.core.actions.ActionHelper; import com.kingsrook.qqq.backend.core.actions.ActionHelper;
@ -59,7 +58,6 @@ import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaD
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendComponentMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendComponentMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData; 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.processes.QStateMachineStep;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData; 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.model.session.QSession;
import com.kingsrook.qqq.backend.core.processes.implementations.basepull.BasepullConfiguration; import com.kingsrook.qqq.backend.core.processes.implementations.basepull.BasepullConfiguration;
@ -81,7 +79,6 @@ public class RunProcessAction
{ {
private static final QLogger LOG = QLogger.getLogger(RunProcessAction.class); private static final QLogger LOG = QLogger.getLogger(RunProcessAction.class);
public static final String BASEPULL_KEY_VALUE = "basepullKeyValue";
public static final String BASEPULL_THIS_RUNTIME_KEY = "basepullThisRuntimeKey"; public static final String BASEPULL_THIS_RUNTIME_KEY = "basepullThisRuntimeKey";
public static final String BASEPULL_LAST_RUNTIME_KEY = "basepullLastRuntimeKey"; public static final String BASEPULL_LAST_RUNTIME_KEY = "basepullLastRuntimeKey";
public static final String BASEPULL_TIMESTAMP_FIELD = "basepullTimestampField"; public static final String BASEPULL_TIMESTAMP_FIELD = "basepullTimestampField";
@ -122,12 +119,6 @@ public class RunProcessAction
UUIDAndTypeStateKey stateKey = new UUIDAndTypeStateKey(UUID.fromString(runProcessInput.getProcessUUID()), StateType.PROCESS_STATUS); UUIDAndTypeStateKey stateKey = new UUIDAndTypeStateKey(UUID.fromString(runProcessInput.getProcessUUID()), StateType.PROCESS_STATUS);
ProcessState processState = primeProcessState(runProcessInput, stateKey, process); ProcessState processState = primeProcessState(runProcessInput, stateKey, process);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// these should always be clear when we're starting a run - so make sure they haven't leaked from previous //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
processState.clearNextStepName();
processState.clearBackStepName();
///////////////////////////////////////////////////////// /////////////////////////////////////////////////////////
// if process is 'basepull' style, keep track of 'now' // // if process is 'basepull' style, keep track of 'now' //
///////////////////////////////////////////////////////// /////////////////////////////////////////////////////////
@ -142,11 +133,90 @@ public class RunProcessAction
try try
{ {
switch(Objects.requireNonNull(process.getStepFlow(), "Process [" + process.getName() + "] has a null stepFlow.")) String lastStepName = runProcessInput.getStartAfterStep();
STEP_LOOP:
while(true)
{ {
case LINEAR -> runLinearStepLoop(process, processState, stateKey, runProcessInput, runProcessOutput); ///////////////////////////////////////////////////////////////////////////////////////////////////////
case STATE_MACHINE -> runStateMachineStep(runProcessInput.getStartAfterStep(), process, processState, stateKey, runProcessInput, runProcessOutput, 0); // always refresh the step list - as any step that runs can modify it (in the process state). //
default -> throw (new QException("Unhandled process step flow: " + process.getStepFlow())); // this is why we don't do a loop over the step list - as we'd get ConcurrentModificationExceptions. //
///////////////////////////////////////////////////////////////////////////////////////////////////////
List<QStepMetaData> stepList = getAvailableStepList(processState, process, lastStepName);
if(stepList.isEmpty())
{
break;
}
QStepMetaData step = stepList.get(0);
lastStepName = step.getName();
if(step instanceof QFrontendStepMetaData frontendStep)
{
////////////////////////////////////////////////////////////////
// Handle what to do with frontend steps, per request setting //
////////////////////////////////////////////////////////////////
switch(runProcessInput.getFrontendStepBehavior())
{
case BREAK ->
{
LOG.trace("Breaking process [" + process.getName() + "] at frontend step (as requested by caller): " + step.getName());
processFrontendStepFieldDefaultValues(processState, frontendStep);
processFrontendComponents(processState, frontendStep);
processState.setNextStepName(step.getName());
break STEP_LOOP;
}
case SKIP ->
{
LOG.trace("Skipping frontend step [" + step.getName() + "] in process [" + process.getName() + "] (as requested by caller)");
//////////////////////////////////////////////////////////////////////
// much less error prone in case this code changes in the future... //
//////////////////////////////////////////////////////////////////////
// noinspection UnnecessaryContinue
continue;
}
case FAIL ->
{
LOG.trace("Throwing error for frontend step [" + step.getName() + "] in process [" + process.getName() + "] (as requested by caller)");
throw (new QException("Failing process at step " + step.getName() + " (as requested, to fail on frontend steps)"));
}
default -> throw new IllegalStateException("Unexpected value: " + runProcessInput.getFrontendStepBehavior());
}
}
else if(step instanceof QBackendStepMetaData backendStepMetaData)
{
///////////////////////
// Run backend steps //
///////////////////////
LOG.debug("Running backend step [" + step.getName() + "] in process [" + process.getName() + "]");
RunBackendStepOutput runBackendStepOutput = 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
{
//////////////////////////////////////////////////
// in case we have a different step type, throw //
//////////////////////////////////////////////////
throw (new QException("Unsure how to run a step of type: " + step.getClass().getName()));
}
} }
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@ -188,310 +258,6 @@ public class RunProcessAction
/***************************************************************************
**
***************************************************************************/
private void runLinearStepLoop(QProcessMetaData process, ProcessState processState, UUIDAndTypeStateKey stateKey, RunProcessInput runProcessInput, RunProcessOutput runProcessOutput) throws Exception
{
String lastStepName = runProcessInput.getStartAfterStep();
String startAtStep = runProcessInput.getStartAtStep();
while(true)
{
///////////////////////////////////////////////////////////////////////////////////////////////////////
// always refresh the step list - as any step that runs can modify it (in the process state). //
// this is why we don't do a loop over the step list - as we'd get ConcurrentModificationExceptions. //
// deal with if we were told, from the input, to start After a step, or start At a step. //
///////////////////////////////////////////////////////////////////////////////////////////////////////
List<QStepMetaData> stepList;
if(startAtStep == null)
{
stepList = getAvailableStepList(processState, process, lastStepName, false);
}
else
{
stepList = getAvailableStepList(processState, process, startAtStep, true);
///////////////////////////////////////////////////////////////////////////////////
// clear this field - so after we run a step, we'll then loop in last-step mode. //
///////////////////////////////////////////////////////////////////////////////////
startAtStep = null;
///////////////////////////////////////////////////////////////////////////////////
// if we're going to run a backend step now, let it see that this is a step-back //
///////////////////////////////////////////////////////////////////////////////////
processState.setIsStepBack(true);
}
if(stepList.isEmpty())
{
break;
}
QStepMetaData step = stepList.get(0);
lastStepName = step.getName();
if(step instanceof QFrontendStepMetaData frontendStep)
{
LoopTodo loopTodo = prepareForFrontendStep(runProcessInput, process, frontendStep, processState);
if(loopTodo == LoopTodo.BREAK)
{
break;
}
}
else if(step instanceof QBackendStepMetaData backendStepMetaData)
{
RunBackendStepOutput runBackendStepOutput = runBackendStep(process, processState, stateKey, runProcessInput, runProcessOutput, backendStepMetaData, step);
/////////////////////////////////////////////////////////////////////////////////////////
// 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();
}
}
else
{
//////////////////////////////////////////////////
// in case we have a different step type, throw //
//////////////////////////////////////////////////
throw (new QException("Unsure how to run a step of type: " + step.getClass().getName()));
}
////////////////////////////////////////////////////////////////////////////////////////
// only let this value be set for the original back step - don't let it stick around. //
// if a process wants to keep track of this itself, it can, but in a different slot. //
////////////////////////////////////////////////////////////////////////////////////////
processState.setIsStepBack(false);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// in case we broke from the loop above (e.g., by going directly into a frontend step), once again make sure to lower this flag. //
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
processState.setIsStepBack(false);
}
/***************************************************************************
**
***************************************************************************/
private enum LoopTodo
{
BREAK,
CONTINUE
}
/***************************************************************************
**
***************************************************************************/
private LoopTodo prepareForFrontendStep(RunProcessInput runProcessInput, QProcessMetaData process, QFrontendStepMetaData step, ProcessState processState) throws QException
{
////////////////////////////////////////////////////////////////
// Handle what to do with frontend steps, per request setting //
////////////////////////////////////////////////////////////////
switch(runProcessInput.getFrontendStepBehavior())
{
case BREAK ->
{
LOG.trace("Breaking process [" + process.getName() + "] at frontend step (as requested by caller): " + step.getName());
processFrontendStepFieldDefaultValues(processState, step);
processFrontendComponents(processState, step);
processState.setNextStepName(step.getName());
if(StringUtils.hasContent(step.getBackStepName()) && processState.getBackStepName().isEmpty())
{
processState.setBackStepName(step.getBackStepName());
}
return LoopTodo.BREAK;
}
case SKIP ->
{
LOG.trace("Skipping frontend step [" + step.getName() + "] in process [" + process.getName() + "] (as requested by caller)");
return LoopTodo.CONTINUE;
}
case FAIL ->
{
LOG.trace("Throwing error for frontend step [" + step.getName() + "] in process [" + process.getName() + "] (as requested by caller)");
throw (new QException("Failing process at step " + step.getName() + " (as requested, to fail on frontend steps)"));
}
default -> throw new IllegalStateException("Unexpected value: " + runProcessInput.getFrontendStepBehavior());
}
}
/***************************************************************************
**
***************************************************************************/
private void runStateMachineStep(String lastStepName, QProcessMetaData process, ProcessState processState, UUIDAndTypeStateKey stateKey, RunProcessInput runProcessInput, RunProcessOutput runProcessOutput, int stackDepth) throws Exception
{
//////////////////////////////
// check for stack-overflow //
//////////////////////////////
Integer maxStateMachineProcessStepFlowStackDepth = Objects.requireNonNullElse(runProcessInput.getValueInteger("maxStateMachineProcessStepFlowStackDepth"), 20);
if(stackDepth > maxStateMachineProcessStepFlowStackDepth)
{
throw (new QException("StateMachine process recurred too many times (exceeded maxStateMachineProcessStepFlowStackDepth of " + maxStateMachineProcessStepFlowStackDepth + ")"));
}
//////////////////////////////////
// figure out what step to run: //
//////////////////////////////////
QStepMetaData step = null;
if(!StringUtils.hasContent(lastStepName))
{
////////////////////////////////////////////////////////////////////
// if no lastStepName is given, start at the process's first step //
////////////////////////////////////////////////////////////////////
if(CollectionUtils.nullSafeIsEmpty(process.getStepList()))
{
throw (new QException("Process [" + process.getName() + "] does not have a step list defined."));
}
step = process.getStepList().get(0);
}
else
{
/////////////////////////////////////
// else run the given lastStepName //
/////////////////////////////////////
processState.clearNextStepName();
processState.clearBackStepName();
step = process.getStep(lastStepName);
if(step == null)
{
throw (new QException("Could not find step by name [" + lastStepName + "]"));
}
}
/////////////////////////////////////////////////////////////////////////
// for the flow of: //
// we were on a frontend step (as a sub-step of a state machine step), //
// and now we're here to run that state-step's backend step - //
// find the state-machine step containing this frontend step. //
/////////////////////////////////////////////////////////////////////////
String skipSubStepsUntil = null;
if(step instanceof QFrontendStepMetaData frontendStepMetaData)
{
QStateMachineStep stateMachineStep = getStateMachineStepContainingSubStep(process, frontendStepMetaData.getName());
if(stateMachineStep == null)
{
throw (new QException("Could not find stateMachineStep that contains last-frontend step: " + frontendStepMetaData.getName()));
}
step = stateMachineStep;
//////////////////////////////////////////////////////////////////////////////////
// set this flag, to know to skip this frontend step in the sub-step loop below //
//////////////////////////////////////////////////////////////////////////////////
skipSubStepsUntil = frontendStepMetaData.getName();
}
if(!(step instanceof QStateMachineStep stateMachineStep))
{
throw (new QException("Have a non-stateMachineStep in a process using stateMachine flow... " + step.getClass().getName()));
}
///////////////////////
// run the sub-steps //
///////////////////////
boolean ranAnySubSteps = false;
for(QStepMetaData subStep : stateMachineStep.getSubSteps())
{
///////////////////////////////////////////////////////////////////////////////////////////////
// ok, well, skip them if this flag is set (and clear the flag once we've hit this sub-step) //
///////////////////////////////////////////////////////////////////////////////////////////////
if(skipSubStepsUntil != null)
{
if(skipSubStepsUntil.equals(subStep.getName()))
{
skipSubStepsUntil = null;
}
continue;
}
ranAnySubSteps = true;
if(subStep instanceof QFrontendStepMetaData frontendStep)
{
LoopTodo loopTodo = prepareForFrontendStep(runProcessInput, process, frontendStep, processState);
if(loopTodo == LoopTodo.BREAK)
{
return;
}
}
else if(subStep instanceof QBackendStepMetaData backendStepMetaData)
{
RunBackendStepOutput runBackendStepOutput = runBackendStep(process, processState, stateKey, runProcessInput, runProcessOutput, backendStepMetaData, step);
Optional<String> nextStepName = runBackendStepOutput.getProcessState().getNextStepName();
if(nextStepName.isEmpty() && StringUtils.hasContent(stateMachineStep.getDefaultNextStepName()))
{
nextStepName = Optional.of(stateMachineStep.getDefaultNextStepName());
}
if(nextStepName.isPresent())
{
//////////////////////////////////////////////////////////////////////////////////////////////////////
// if we've been given a next-step-name, go to that step now. //
// it might be a backend-only stateMachineStep, in which case, we should run that backend step now. //
// or it might be a frontend-then-backend step, in which case, we want to go to that frontend step. //
// if we weren't given a next-step-name, then we should stay in the same state - either to finish //
// its sub-steps, or, to fall out of the loop and end the process. //
//////////////////////////////////////////////////////////////////////////////////////////////////////
processState.clearNextStepName();
processState.clearBackStepName();
runStateMachineStep(nextStepName.get(), process, processState, stateKey, runProcessInput, runProcessOutput, stackDepth + 1);
return;
}
}
else
{
//////////////////////////////////////////////////
// in case we have a different step type, throw //
//////////////////////////////////////////////////
throw (new QException("Unsure how to run a step of type: " + step.getClass().getName()));
}
}
if(!ranAnySubSteps)
{
if(StringUtils.hasContent(stateMachineStep.getDefaultNextStepName()))
{
runStateMachineStep(stateMachineStep.getDefaultNextStepName(), process, processState, stateKey, runProcessInput, runProcessOutput, stackDepth + 1);
}
}
}
/*******************************************************************************
**
*******************************************************************************/
public QStateMachineStep getStateMachineStepContainingSubStep(QProcessMetaData process, String stepName)
{
for(QStepMetaData step : process.getAllSteps().values())
{
if(step instanceof QStateMachineStep stateMachineStep)
{
for(QStepMetaData subStep : stateMachineStep.getSubSteps())
{
if(subStep.getName().equals(stepName))
{
return (stateMachineStep);
}
}
}
}
return (null);
}
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
@ -569,12 +335,12 @@ public class RunProcessAction
/////////////////////////////////////////////////// ///////////////////////////////////////////////////
runProcessInput.seedFromProcessState(optionalProcessState.get()); runProcessInput.seedFromProcessState(optionalProcessState.get());
///////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
// if we're restoring an old state, we can discard a previously stored processMetaDataAdjustment - // // if we're restoring an old state, we can discard a previously stored updatedFrontendStepList - //
// it is only needed on the transitional edge from a backend-step to a frontend step, but not // // it is only needed on the transitional edge from a backend-step to a frontend step, but not //
// in the other directly // // in the other directly //
///////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
optionalProcessState.get().setProcessMetaDataAdjustment(null); optionalProcessState.get().setUpdatedFrontendStepList(null);
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// if there were values from the caller, put those (back) in the request // // if there were values from the caller, put those (back) in the request //
@ -589,40 +355,16 @@ public class RunProcessAction
} }
ProcessState processState = optionalProcessState.get(); ProcessState processState = optionalProcessState.get();
processState.clearNextStepName();
return processState; return processState;
} }
/***************************************************************************
**
***************************************************************************/
private RunBackendStepOutput runBackendStep(QProcessMetaData process, ProcessState processState, UUIDAndTypeStateKey stateKey, RunProcessInput runProcessInput, RunProcessOutput runProcessOutput, QBackendStepMetaData backendStepMetaData, QStepMetaData step) throws Exception
{
///////////////////////
// Run backend steps //
///////////////////////
LOG.debug("Running backend step [" + step.getName() + "] in process [" + process.getName() + "]");
RunBackendStepOutput runBackendStepOutput = runBackendStep(runProcessInput, process, runProcessOutput, stateKey, backendStepMetaData, process, processState);
//////////////////////////////////////////////////////////////////////////////////////////////
// similarly, if the step produced a processMetaDataAdjustment, propagate that data outward //
//////////////////////////////////////////////////////////////////////////////////////////////
if(runBackendStepOutput.getProcessMetaDataAdjustment() != null)
{
LOG.debug("Process step [" + step.getName() + "] generated a ProcessMetaDataAdjustment [" + runBackendStepOutput.getProcessMetaDataAdjustment() + "]!");
runProcessOutput.setProcessMetaDataAdjustment(runBackendStepOutput.getProcessMetaDataAdjustment());
}
return runBackendStepOutput;
}
/******************************************************************************* /*******************************************************************************
** Run a single backend step. ** Run a single backend step.
*******************************************************************************/ *******************************************************************************/
RunBackendStepOutput runBackendStep(RunProcessInput runProcessInput, QProcessMetaData process, RunProcessOutput runProcessOutput, UUIDAndTypeStateKey stateKey, QBackendStepMetaData backendStep, QProcessMetaData qProcessMetaData, ProcessState processState) throws Exception protected RunBackendStepOutput 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());
@ -667,10 +409,8 @@ public class RunProcessAction
/******************************************************************************* /*******************************************************************************
** Get the list of steps which are eligible to run. ** Get the list of steps which are eligible to run.
**
** lastStep will be included in the list, or not, based on includeLastStep.
*******************************************************************************/ *******************************************************************************/
static List<QStepMetaData> getAvailableStepList(ProcessState processState, QProcessMetaData process, String lastStep, boolean includeLastStep) throws QException private List<QStepMetaData> getAvailableStepList(ProcessState processState, QProcessMetaData process, String lastStep) throws QException
{ {
if(lastStep == null) if(lastStep == null)
{ {
@ -697,10 +437,6 @@ public class RunProcessAction
if(stepName.equals(lastStep)) if(stepName.equals(lastStep))
{ {
foundLastStep = true; foundLastStep = true;
if(includeLastStep)
{
validStepNames.add(stepName);
}
} }
} }
return (stepNamesToSteps(process, validStepNames)); return (stepNamesToSteps(process, validStepNames));
@ -712,7 +448,7 @@ public class RunProcessAction
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
private static List<QStepMetaData> stepNamesToSteps(QProcessMetaData process, List<String> stepNames) throws QException private List<QStepMetaData> stepNamesToSteps(QProcessMetaData process, List<String> stepNames) throws QException
{ {
List<QStepMetaData> result = new ArrayList<>(); List<QStepMetaData> result = new ArrayList<>();
@ -781,13 +517,9 @@ public class RunProcessAction
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
protected String determineBasepullKeyValue(QProcessMetaData process, RunProcessInput runProcessInput, BasepullConfiguration basepullConfiguration) throws QException protected String determineBasepullKeyValue(QProcessMetaData process, BasepullConfiguration basepullConfiguration) throws QException
{ {
String basepullKeyValue = (basepullConfiguration.getKeyValue() != null) ? basepullConfiguration.getKeyValue() : process.getName(); String basepullKeyValue = (basepullConfiguration.getKeyValue() != null) ? basepullConfiguration.getKeyValue() : process.getName();
if(runProcessInput.getValueString(BASEPULL_KEY_VALUE) != null)
{
basepullKeyValue = runProcessInput.getValueString(BASEPULL_KEY_VALUE);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// if process specifies that it uses variants, look for that data in the session and append to our basepull key // // if process specifies that it uses variants, look for that data in the session and append to our basepull key //
@ -819,7 +551,7 @@ public class RunProcessAction
String basepullTableName = basepullConfiguration.getTableName(); String basepullTableName = basepullConfiguration.getTableName();
String basepullKeyFieldName = basepullConfiguration.getKeyField(); String basepullKeyFieldName = basepullConfiguration.getKeyField();
String basepullLastRunTimeFieldName = basepullConfiguration.getLastRunTimeFieldName(); String basepullLastRunTimeFieldName = basepullConfiguration.getLastRunTimeFieldName();
String basepullKeyValue = determineBasepullKeyValue(process, runProcessInput, basepullConfiguration); String basepullKeyValue = determineBasepullKeyValue(process, basepullConfiguration);
/////////////////////////////////////// ///////////////////////////////////////
// get the stored basepull timestamp // // get the stored basepull timestamp //
@ -899,7 +631,7 @@ public class RunProcessAction
String basepullKeyFieldName = basepullConfiguration.getKeyField(); String basepullKeyFieldName = basepullConfiguration.getKeyField();
String basepullLastRunTimeFieldName = basepullConfiguration.getLastRunTimeFieldName(); String basepullLastRunTimeFieldName = basepullConfiguration.getLastRunTimeFieldName();
Integer basepullHoursBackForInitialTimestamp = basepullConfiguration.getHoursBackForInitialTimestamp(); Integer basepullHoursBackForInitialTimestamp = basepullConfiguration.getHoursBackForInitialTimestamp();
String basepullKeyValue = determineBasepullKeyValue(process, runProcessInput, basepullConfiguration); String basepullKeyValue = determineBasepullKeyValue(process, basepullConfiguration);
/////////////////////////////////////// ///////////////////////////////////////
// get the stored basepull timestamp // // get the stored basepull timestamp //

View File

@ -42,7 +42,6 @@ import com.kingsrook.qqq.backend.core.actions.AbstractQActionFunction;
import com.kingsrook.qqq.backend.core.actions.async.AsyncRecordPipeLoop; import com.kingsrook.qqq.backend.core.actions.async.AsyncRecordPipeLoop;
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader; import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
import com.kingsrook.qqq.backend.core.actions.reporting.customizers.DataSourceQueryInputCustomizer; import com.kingsrook.qqq.backend.core.actions.reporting.customizers.DataSourceQueryInputCustomizer;
import com.kingsrook.qqq.backend.core.actions.reporting.customizers.ReportCustomRecordSourceInterface;
import com.kingsrook.qqq.backend.core.actions.reporting.customizers.ReportViewCustomizer; import com.kingsrook.qqq.backend.core.actions.reporting.customizers.ReportViewCustomizer;
import com.kingsrook.qqq.backend.core.actions.tables.CountAction; import com.kingsrook.qqq.backend.core.actions.tables.CountAction;
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction; import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
@ -63,8 +62,6 @@ import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.QueryHint; import com.kingsrook.qqq.backend.core.model.actions.tables.QueryHint;
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput; import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput; import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.CriteriaMissingInputValueBehavior;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.FilterUseCase;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.JoinsContext; import com.kingsrook.qqq.backend.core.model.actions.tables.query.JoinsContext;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
@ -305,19 +302,10 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
JoinsContext joinsContext = null; JoinsContext joinsContext = null;
if(dataSource != null) if(dataSource != null)
{ {
///////////////////////////////////////////////////////////////////////////////////////
// count records, if applicable, from the data source - for populating into the //
// countByDataSource map, as well as for checking if too many rows (e.g., for excel) //
///////////////////////////////////////////////////////////////////////////////////////
countDataSourceRecords(reportInput, dataSource, reportFormat);
///////////////////////////////////////////////////////////////////////////////////////////
// if there's a source table, set up a joins context, to use below for looking up fields //
///////////////////////////////////////////////////////////////////////////////////////////
if(StringUtils.hasContent(dataSource.getSourceTable())) if(StringUtils.hasContent(dataSource.getSourceTable()))
{ {
QQueryFilter queryFilter = dataSource.getQueryFilter() == null ? new QQueryFilter() : dataSource.getQueryFilter().clone(); joinsContext = new JoinsContext(QContext.getQInstance(), dataSource.getSourceTable(), cloneDataSourceQueryJoins(dataSource), dataSource.getQueryFilter() == null ? null : dataSource.getQueryFilter().clone());
joinsContext = new JoinsContext(QContext.getQInstance(), dataSource.getSourceTable(), dataSource.getQueryJoins(), queryFilter); countDataSourceRecords(reportInput, dataSource, reportFormat);
} }
} }
@ -341,7 +329,6 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
field.setName(column.getName()); field.setName(column.getName());
if(StringUtils.hasContent(column.getLabel())) if(StringUtils.hasContent(column.getLabel()))
{ {
field.setLabel(column.getLabel()); field.setLabel(column.getLabel());
} }
fields.add(field); fields.add(field);
@ -359,33 +346,23 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
*******************************************************************************/ *******************************************************************************/
private void countDataSourceRecords(ReportInput reportInput, QReportDataSource dataSource, ReportFormat reportFormat) throws QException private void countDataSourceRecords(ReportInput reportInput, QReportDataSource dataSource, ReportFormat reportFormat) throws QException
{ {
Integer count = null; QQueryFilter queryFilter = dataSource.getQueryFilter() == null ? new QQueryFilter() : dataSource.getQueryFilter().clone();
if(dataSource.getCustomRecordSource() != null) setInputValuesInQueryFilter(reportInput, queryFilter);
CountInput countInput = new CountInput();
countInput.setTableName(dataSource.getSourceTable());
countInput.setFilter(queryFilter);
countInput.setQueryJoins(cloneDataSourceQueryJoins(dataSource));
CountOutput countOutput = new CountAction().execute(countInput);
if(countOutput.getCount() != null)
{ {
// todo - add `count` method to interface? countByDataSource.put(dataSource.getName(), countOutput.getCount());
}
else if(StringUtils.hasContent(dataSource.getSourceTable()))
{
QQueryFilter queryFilter = dataSource.getQueryFilter() == null ? new QQueryFilter() : dataSource.getQueryFilter().clone();
setInputValuesInQueryFilter(reportInput, queryFilter);
CountInput countInput = new CountInput(); if(reportFormat.getMaxRows() != null && countOutput.getCount() > reportFormat.getMaxRows())
countInput.setTableName(dataSource.getSourceTable());
countInput.setFilter(queryFilter);
countInput.setQueryJoins(cloneDataSourceQueryJoins(dataSource));
CountOutput countOutput = new CountAction().execute(countInput);
count = countOutput.getCount();
}
if(count != null)
{
countByDataSource.put(dataSource.getName(), count);
if(reportFormat.getMaxRows() != null && count > reportFormat.getMaxRows())
{ {
throw (new QUserFacingException("The requested report would include more rows (" throw (new QUserFacingException("The requested report would include more rows ("
+ String.format("%,d", count) + ") than the maximum allowed (" + String.format("%,d", countOutput.getCount()) + ") than the maximum allowed ("
+ String.format("%,d", reportFormat.getMaxRows()) + ") for the selected file format (" + reportFormat + ").")); + String.format("%,d", reportFormat.getMaxRows()) + ") for the selected file format (" + reportFormat + ")."));
} }
} }
@ -446,19 +423,13 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
String tableLabel = ObjectUtils.tryElse(() -> QContext.getQInstance().getTable(dataSource.getSourceTable()).getLabel(), Objects.requireNonNullElse(dataSource.getSourceTable(), "")); String tableLabel = ObjectUtils.tryElse(() -> QContext.getQInstance().getTable(dataSource.getSourceTable()).getLabel(), Objects.requireNonNullElse(dataSource.getSourceTable(), ""));
AtomicInteger consumedCount = new AtomicInteger(0); AtomicInteger consumedCount = new AtomicInteger(0);
///////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// run a record pipe loop, over the query (or other data-supplier/source) for this data source // // run a record pipe loop, over the query for this data source //
///////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
RecordPipe recordPipe = new BufferedRecordPipe(1000); RecordPipe recordPipe = new BufferedRecordPipe(1000);
new AsyncRecordPipeLoop().run("Report[" + reportInput.getReportName() + "]", null, recordPipe, (callback) -> new AsyncRecordPipeLoop().run("Report[" + reportInput.getReportName() + "]", null, recordPipe, (callback) ->
{ {
if(dataSource.getCustomRecordSource() != null) if(dataSource.getSourceTable() != null)
{
ReportCustomRecordSourceInterface recordSource = QCodeLoader.getAdHoc(ReportCustomRecordSourceInterface.class, dataSource.getCustomRecordSource());
recordSource.execute(reportInput, dataSource, recordPipe);
return (true);
}
else if(dataSource.getSourceTable() != null)
{ {
QQueryFilter queryFilter = dataSource.getQueryFilter() == null ? new QQueryFilter() : dataSource.getQueryFilter().clone(); QQueryFilter queryFilter = dataSource.getQueryFilter() == null ? new QQueryFilter() : dataSource.getQueryFilter().clone();
setInputValuesInQueryFilter(reportInput, queryFilter); setInputValuesInQueryFilter(reportInput, queryFilter);
@ -616,56 +587,7 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
return; return;
} }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////// queryFilter.interpretValues(reportInput.getInputValues());
// for reports defined in meta-data, the established rule is, that missing input variable values are discarded. //
// but for non-meta-data reports (e.g., user-saved), we expect an exception for missing values. //
// so, set those use-cases up. //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
FilterUseCase filterUseCase;
if(StringUtils.hasContent(reportInput.getReportName()) && QContext.getQInstance().getReport(reportInput.getReportName()) != null)
{
filterUseCase = new ReportFromMetaDataFilterUseCase();
}
else
{
filterUseCase = new ReportNotFromMetaDataFilterUseCase();
}
queryFilter.interpretValues(reportInput.getInputValues(), filterUseCase);
}
/***************************************************************************
**
***************************************************************************/
private static class ReportFromMetaDataFilterUseCase implements FilterUseCase
{
/***************************************************************************
**
***************************************************************************/
@Override
public CriteriaMissingInputValueBehavior getDefaultCriteriaMissingInputValueBehavior()
{
return CriteriaMissingInputValueBehavior.REMOVE_FROM_FILTER;
}
}
/***************************************************************************
**
***************************************************************************/
private static class ReportNotFromMetaDataFilterUseCase implements FilterUseCase
{
/***************************************************************************
**
***************************************************************************/
@Override
public CriteriaMissingInputValueBehavior getDefaultCriteriaMissingInputValueBehavior()
{
return CriteriaMissingInputValueBehavior.THROW_EXCEPTION;
}
} }

View File

@ -1,43 +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.reporting.customizers;
import com.kingsrook.qqq.backend.core.actions.reporting.RecordPipe;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportInput;
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportDataSource;
/*******************************************************************************
** Interface to be implemented to do a custom source of data for a report
** (instead of just a query against a table).
*******************************************************************************/
public interface ReportCustomRecordSourceInterface
{
/***************************************************************************
** Given the report input, put records into the pipe, for the report.
***************************************************************************/
void execute(ReportInput reportInput, QReportDataSource reportDataSource, RecordPipe recordPipe) throws QException;
}

View File

@ -124,11 +124,10 @@ public class ExcelPoiBasedStreamingExportStreamer implements ExportStreamerInter
private Writer activeSheetWriter = null; private Writer activeSheetWriter = null;
private StreamedSheetWriter sheetWriter = null; private StreamedSheetWriter sheetWriter = null;
private QReportView currentView = null; private QReportView currentView = null;
private Map<String, List<QFieldMetaData>> fieldsPerView = new HashMap<>(); private Map<String, List<QFieldMetaData>> fieldsPerView = new HashMap<>();
private Map<String, Integer> rowsPerView = new HashMap<>(); private Map<String, Integer> rowsPerView = new HashMap<>();
private Map<String, String> labelViewsByName = new HashMap<>(); private Map<String, String> labelViewsByName = new HashMap<>();
private Map<String, String> sheetReferenceByViewName = new HashMap<>();
@ -181,7 +180,6 @@ public class ExcelPoiBasedStreamingExportStreamer implements ExportStreamerInter
String sheetReference = sheet.getPackagePart().getPartName().getName().substring(1); String sheetReference = sheet.getPackagePart().getPartName().getName().substring(1);
sheetMapByExcelReference.put(sheetReference, sheet); sheetMapByExcelReference.put(sheetReference, sheet);
sheetMapByViewName.put(view.getName(), sheet); sheetMapByViewName.put(view.getName(), sheet);
sheetReferenceByViewName.put(view.getName(), sheetReference);
sheetCounter++; sheetCounter++;
} }
@ -448,7 +446,7 @@ public class ExcelPoiBasedStreamingExportStreamer implements ExportStreamerInter
// - with a new output stream writer // // - with a new output stream writer //
// - and with a SpreadsheetWriter // // - and with a SpreadsheetWriter //
////////////////////////////////////////// //////////////////////////////////////////
zipOutputStream.putNextEntry(new ZipEntry(sheetReferenceByViewName.get(view.getName()))); zipOutputStream.putNextEntry(new ZipEntry("xl/worksheets/sheet" + this.sheetIndex++ + ".xml"));
activeSheetWriter = new OutputStreamWriter(zipOutputStream); activeSheetWriter = new OutputStreamWriter(zipOutputStream);
sheetWriter = new StreamedSheetWriter(activeSheetWriter); sheetWriter = new StreamedSheetWriter(activeSheetWriter);

View File

@ -320,7 +320,7 @@ public class DeleteAction
QTableMetaData table = deleteInput.getTable(); QTableMetaData table = deleteInput.getTable();
List<QRecord> primaryKeysNotFound = validateRecordsExistAndCanBeAccessed(deleteInput, oldRecordList.get()); List<QRecord> primaryKeysNotFound = validateRecordsExistAndCanBeAccessed(deleteInput, oldRecordList.get());
ValidateRecordSecurityLockHelper.validateSecurityFields(table, oldRecordList.get(), ValidateRecordSecurityLockHelper.Action.DELETE, deleteInput.getTransaction()); ValidateRecordSecurityLockHelper.validateSecurityFields(table, oldRecordList.get(), ValidateRecordSecurityLockHelper.Action.DELETE);
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// after all validations, run the pre-delete customizer, if there is one // // after all validations, run the pre-delete customizer, if there is one //

View File

@ -258,7 +258,7 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
} }
runPreInsertCustomizerIfItIsTime(insertInput, isPreview, preInsertCustomizer, AbstractPreInsertCustomizer.WhenToRun.BEFORE_SECURITY_CHECKS); runPreInsertCustomizerIfItIsTime(insertInput, isPreview, preInsertCustomizer, AbstractPreInsertCustomizer.WhenToRun.BEFORE_SECURITY_CHECKS);
ValidateRecordSecurityLockHelper.validateSecurityFields(insertInput.getTable(), insertInput.getRecords(), ValidateRecordSecurityLockHelper.Action.INSERT, insertInput.getTransaction()); ValidateRecordSecurityLockHelper.validateSecurityFields(insertInput.getTable(), insertInput.getRecords(), ValidateRecordSecurityLockHelper.Action.INSERT);
runPreInsertCustomizerIfItIsTime(insertInput, isPreview, preInsertCustomizer, AbstractPreInsertCustomizer.WhenToRun.AFTER_ALL_VALIDATIONS); runPreInsertCustomizerIfItIsTime(insertInput, isPreview, preInsertCustomizer, AbstractPreInsertCustomizer.WhenToRun.AFTER_ALL_VALIDATIONS);
} }

View File

@ -26,7 +26,6 @@ import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -51,10 +50,8 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperat
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryJoin;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
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.data.QRecordEntity;
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.fields.AdornmentType; import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData; import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
@ -67,7 +64,6 @@ import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleDispatcher;
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface; import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.ListingHash; import com.kingsrook.qqq.backend.core.utils.ListingHash;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
import com.kingsrook.qqq.backend.core.utils.ValueUtils; import com.kingsrook.qqq.backend.core.utils.ValueUtils;
@ -105,8 +101,6 @@ public class QueryAction
throw (new QException("A table named [" + queryInput.getTableName() + "] was not found in the active QInstance")); throw (new QException("A table named [" + queryInput.getTableName() + "] was not found in the active QInstance"));
} }
validateFieldNamesToInclude(queryInput);
QBackendMetaData backend = queryInput.getBackend(); QBackendMetaData backend = queryInput.getBackend();
postQueryRecordCustomizer = QCodeLoader.getTableCustomizer(table, TableCustomizers.POST_QUERY_RECORD.getRole()); postQueryRecordCustomizer = QCodeLoader.getTableCustomizer(table, TableCustomizers.POST_QUERY_RECORD.getRole());
this.queryInput = queryInput; this.queryInput = queryInput;
@ -164,125 +158,6 @@ public class QueryAction
/***************************************************************************
** if QueryInput contains a set of FieldNamesToInclude, then validate that
** those are known field names in the table being queried, or a selected
** queryJoin.
***************************************************************************/
static void validateFieldNamesToInclude(QueryInput queryInput) throws QException
{
Set<String> fieldNamesToInclude = queryInput.getFieldNamesToInclude();
if(fieldNamesToInclude == null)
{
////////////////////////////////
// null set means select all. //
////////////////////////////////
return;
}
if(fieldNamesToInclude.isEmpty())
{
/////////////////////////////////////
// empty set, however, is an error //
/////////////////////////////////////
throw (new QException("An empty set of fieldNamesToInclude was given as queryInput, which is not allowed."));
}
List<String> unrecognizedFieldNames = new ArrayList<>();
Map<String, QTableMetaData> selectedQueryJoins = null;
for(String fieldName : fieldNamesToInclude)
{
if(fieldName.contains("."))
{
////////////////////////////////////////////////
// handle names with dots - fields from joins //
////////////////////////////////////////////////
String[] parts = fieldName.split("\\.");
if(parts.length != 2)
{
unrecognizedFieldNames.add(fieldName);
}
else
{
String tableOrAlias = parts[0];
String fieldNamePart = parts[1];
////////////////////////////////////////////
// build map of queryJoins being selected //
////////////////////////////////////////////
if(selectedQueryJoins == null)
{
selectedQueryJoins = new HashMap<>();
for(QueryJoin queryJoin : CollectionUtils.nonNullList(queryInput.getQueryJoins()))
{
if(queryJoin.getSelect())
{
String joinTableOrAlias = queryJoin.getJoinTableOrItsAlias();
QTableMetaData joinTable = QContext.getQInstance().getTable(queryJoin.getJoinTable());
if(joinTable != null)
{
selectedQueryJoins.put(joinTableOrAlias, joinTable);
}
}
}
}
if(!selectedQueryJoins.containsKey(tableOrAlias))
{
///////////////////////////////////////////
// unrecognized tableOrAlias is an error //
///////////////////////////////////////////
unrecognizedFieldNames.add(fieldName);
}
else
{
QTableMetaData joinTable = selectedQueryJoins.get(tableOrAlias);
if(!joinTable.getFields().containsKey(fieldNamePart))
{
//////////////////////////////////////////////////////////
// unrecognized field within the join table is an error //
//////////////////////////////////////////////////////////
unrecognizedFieldNames.add(fieldName);
}
}
}
}
else
{
///////////////////////////////////////////////////////////////////////
// non-join fields - just ensure field name is in table's fields map //
///////////////////////////////////////////////////////////////////////
if(!queryInput.getTable().getFields().containsKey(fieldName))
{
unrecognizedFieldNames.add(fieldName);
}
}
}
if(!unrecognizedFieldNames.isEmpty())
{
throw (new QException("QueryInput contained " + unrecognizedFieldNames.size() + " unrecognized field name" + StringUtils.plural(unrecognizedFieldNames) + ": " + StringUtils.join(",", unrecognizedFieldNames)));
}
}
/*******************************************************************************
** shorthand way to call for the most common use-case, when you just want the
** entities to be returned, and you just want to pass in a table name and filter.
*******************************************************************************/
public static <T extends QRecordEntity> List<T> execute(String tableName, Class<T> entityClass, QQueryFilter filter) throws QException
{
QueryAction queryAction = new QueryAction();
QueryInput queryInput = new QueryInput();
queryInput.setTableName(tableName);
queryInput.setFilter(filter);
QueryOutput queryOutput = queryAction.execute(queryInput);
return (queryOutput.getRecordEntities(entityClass));
}
/******************************************************************************* /*******************************************************************************
** shorthand way to call for the most common use-case, when you just want the ** shorthand way to call for the most common use-case, when you just want the
** records to be returned, and you just want to pass in a table name and filter. ** records to be returned, and you just want to pass in a table name and filter.

View File

@ -261,7 +261,7 @@ public class UpdateAction
} }
else else
{ {
ValidateRecordSecurityLockHelper.validateSecurityFields(table, updateInput.getRecords(), ValidateRecordSecurityLockHelper.Action.UPDATE, updateInput.getTransaction()); ValidateRecordSecurityLockHelper.validateSecurityFields(table, updateInput.getRecords(), ValidateRecordSecurityLockHelper.Action.UPDATE);
} }
if(updateInput.getInputSource().shouldValidateRequiredFields()) if(updateInput.getInputSource().shouldValidateRequiredFields())
@ -374,7 +374,7 @@ public class UpdateAction
} }
} }
ValidateRecordSecurityLockHelper.validateSecurityFields(table, updateInput.getRecords(), ValidateRecordSecurityLockHelper.Action.UPDATE, updateInput.getTransaction()); ValidateRecordSecurityLockHelper.validateSecurityFields(table, updateInput.getRecords(), ValidateRecordSecurityLockHelper.Action.UPDATE);
for(QRecord record : page) for(QRecord record : page)
{ {

View File

@ -28,7 +28,6 @@ import java.util.Collections;
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.kingsrook.qqq.backend.core.actions.QBackendTransaction;
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction; import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
import com.kingsrook.qqq.backend.core.context.QContext; import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
@ -84,7 +83,7 @@ public class ValidateRecordSecurityLockHelper
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public static void validateSecurityFields(QTableMetaData table, List<QRecord> records, Action action, QBackendTransaction transaction) throws QException public static void validateSecurityFields(QTableMetaData table, List<QRecord> records, Action action) throws QException
{ {
MultiRecordSecurityLock locksToCheck = getRecordSecurityLocks(table, action); MultiRecordSecurityLock locksToCheck = getRecordSecurityLocks(table, action);
if(locksToCheck == null || CollectionUtils.nullSafeIsEmpty(locksToCheck.getLocks())) if(locksToCheck == null || CollectionUtils.nullSafeIsEmpty(locksToCheck.getLocks()))
@ -102,7 +101,7 @@ public class ValidateRecordSecurityLockHelper
// actually check lock values // // actually check lock values //
//////////////////////////////// ////////////////////////////////
Map<Serializable, RecordWithErrors> errorRecords = new HashMap<>(); Map<Serializable, RecordWithErrors> errorRecords = new HashMap<>();
evaluateRecordLocks(table, records, action, locksToCheck, errorRecords, new ArrayList<>(), madeUpPrimaryKeys, transaction); evaluateRecordLocks(table, records, action, locksToCheck, errorRecords, new ArrayList<>(), madeUpPrimaryKeys);
///////////////////////////////// /////////////////////////////////
// propagate errors to records // // propagate errors to records //
@ -142,7 +141,7 @@ public class ValidateRecordSecurityLockHelper
** BUT - WRITE locks - in their case, we read the record no matter what, and in ** BUT - WRITE locks - in their case, we read the record no matter what, and in
** here we need to verify we have a key that allows us to WRITE the record. ** here we need to verify we have a key that allows us to WRITE the record.
*******************************************************************************/ *******************************************************************************/
private static void evaluateRecordLocks(QTableMetaData table, List<QRecord> records, Action action, RecordSecurityLock recordSecurityLock, Map<Serializable, RecordWithErrors> errorRecords, List<Integer> treePosition, Map<Serializable, QRecord> madeUpPrimaryKeys, QBackendTransaction transaction) throws QException private static void evaluateRecordLocks(QTableMetaData table, List<QRecord> records, Action action, RecordSecurityLock recordSecurityLock, Map<Serializable, RecordWithErrors> errorRecords, List<Integer> treePosition, Map<Serializable, QRecord> madeUpPrimaryKeys) throws QException
{ {
if(recordSecurityLock instanceof MultiRecordSecurityLock multiRecordSecurityLock) if(recordSecurityLock instanceof MultiRecordSecurityLock multiRecordSecurityLock)
{ {
@ -153,7 +152,7 @@ public class ValidateRecordSecurityLockHelper
for(RecordSecurityLock childLock : CollectionUtils.nonNullList(multiRecordSecurityLock.getLocks())) for(RecordSecurityLock childLock : CollectionUtils.nonNullList(multiRecordSecurityLock.getLocks()))
{ {
treePosition.add(i); treePosition.add(i);
evaluateRecordLocks(table, records, action, childLock, errorRecords, treePosition, madeUpPrimaryKeys, transaction); evaluateRecordLocks(table, records, action, childLock, errorRecords, treePosition, madeUpPrimaryKeys);
treePosition.remove(treePosition.size() - 1); treePosition.remove(treePosition.size() - 1);
i++; i++;
} }
@ -226,7 +225,6 @@ public class ValidateRecordSecurityLockHelper
// query will be like (fkey1=? and fkey2=?) OR (fkey1=? and fkey2=?) OR (fkey1=? and fkey2=?) // // query will be like (fkey1=? and fkey2=?) OR (fkey1=? and fkey2=?) OR (fkey1=? and fkey2=?) //
//////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////
QueryInput queryInput = new QueryInput(); QueryInput queryInput = new QueryInput();
queryInput.setTransaction(transaction);
queryInput.setTableName(leftMostJoin.getLeftTable()); queryInput.setTableName(leftMostJoin.getLeftTable());
QQueryFilter filter = new QQueryFilter().withBooleanOperator(QQueryFilter.BooleanOperator.OR); QQueryFilter filter = new QQueryFilter().withBooleanOperator(QQueryFilter.BooleanOperator.OR);
queryInput.setFilter(filter); queryInput.setFilter(filter);

View File

@ -28,6 +28,7 @@ import java.time.LocalDateTime;
import java.time.LocalTime; import java.time.LocalTime;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -467,8 +468,7 @@ public class QValueFormatter
{ {
for(QFieldMetaData field : table.getFields().values()) for(QFieldMetaData field : table.getFields().values())
{ {
Optional<FieldAdornment> fileDownloadAdornment = field.getAdornment(AdornmentType.FILE_DOWNLOAD); if(field.getType().equals(QFieldType.BLOB))
if(fileDownloadAdornment.isPresent())
{ {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// file name comes from: // // file name comes from: //
@ -478,7 +478,20 @@ public class QValueFormatter
// - tableLabel primaryKey fieldLabel // // - tableLabel primaryKey fieldLabel //
// - and - if the FILE_DOWNLOAD adornment had a DEFAULT_EXTENSION, then it gets added (preceded by a dot) // // - and - if the FILE_DOWNLOAD adornment had a DEFAULT_EXTENSION, then it gets added (preceded by a dot) //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Map<String, Serializable> adornmentValues = fileDownloadAdornment.get().getValues(); Optional<FieldAdornment> fileDownloadAdornment = field.getAdornment(AdornmentType.FILE_DOWNLOAD);
Map<String, Serializable> adornmentValues = Collections.emptyMap();
if(fileDownloadAdornment.isPresent())
{
adornmentValues = fileDownloadAdornment.get().getValues();
}
else
{
///////////////////////////////////////////////////////
// don't change blobs unless they are file-downloads //
///////////////////////////////////////////////////////
continue;
}
String fileNameField = ValueUtils.getValueAsString(adornmentValues.get(AdornmentType.FileDownloadValues.FILE_NAME_FIELD)); String fileNameField = ValueUtils.getValueAsString(adornmentValues.get(AdornmentType.FileDownloadValues.FILE_NAME_FIELD));
String fileNameFormat = ValueUtils.getValueAsString(adornmentValues.get(AdornmentType.FileDownloadValues.FILE_NAME_FORMAT)); String fileNameFormat = ValueUtils.getValueAsString(adornmentValues.get(AdornmentType.FileDownloadValues.FILE_NAME_FORMAT));
@ -529,13 +542,7 @@ public class QValueFormatter
} }
} }
///////////////////////////////////////////// record.setValue(field.getName(), "/data/" + table.getName() + "/" + primaryKey + "/" + field.getName() + "/" + fileName);
// if field type is blob, update its value //
/////////////////////////////////////////////
if(QFieldType.BLOB.equals(field.getType()))
{
record.setValue(field.getName(), "/data/" + table.getName() + "/" + primaryKey + "/" + field.getName() + "/" + fileName);
}
record.setDisplayValue(field.getName(), fileName); record.setDisplayValue(field.getName(), fileName);
} }
} }

View File

@ -26,12 +26,8 @@ import java.io.Serializable;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader; import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction; import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
import com.kingsrook.qqq.backend.core.context.QContext; import com.kingsrook.qqq.backend.core.context.QContext;
@ -54,7 +50,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.StringUtils; 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 static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair; import org.apache.commons.lang.NotImplementedException;
/******************************************************************************* /*******************************************************************************
@ -65,9 +61,6 @@ public class SearchPossibleValueSourceAction
{ {
private static final QLogger LOG = QLogger.getLogger(SearchPossibleValueSourceAction.class); private static final QLogger LOG = QLogger.getLogger(SearchPossibleValueSourceAction.class);
private static final Set<String> warnedAboutUnexpectedValueField = Collections.synchronizedSet(new HashSet<>());
private static final Set<String> warnedAboutUnexpectedNoOfFieldsToSearchByLabel = Collections.synchronizedSet(new HashSet<>());
private QPossibleValueTranslator possibleValueTranslator; private QPossibleValueTranslator possibleValueTranslator;
@ -117,7 +110,6 @@ public class SearchPossibleValueSourceAction
List<Serializable> matchingIds = new ArrayList<>(); List<Serializable> matchingIds = new ArrayList<>();
List<?> inputIdsAsCorrectType = convertInputIdsToEnumIdType(possibleValueSource, input.getIdList()); List<?> inputIdsAsCorrectType = convertInputIdsToEnumIdType(possibleValueSource, input.getIdList());
Set<String> labels = null;
for(QPossibleValue<?> possibleValue : possibleValueSource.getEnumValues()) for(QPossibleValue<?> possibleValue : possibleValueSource.getEnumValues())
{ {
@ -130,24 +122,12 @@ public class SearchPossibleValueSourceAction
match = true; match = true;
} }
} }
else if(input.getLabelList() != null)
{
if(labels == null)
{
labels = input.getLabelList().stream().filter(Objects::nonNull).map(l -> l.toLowerCase()).collect(Collectors.toSet());
}
if(labels.contains(possibleValue.getLabel().toLowerCase()))
{
match = true;
}
}
else else
{ {
if(StringUtils.hasContent(input.getSearchTerm())) if(StringUtils.hasContent(input.getSearchTerm()))
{ {
match = (Objects.equals(ValueUtils.getValueAsString(possibleValue.getId()).toLowerCase(), input.getSearchTerm().toLowerCase()) match = (Objects.equals(ValueUtils.getValueAsString(possibleValue.getId()).toLowerCase(), input.getSearchTerm().toLowerCase())
|| possibleValue.getLabel().toLowerCase().startsWith(input.getSearchTerm().toLowerCase())); || possibleValue.getLabel().toLowerCase().startsWith(input.getSearchTerm().toLowerCase()));
} }
else else
{ {
@ -188,37 +168,21 @@ public class SearchPossibleValueSourceAction
Object anIdFromTheEnum = possibleValueSource.getEnumValues().get(0).getId(); Object anIdFromTheEnum = possibleValueSource.getEnumValues().get(0).getId();
for(Serializable inputId : inputIdList) if(anIdFromTheEnum instanceof Integer)
{ {
Object properlyTypedId = null; inputIdList.forEach(id -> rs.add(ValueUtils.getValueAsInteger(id)));
try }
{ else if(anIdFromTheEnum instanceof String)
if(anIdFromTheEnum instanceof Integer) {
{ inputIdList.forEach(id -> rs.add(ValueUtils.getValueAsString(id)));
properlyTypedId = ValueUtils.getValueAsInteger(inputId); }
} else if(anIdFromTheEnum instanceof Boolean)
else if(anIdFromTheEnum instanceof String) {
{ inputIdList.forEach(id -> rs.add(ValueUtils.getValueAsBoolean(id)));
properlyTypedId = ValueUtils.getValueAsString(inputId); }
} else
else if(anIdFromTheEnum instanceof Boolean) {
{ LOG.warn("Unexpected type [" + anIdFromTheEnum.getClass().getSimpleName() + "] for ids in enum: " + possibleValueSource.getName());
properlyTypedId = ValueUtils.getValueAsBoolean(inputId);
}
else
{
LOG.warn("Unexpected type [" + anIdFromTheEnum.getClass().getSimpleName() + "] for ids in enum: " + possibleValueSource.getName());
}
}
catch(Exception e)
{
LOG.debug("Error converting possible value id to expected id type", e, logPair("value", inputId));
}
if (properlyTypedId != null)
{
rs.add(properlyTypedId);
}
} }
return (rs); return (rs);
@ -245,53 +209,6 @@ public class SearchPossibleValueSourceAction
{ {
queryFilter.addCriteria(new QFilterCriteria(table.getPrimaryKeyField(), QCriteriaOperator.IN, input.getIdList())); queryFilter.addCriteria(new QFilterCriteria(table.getPrimaryKeyField(), QCriteriaOperator.IN, input.getIdList()));
} }
else if(input.getLabelList() != null)
{
List<String> fieldNames = new ArrayList<>();
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// the 'value fields' will either be 'id' or 'label' (which means, use the fields from the tableMetaData's label fields) //
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
for(String valueField : possibleValueSource.getValueFields())
{
if("id".equals(valueField))
{
fieldNames.add(table.getPrimaryKeyField());
}
else if("label".equals(valueField))
{
if(table.getRecordLabelFields() != null)
{
fieldNames.addAll(table.getRecordLabelFields());
}
}
else
{
String message = "Unexpected valueField defined in possibleValueSource when searching possibleValueSource by label (required: 'id' or 'label')";
if(!warnedAboutUnexpectedValueField.contains(possibleValueSource.getName()))
{
LOG.warn(message, logPair("valueField", valueField), logPair("possibleValueSource", possibleValueSource.getName()));
warnedAboutUnexpectedValueField.add(possibleValueSource.getName());
}
output.setWarning(message);
}
}
if(fieldNames.size() == 1)
{
queryFilter.addCriteria(new QFilterCriteria(fieldNames.get(0), QCriteriaOperator.IN, input.getLabelList()));
}
else
{
String message = "Unexpected number of fields found for searching possibleValueSource by label (required: 1, found: " + fieldNames.size() + ")";
if(!warnedAboutUnexpectedNoOfFieldsToSearchByLabel.contains(possibleValueSource.getName()))
{
LOG.warn(message);
warnedAboutUnexpectedNoOfFieldsToSearchByLabel.add(possibleValueSource.getName());
}
output.setWarning(message);
}
}
else else
{ {
String searchTerm = input.getSearchTerm(); String searchTerm = input.getSearchTerm();
@ -343,6 +260,9 @@ public class SearchPossibleValueSourceAction
} }
} }
// todo - skip & limit as params
queryFilter.setLimit(250);
/////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////
// if given a default filter, make it the 'top level' filter and the one we just created a subfilter // // if given a default filter, make it the 'top level' filter and the one we just created a subfilter //
/////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////
@ -352,9 +272,6 @@ public class SearchPossibleValueSourceAction
queryFilter = input.getDefaultQueryFilter(); queryFilter = input.getDefaultQueryFilter();
} }
queryFilter.setLimit(input.getLimit());
queryFilter.setSkip(input.getSkip());
queryFilter.setOrderBys(possibleValueSource.getOrderByFields()); queryFilter.setOrderBys(possibleValueSource.getOrderByFields());
queryInput.setFilter(queryFilter); queryInput.setFilter(queryFilter);
@ -371,7 +288,7 @@ public class SearchPossibleValueSourceAction
fieldName = table.getPrimaryKeyField(); fieldName = table.getPrimaryKeyField();
} }
List<Serializable> ids = queryOutput.getRecords().stream().map(r -> r.getValue(fieldName)).toList(); List<Serializable> ids = queryOutput.getRecords().stream().map(r -> r.getValue(fieldName)).toList();
List<QPossibleValue<?>> qPossibleValues = possibleValueTranslator.buildTranslatedPossibleValueList(possibleValueSource, ids); List<QPossibleValue<?>> qPossibleValues = possibleValueTranslator.buildTranslatedPossibleValueList(possibleValueSource, ids);
output.setResults(qPossibleValues); output.setResults(qPossibleValues);
@ -384,7 +301,7 @@ public class SearchPossibleValueSourceAction
** **
*******************************************************************************/ *******************************************************************************/
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({ "rawtypes", "unchecked" })
private SearchPossibleValueSourceOutput searchPossibleValueCustom(SearchPossibleValueSourceInput input, QPossibleValueSource possibleValueSource) throws QException private SearchPossibleValueSourceOutput searchPossibleValueCustom(SearchPossibleValueSourceInput input, QPossibleValueSource possibleValueSource)
{ {
try try
{ {
@ -397,10 +314,11 @@ public class SearchPossibleValueSourceAction
} }
catch(Exception e) catch(Exception e)
{ {
String message = "Error sending searching custom possible value source [" + input.getPossibleValueSourceName() + "]"; // LOG.warn("Error sending [" + value + "] for field [" + field + "] through custom code for PVS [" + field.getPossibleValueSourceName() + "]", e);
LOG.warn(message, e);
throw (new QException(message));
} }
throw new NotImplementedException("Not impleemnted");
// return (null);
} }
} }

View File

@ -1,55 +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.instances;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerHelper;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
/*******************************************************************************
** Version of AbstractQQQApplication that assumes all meta-data is produced
** by MetaDataProducers in a single package.
*******************************************************************************/
public abstract class AbstractMetaDataProducerBasedQQQApplication extends AbstractQQQApplication
{
/***************************************************************************
**
***************************************************************************/
public abstract String getMetaDataPackageName();
/***************************************************************************
**
***************************************************************************/
@Override
public QInstance defineQInstance() throws QException
{
QInstance qInstance = new QInstance();
MetaDataProducerHelper.processAllMetaDataProducersInPackage(qInstance, getMetaDataPackageName());
return (qInstance);
}
}

View File

@ -1,78 +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.instances;
import java.util.ArrayList;
import java.util.List;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.exceptions.QInstanceValidationException;
import com.kingsrook.qqq.backend.core.instances.validation.plugins.QInstanceValidatorPluginInterface;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
/*******************************************************************************
** Base class to provide the definition of a QQQ-based application.
**
** Essentially, just how to define its meta-data - in the form of a QInstance.
**
** Also provides means to define the instance validation plugins to be used.
*******************************************************************************/
public abstract class AbstractQQQApplication
{
/***************************************************************************
**
***************************************************************************/
public abstract QInstance defineQInstance() throws QException;
/***************************************************************************
**
***************************************************************************/
public QInstance defineValidatedQInstance() throws QException, QInstanceValidationException
{
QInstance qInstance = defineQInstance();
QInstanceValidator.removeAllValidatorPlugins();
for(QInstanceValidatorPluginInterface<?> validatorPlugin : CollectionUtils.nonNullList(getValidatorPlugins()))
{
QInstanceValidator.addValidatorPlugin(validatorPlugin);
}
QInstanceValidator qInstanceValidator = new QInstanceValidator();
qInstanceValidator.validate(qInstance);
return (qInstance);
}
/***************************************************************************
**
***************************************************************************/
protected List<QInstanceValidatorPluginInterface<?>> getValidatorPlugins()
{
return new ArrayList<>();
}
}

View File

@ -42,7 +42,6 @@ import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference; import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataInterface; import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataInterface;
import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType; import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType;
import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType.FileUploadAdornment;
import com.kingsrook.qqq.backend.core.model.metadata.fields.DynamicDefaultValueBehavior; import com.kingsrook.qqq.backend.core.model.metadata.fields.DynamicDefaultValueBehavior;
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment; import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData; import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
@ -55,12 +54,10 @@ import com.kingsrook.qqq.backend.core.model.metadata.permissions.MetaDataWithPer
import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRules; import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRules;
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource; import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType; import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QComponentType; import com.kingsrook.qqq.backend.core.model.metadata.processes.QComponentType;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendComponentMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendComponentMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData; 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.processes.QStateMachineStep;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QSupplementalProcessMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QSupplementalProcessMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportDataSource; import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportDataSource;
@ -77,11 +74,6 @@ import com.kingsrook.qqq.backend.core.processes.implementations.bulk.edit.BulkEd
import com.kingsrook.qqq.backend.core.processes.implementations.bulk.edit.BulkEditTransformStep; import com.kingsrook.qqq.backend.core.processes.implementations.bulk.edit.BulkEditTransformStep;
import com.kingsrook.qqq.backend.core.processes.implementations.bulk.insert.BulkInsertExtractStep; import com.kingsrook.qqq.backend.core.processes.implementations.bulk.insert.BulkInsertExtractStep;
import com.kingsrook.qqq.backend.core.processes.implementations.bulk.insert.BulkInsertLoadStep; import com.kingsrook.qqq.backend.core.processes.implementations.bulk.insert.BulkInsertLoadStep;
import com.kingsrook.qqq.backend.core.processes.implementations.bulk.insert.BulkInsertPrepareFileMappingStep;
import com.kingsrook.qqq.backend.core.processes.implementations.bulk.insert.BulkInsertPrepareFileUploadStep;
import com.kingsrook.qqq.backend.core.processes.implementations.bulk.insert.BulkInsertPrepareValueMappingStep;
import com.kingsrook.qqq.backend.core.processes.implementations.bulk.insert.BulkInsertReceiveFileMappingStep;
import com.kingsrook.qqq.backend.core.processes.implementations.bulk.insert.BulkInsertReceiveValueMappingStep;
import com.kingsrook.qqq.backend.core.processes.implementations.bulk.insert.BulkInsertTransformStep; import com.kingsrook.qqq.backend.core.processes.implementations.bulk.insert.BulkInsertTransformStep;
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.ExtractViaQueryStep; import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.ExtractViaQueryStep;
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.StreamedETLWithFrontendProcess; import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.StreamedETLWithFrontendProcess;
@ -418,27 +410,10 @@ public class QInstanceEnricher
** **
*******************************************************************************/ *******************************************************************************/
private void enrichStep(QStepMetaData step) private void enrichStep(QStepMetaData step)
{
enrichStep(step, false);
}
/***************************************************************************
**
***************************************************************************/
private void enrichStep(QStepMetaData step, boolean isSubStep)
{ {
if(!StringUtils.hasContent(step.getLabel())) if(!StringUtils.hasContent(step.getLabel()))
{ {
if(isSubStep && (step.getName().endsWith(".backend") || step.getName().endsWith(".frontend"))) step.setLabel(nameToLabel(step.getName()));
{
step.setLabel(nameToLabel(step.getName().replaceFirst("\\.(backend|frontend)", "")));
}
else
{
step.setLabel(nameToLabel(step.getName()));
}
} }
step.getInputFields().forEach(this::enrichField); step.getInputFields().forEach(this::enrichField);
@ -459,13 +434,6 @@ public class QInstanceEnricher
frontendStepMetaData.getRecordListFields().forEach(this::enrichField); frontendStepMetaData.getRecordListFields().forEach(this::enrichField);
} }
} }
else if(step instanceof QStateMachineStep stateMachineStep)
{
for(QStepMetaData subStep : CollectionUtils.nonNullList(stateMachineStep.getSubSteps()))
{
enrichStep(subStep, true);
}
}
} }
@ -840,7 +808,7 @@ public class QInstanceEnricher
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public void defineTableBulkInsert(QInstance qInstance, QTableMetaData table, String processName) private void defineTableBulkInsert(QInstance qInstance, QTableMetaData table, String processName)
{ {
Map<String, Serializable> values = new HashMap<>(); Map<String, Serializable> values = new HashMap<>();
values.put(StreamedETLWithFrontendProcess.FIELD_DESTINATION_TABLE, table.getName()); values.put(StreamedETLWithFrontendProcess.FIELD_DESTINATION_TABLE, table.getName());
@ -852,7 +820,6 @@ public class QInstanceEnricher
values values
) )
.withName(processName) .withName(processName)
.withIcon(new QIcon().withName("library_add"))
.withLabel(table.getLabel() + " Bulk Insert") .withLabel(table.getLabel() + " Bulk Insert")
.withTableName(table.getName()) .withTableName(table.getName())
.withIsHidden(true) .withIsHidden(true)
@ -883,74 +850,18 @@ public class QInstanceEnricher
.map(QFieldMetaData::getLabel) .map(QFieldMetaData::getLabel)
.collect(Collectors.joining(", ")); .collect(Collectors.joining(", "));
QBackendStepMetaData prepareFileUploadStep = new QBackendStepMetaData()
.withName("prepareFileUpload")
.withCode(new QCodeReference(BulkInsertPrepareFileUploadStep.class));
QFrontendStepMetaData uploadScreen = new QFrontendStepMetaData() QFrontendStepMetaData uploadScreen = new QFrontendStepMetaData()
.withName("upload") .withName("upload")
.withLabel("Upload File") .withLabel("Upload File")
.withFormField(new QFieldMetaData("theFile", QFieldType.BLOB) .withFormField(new QFieldMetaData("theFile", QFieldType.BLOB).withLabel(table.getLabel() + " File").withIsRequired(true))
.withFieldAdornment(FileUploadAdornment.newFieldAdornment() .withComponent(new QFrontendComponentMetaData()
.withValue(FileUploadAdornment.formatDragAndDrop()) .withType(QComponentType.HELP_TEXT)
.withValue(FileUploadAdornment.widthFull())) .withValue("previewText", "file upload instructions")
.withLabel(table.getLabel() + " File") .withValue("text", "Upload a CSV file with the following columns:\n" + fieldsForHelpText))
.withIsRequired(true))
.withComponent(new QFrontendComponentMetaData().withType(QComponentType.HTML))
.withComponent(new QFrontendComponentMetaData().withType(QComponentType.EDIT_FORM)); .withComponent(new QFrontendComponentMetaData().withType(QComponentType.EDIT_FORM));
QBackendStepMetaData prepareFileMappingStep = new QBackendStepMetaData() process.addStep(0, uploadScreen);
.withName("prepareFileMapping") process.getFrontendStep("review").setRecordListFields(editableFields);
.withCode(new QCodeReference(BulkInsertPrepareFileMappingStep.class));
QFrontendStepMetaData fileMappingScreen = new QFrontendStepMetaData()
.withName("fileMapping")
.withLabel("File Mapping")
.withBackStepName("upload")
.withComponent(new QFrontendComponentMetaData().withType(QComponentType.BULK_LOAD_FILE_MAPPING_FORM));
QBackendStepMetaData receiveFileMappingStep = new QBackendStepMetaData()
.withName("receiveFileMapping")
.withCode(new QCodeReference(BulkInsertReceiveFileMappingStep.class));
QBackendStepMetaData prepareValueMappingStep = new QBackendStepMetaData()
.withName("prepareValueMapping")
.withCode(new QCodeReference(BulkInsertPrepareValueMappingStep.class));
QFrontendStepMetaData valueMappingScreen = new QFrontendStepMetaData()
.withName("valueMapping")
.withLabel("Value Mapping")
.withBackStepName("prepareFileMapping")
.withComponent(new QFrontendComponentMetaData().withType(QComponentType.BULK_LOAD_VALUE_MAPPING_FORM));
QBackendStepMetaData receiveValueMappingStep = new QBackendStepMetaData()
.withName("receiveValueMapping")
.withCode(new QCodeReference(BulkInsertReceiveValueMappingStep.class));
int i = 0;
process.addStep(i++, prepareFileUploadStep);
process.addStep(i++, uploadScreen);
process.addStep(i++, prepareFileMappingStep);
process.addStep(i++, fileMappingScreen);
process.addStep(i++, receiveFileMappingStep);
process.addStep(i++, prepareValueMappingStep);
process.addStep(i++, valueMappingScreen);
process.addStep(i++, receiveValueMappingStep);
process.getFrontendStep(StreamedETLWithFrontendProcess.STEP_NAME_REVIEW).setRecordListFields(editableFields);
//////////////////////////////////////////////////////////////////////////////////////////
// put the bulk-load profile form (e.g., for saving it) on the review & result screens) //
//////////////////////////////////////////////////////////////////////////////////////////
process.getFrontendStep(StreamedETLWithFrontendProcess.STEP_NAME_REVIEW)
.withBackStepName("prepareFileMapping")
.getComponents().add(0, new QFrontendComponentMetaData().withType(QComponentType.BULK_LOAD_PROFILE_FORM));
process.getFrontendStep(StreamedETLWithFrontendProcess.STEP_NAME_RESULT)
.getComponents().add(0, new QFrontendComponentMetaData().withType(QComponentType.BULK_LOAD_PROFILE_FORM));
qInstance.addProcess(process); qInstance.addProcess(process);
} }

View File

@ -43,9 +43,7 @@ import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationHandler
import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizers; import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizers;
import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.AbstractWidgetRenderer; import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.AbstractWidgetRenderer;
import com.kingsrook.qqq.backend.core.actions.metadata.JoinGraph; import com.kingsrook.qqq.backend.core.actions.metadata.JoinGraph;
import com.kingsrook.qqq.backend.core.actions.metadata.MetaDataFilterInterface;
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep; import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
import com.kingsrook.qqq.backend.core.actions.reporting.customizers.ReportCustomRecordSourceInterface;
import com.kingsrook.qqq.backend.core.actions.scripts.TestScriptActionInterface; import com.kingsrook.qqq.backend.core.actions.scripts.TestScriptActionInterface;
import com.kingsrook.qqq.backend.core.actions.values.QCustomPossibleValueProvider; import com.kingsrook.qqq.backend.core.actions.values.QCustomPossibleValueProvider;
import com.kingsrook.qqq.backend.core.exceptions.QInstanceValidationException; import com.kingsrook.qqq.backend.core.exceptions.QInstanceValidationException;
@ -75,7 +73,6 @@ import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData; import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppSection; import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppSection;
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource; import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
@ -187,7 +184,6 @@ public class QInstanceValidator
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
try try
{ {
validateInstanceAttributes(qInstance);
validateBackends(qInstance); validateBackends(qInstance);
validateAuthentication(qInstance); validateAuthentication(qInstance);
validateAutomationProviders(qInstance); validateAutomationProviders(qInstance);
@ -228,19 +224,6 @@ public class QInstanceValidator
/***************************************************************************
**
***************************************************************************/
private void validateInstanceAttributes(QInstance qInstance)
{
if(qInstance.getMetaDataFilter() != null)
{
validateSimpleCodeReference("Instance metaDataFilter ", qInstance.getMetaDataFilter(), MetaDataFilterInterface.class);
}
}
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
@ -944,8 +927,13 @@ public class QInstanceValidator
assertCondition(Objects.equals(fieldName, field.getName()), assertCondition(Objects.equals(fieldName, field.getName()),
"Inconsistent naming in table " + tableName + " for field " + fieldName + "/" + field.getName() + "."); "Inconsistent naming in table " + tableName + " for field " + fieldName + "/" + field.getName() + ".");
if(field.getPossibleValueSourceName() != null)
{
assertCondition(qInstance.getPossibleValueSource(field.getPossibleValueSourceName()) != null,
"Unrecognized possibleValueSourceName " + field.getPossibleValueSourceName() + " in table " + tableName + " for field " + fieldName + ".");
}
String prefix = "Field " + fieldName + " in table " + tableName + " "; String prefix = "Field " + fieldName + " in table " + tableName + " ";
validateFieldPossibleValueSourceAttributes(qInstance, field, prefix);
/////////////////////////////////////////////////// ///////////////////////////////////////////////////
// validate things we know about field behaviors // // validate things we know about field behaviors //
@ -1050,31 +1038,6 @@ public class QInstanceValidator
/***************************************************************************
**
***************************************************************************/
private void validateFieldPossibleValueSourceAttributes(QInstance qInstance, QFieldMetaData field, String prefix)
{
if(field.getPossibleValueSourceName() != null)
{
assertCondition(qInstance.getPossibleValueSource(field.getPossibleValueSourceName()) != null,
prefix + "has an unrecognized possibleValueSourceName " + field.getPossibleValueSourceName());
assertCondition(field.getInlinePossibleValueSource() == null, prefix.trim() + " has both a possibleValueSourceName and an inlinePossibleValueSource, which is not allowed.");
}
if(field.getInlinePossibleValueSource() != null)
{
String name = "inlinePossibleValueSource for " + prefix.trim();
if(assertCondition(QPossibleValueSourceType.ENUM.equals(field.getInlinePossibleValueSource().getType()), name + " must have a type of ENUM."))
{
validatePossibleValueSource(qInstance, name, field.getInlinePossibleValueSource());
}
}
}
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
@ -1582,16 +1545,6 @@ public class QInstanceValidator
} }
} }
for(QFieldMetaData field : process.getInputFields())
{
validateFieldPossibleValueSourceAttributes(qInstance, field, "Process " + processName + ", input field " + field.getName());
}
for(QFieldMetaData field : process.getOutputFields())
{
validateFieldPossibleValueSourceAttributes(qInstance, field, "Process " + processName + ", output field " + field.getName());
}
if(process.getCancelStep() != null) if(process.getCancelStep() != null)
{ {
if(assertCondition(process.getCancelStep().getCode() != null, "Cancel step is missing a code reference, in process " + processName)) if(assertCondition(process.getCancelStep().getCode() != null, "Cancel step is missing a code reference, in process " + processName))
@ -1707,12 +1660,9 @@ public class QInstanceValidator
String dataSourceErrorPrefix = "Report " + reportName + " data source " + dataSource.getName() + " "; String dataSourceErrorPrefix = "Report " + reportName + " data source " + dataSource.getName() + " ";
boolean hasASource = false;
if(StringUtils.hasContent(dataSource.getSourceTable())) if(StringUtils.hasContent(dataSource.getSourceTable()))
{ {
hasASource = true; assertCondition(dataSource.getStaticDataSupplier() == null, dataSourceErrorPrefix + "has both a sourceTable and a staticDataSupplier (exactly 1 is required).");
assertCondition(dataSource.getStaticDataSupplier() == null, dataSourceErrorPrefix + "has both a sourceTable and a staticDataSupplier (not compatible together).");
if(assertCondition(qInstance.getTable(dataSource.getSourceTable()) != null, dataSourceErrorPrefix + "source table " + dataSource.getSourceTable() + " is not a table in this instance.")) if(assertCondition(qInstance.getTable(dataSource.getSourceTable()) != null, dataSourceErrorPrefix + "source table " + dataSource.getSourceTable() + " is not a table in this instance."))
{ {
if(dataSource.getQueryFilter() != null) if(dataSource.getQueryFilter() != null)
@ -1721,21 +1671,14 @@ public class QInstanceValidator
} }
} }
} }
else if(dataSource.getStaticDataSupplier() != null)
if(dataSource.getStaticDataSupplier() != null)
{ {
assertCondition(dataSource.getCustomRecordSource() == null, dataSourceErrorPrefix + "has both a staticDataSupplier and a customRecordSource (not compatible together).");
hasASource = true;
validateSimpleCodeReference(dataSourceErrorPrefix, dataSource.getStaticDataSupplier(), Supplier.class); validateSimpleCodeReference(dataSourceErrorPrefix, dataSource.getStaticDataSupplier(), Supplier.class);
} }
else
if(dataSource.getCustomRecordSource() != null)
{ {
hasASource = true; errors.add(dataSourceErrorPrefix + "does not have a sourceTable or a staticDataSupplier (exactly 1 is required).");
validateSimpleCodeReference(dataSourceErrorPrefix, dataSource.getCustomRecordSource(), ReportCustomRecordSourceInterface.class);
} }
assertCondition(hasASource, dataSourceErrorPrefix + "does not have a sourceTable, customRecordSource, or a staticDataSupplier.");
} }
} }
@ -1994,93 +1937,83 @@ public class QInstanceValidator
qInstance.getPossibleValueSources().forEach((pvsName, possibleValueSource) -> qInstance.getPossibleValueSources().forEach((pvsName, possibleValueSource) ->
{ {
assertCondition(Objects.equals(pvsName, possibleValueSource.getName()), "Inconsistent naming for possibleValueSource: " + pvsName + "/" + possibleValueSource.getName() + "."); assertCondition(Objects.equals(pvsName, possibleValueSource.getName()), "Inconsistent naming for possibleValueSource: " + pvsName + "/" + possibleValueSource.getName() + ".");
validatePossibleValueSource(qInstance, pvsName, possibleValueSource); if(assertCondition(possibleValueSource.getType() != null, "Missing type for possibleValueSource: " + pvsName))
{
////////////////////////////////////////////////////////////////////////////////////////////////
// assert about fields that should and should not be set, based on possible value source type //
// do additional type-specific validations as well //
////////////////////////////////////////////////////////////////////////////////////////////////
switch(possibleValueSource.getType())
{
case ENUM ->
{
assertCondition(!StringUtils.hasContent(possibleValueSource.getTableName()), "enum-type possibleValueSource " + pvsName + " should not have a tableName.");
assertCondition(!CollectionUtils.nullSafeHasContents(possibleValueSource.getSearchFields()), "enum-type possibleValueSource " + pvsName + " should not have searchFields.");
assertCondition(!CollectionUtils.nullSafeHasContents(possibleValueSource.getOrderByFields()), "enum-type possibleValueSource " + pvsName + " should not have orderByFields.");
assertCondition(possibleValueSource.getCustomCodeReference() == null, "enum-type possibleValueSource " + pvsName + " should not have a customCodeReference.");
assertCondition(CollectionUtils.nullSafeHasContents(possibleValueSource.getEnumValues()), "enum-type possibleValueSource " + pvsName + " is missing enum values");
}
case TABLE ->
{
assertCondition(CollectionUtils.nullSafeIsEmpty(possibleValueSource.getEnumValues()), "table-type possibleValueSource " + pvsName + " should not have enum values.");
assertCondition(possibleValueSource.getCustomCodeReference() == null, "table-type possibleValueSource " + pvsName + " should not have a customCodeReference.");
QTableMetaData tableMetaData = null;
if(assertCondition(StringUtils.hasContent(possibleValueSource.getTableName()), "table-type possibleValueSource " + pvsName + " is missing a tableName."))
{
tableMetaData = qInstance.getTable(possibleValueSource.getTableName());
assertCondition(tableMetaData != null, "Unrecognized table " + possibleValueSource.getTableName() + " for possibleValueSource " + pvsName + ".");
}
if(assertCondition(CollectionUtils.nullSafeHasContents(possibleValueSource.getSearchFields()), "table-type possibleValueSource " + pvsName + " is missing searchFields."))
{
if(tableMetaData != null)
{
QTableMetaData finalTableMetaData = tableMetaData;
for(String searchField : possibleValueSource.getSearchFields())
{
assertNoException(() -> finalTableMetaData.getField(searchField), "possibleValueSource " + pvsName + " has an unrecognized searchField: " + searchField);
}
}
}
if(assertCondition(CollectionUtils.nullSafeHasContents(possibleValueSource.getOrderByFields()), "table-type possibleValueSource " + pvsName + " is missing orderByFields."))
{
if(tableMetaData != null)
{
QTableMetaData finalTableMetaData = tableMetaData;
for(QFilterOrderBy orderByField : possibleValueSource.getOrderByFields())
{
assertNoException(() -> finalTableMetaData.getField(orderByField.getFieldName()), "possibleValueSource " + pvsName + " has an unrecognized orderByField: " + orderByField.getFieldName());
}
}
}
}
case CUSTOM ->
{
assertCondition(CollectionUtils.nullSafeIsEmpty(possibleValueSource.getEnumValues()), "custom-type possibleValueSource " + pvsName + " should not have enum values.");
assertCondition(!StringUtils.hasContent(possibleValueSource.getTableName()), "custom-type possibleValueSource " + pvsName + " should not have a tableName.");
assertCondition(!CollectionUtils.nullSafeHasContents(possibleValueSource.getSearchFields()), "custom-type possibleValueSource " + pvsName + " should not have searchFields.");
assertCondition(!CollectionUtils.nullSafeHasContents(possibleValueSource.getOrderByFields()), "custom-type possibleValueSource " + pvsName + " should not have orderByFields.");
if(assertCondition(possibleValueSource.getCustomCodeReference() != null, "custom-type possibleValueSource " + pvsName + " is missing a customCodeReference."))
{
validateSimpleCodeReference("PossibleValueSource " + pvsName + " custom code reference: ", possibleValueSource.getCustomCodeReference(), QCustomPossibleValueProvider.class);
}
}
default -> errors.add("Unexpected possibleValueSource type: " + possibleValueSource.getType());
}
runPlugins(QPossibleValueSource.class, possibleValueSource, qInstance);
}
}); });
} }
} }
/***************************************************************************
**
***************************************************************************/
private void validatePossibleValueSource(QInstance qInstance, String name, QPossibleValueSource possibleValueSource)
{
if(assertCondition(possibleValueSource.getType() != null, "Missing type for possibleValueSource: " + name))
{
////////////////////////////////////////////////////////////////////////////////////////////////
// assert about fields that should and should not be set, based on possible value source type //
// do additional type-specific validations as well //
////////////////////////////////////////////////////////////////////////////////////////////////
switch(possibleValueSource.getType())
{
case ENUM ->
{
assertCondition(!StringUtils.hasContent(possibleValueSource.getTableName()), "enum-type possibleValueSource " + name + " should not have a tableName.");
assertCondition(!CollectionUtils.nullSafeHasContents(possibleValueSource.getSearchFields()), "enum-type possibleValueSource " + name + " should not have searchFields.");
assertCondition(!CollectionUtils.nullSafeHasContents(possibleValueSource.getOrderByFields()), "enum-type possibleValueSource " + name + " should not have orderByFields.");
assertCondition(possibleValueSource.getCustomCodeReference() == null, "enum-type possibleValueSource " + name + " should not have a customCodeReference.");
assertCondition(CollectionUtils.nullSafeHasContents(possibleValueSource.getEnumValues()), "enum-type possibleValueSource " + name + " is missing enum values");
}
case TABLE ->
{
assertCondition(CollectionUtils.nullSafeIsEmpty(possibleValueSource.getEnumValues()), "table-type possibleValueSource " + name + " should not have enum values.");
assertCondition(possibleValueSource.getCustomCodeReference() == null, "table-type possibleValueSource " + name + " should not have a customCodeReference.");
QTableMetaData tableMetaData = null;
if(assertCondition(StringUtils.hasContent(possibleValueSource.getTableName()), "table-type possibleValueSource " + name + " is missing a tableName."))
{
tableMetaData = qInstance.getTable(possibleValueSource.getTableName());
assertCondition(tableMetaData != null, "Unrecognized table " + possibleValueSource.getTableName() + " for possibleValueSource " + name + ".");
}
if(assertCondition(CollectionUtils.nullSafeHasContents(possibleValueSource.getSearchFields()), "table-type possibleValueSource " + name + " is missing searchFields."))
{
if(tableMetaData != null)
{
QTableMetaData finalTableMetaData = tableMetaData;
for(String searchField : possibleValueSource.getSearchFields())
{
assertNoException(() -> finalTableMetaData.getField(searchField), "possibleValueSource " + name + " has an unrecognized searchField: " + searchField);
}
}
}
if(assertCondition(CollectionUtils.nullSafeHasContents(possibleValueSource.getOrderByFields()), "table-type possibleValueSource " + name + " is missing orderByFields."))
{
if(tableMetaData != null)
{
QTableMetaData finalTableMetaData = tableMetaData;
for(QFilterOrderBy orderByField : possibleValueSource.getOrderByFields())
{
assertNoException(() -> finalTableMetaData.getField(orderByField.getFieldName()), "possibleValueSource " + name + " has an unrecognized orderByField: " + orderByField.getFieldName());
}
}
}
}
case CUSTOM ->
{
assertCondition(CollectionUtils.nullSafeIsEmpty(possibleValueSource.getEnumValues()), "custom-type possibleValueSource " + name + " should not have enum values.");
assertCondition(!StringUtils.hasContent(possibleValueSource.getTableName()), "custom-type possibleValueSource " + name + " should not have a tableName.");
assertCondition(!CollectionUtils.nullSafeHasContents(possibleValueSource.getSearchFields()), "custom-type possibleValueSource " + name + " should not have searchFields.");
assertCondition(!CollectionUtils.nullSafeHasContents(possibleValueSource.getOrderByFields()), "custom-type possibleValueSource " + name + " should not have orderByFields.");
if(assertCondition(possibleValueSource.getCustomCodeReference() != null, "custom-type possibleValueSource " + name + " is missing a customCodeReference."))
{
validateSimpleCodeReference("PossibleValueSource " + name + " custom code reference: ", possibleValueSource.getCustomCodeReference(), QCustomPossibleValueProvider.class);
}
}
default -> errors.add("Unexpected possibleValueSource type: " + possibleValueSource.getType());
}
runPlugins(QPossibleValueSource.class, possibleValueSource, qInstance);
}
}
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/

View File

@ -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-2023. 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,10 +19,12 @@
* 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.core.model.metadata; package com.kingsrook.qqq.backend.core.model;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerOutput;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
/******************************************************************************* /*******************************************************************************

View File

@ -31,16 +31,6 @@ import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
*******************************************************************************/ *******************************************************************************/
public class MetaDataInput extends AbstractActionInput public class MetaDataInput extends AbstractActionInput
{ {
private String frontendName;
private String frontendVersion;
private String middlewareName;
private String middlewareVersion;
private String applicationName;
private String applicationVersion;
/******************************************************************************* /*******************************************************************************
** **
@ -49,190 +39,4 @@ public class MetaDataInput extends AbstractActionInput
{ {
} }
/*******************************************************************************
** Getter for frontendName
*******************************************************************************/
public String getFrontendName()
{
return (this.frontendName);
}
/*******************************************************************************
** Setter for frontendName
*******************************************************************************/
public void setFrontendName(String frontendName)
{
this.frontendName = frontendName;
}
/*******************************************************************************
** Fluent setter for frontendName
*******************************************************************************/
public MetaDataInput withFrontendName(String frontendName)
{
this.frontendName = frontendName;
return (this);
}
/*******************************************************************************
** Getter for frontendVersion
*******************************************************************************/
public String getFrontendVersion()
{
return (this.frontendVersion);
}
/*******************************************************************************
** Setter for frontendVersion
*******************************************************************************/
public void setFrontendVersion(String frontendVersion)
{
this.frontendVersion = frontendVersion;
}
/*******************************************************************************
** Fluent setter for frontendVersion
*******************************************************************************/
public MetaDataInput withFrontendVersion(String frontendVersion)
{
this.frontendVersion = frontendVersion;
return (this);
}
/*******************************************************************************
** Getter for middlewareName
*******************************************************************************/
public String getMiddlewareName()
{
return (this.middlewareName);
}
/*******************************************************************************
** Setter for middlewareName
*******************************************************************************/
public void setMiddlewareName(String middlewareName)
{
this.middlewareName = middlewareName;
}
/*******************************************************************************
** Fluent setter for middlewareName
*******************************************************************************/
public MetaDataInput withMiddlewareName(String middlewareName)
{
this.middlewareName = middlewareName;
return (this);
}
/*******************************************************************************
** Getter for middlewareVersion
*******************************************************************************/
public String getMiddlewareVersion()
{
return (this.middlewareVersion);
}
/*******************************************************************************
** Setter for middlewareVersion
*******************************************************************************/
public void setMiddlewareVersion(String middlewareVersion)
{
this.middlewareVersion = middlewareVersion;
}
/*******************************************************************************
** Fluent setter for middlewareVersion
*******************************************************************************/
public MetaDataInput withMiddlewareVersion(String middlewareVersion)
{
this.middlewareVersion = middlewareVersion;
return (this);
}
/*******************************************************************************
** Getter for applicationName
*******************************************************************************/
public String getApplicationName()
{
return (this.applicationName);
}
/*******************************************************************************
** Setter for applicationName
*******************************************************************************/
public void setApplicationName(String applicationName)
{
this.applicationName = applicationName;
}
/*******************************************************************************
** Fluent setter for applicationName
*******************************************************************************/
public MetaDataInput withApplicationName(String applicationName)
{
this.applicationName = applicationName;
return (this);
}
/*******************************************************************************
** Getter for applicationVersion
*******************************************************************************/
public String getApplicationVersion()
{
return (this.applicationVersion);
}
/*******************************************************************************
** Setter for applicationVersion
*******************************************************************************/
public void setApplicationVersion(String applicationVersion)
{
this.applicationVersion = applicationVersion;
}
/*******************************************************************************
** Fluent setter for applicationVersion
*******************************************************************************/
public MetaDataInput withApplicationVersion(String applicationVersion)
{
this.applicationVersion = applicationVersion;
return (this);
}
} }

View File

@ -1,139 +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.model.actions.processes;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.kingsrook.qqq.backend.core.logging.QLogger;
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.utils.StringUtils;
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
/*******************************************************************************
** Object that stores adjustments that a process wants to make, at run-time,
** to its meta-data.
**
** e.g., changing the steps; updating fields (e.g., changing an inline PVS,
** or an isRequired attribute)
*******************************************************************************/
public class ProcessMetaDataAdjustment
{
private static final QLogger LOG = QLogger.getLogger(ProcessMetaDataAdjustment.class);
private List<QFrontendStepMetaData> updatedFrontendStepList = null;
private Map<String, QFieldMetaData> updatedFields = null;
/*******************************************************************************
**
*******************************************************************************/
public ProcessMetaDataAdjustment withUpdatedField(QFieldMetaData field)
{
if(updatedFields == null)
{
updatedFields = new LinkedHashMap<>();
}
if(!StringUtils.hasContent(field.getName()))
{
LOG.warn("Missing name on field in withUpdatedField - no update will happen.");
}
else
{
if(updatedFields.containsKey(field.getName()))
{
LOG.info("UpdatedFields map already contained a field with this name - overwriting it.", logPair("fieldName", field.getName()));
}
updatedFields.put(field.getName(), field);
}
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 ProcessMetaDataAdjustment withUpdatedFrontendStepList(List<QFrontendStepMetaData> updatedFrontendStepList)
{
this.updatedFrontendStepList = updatedFrontendStepList;
return (this);
}
/*******************************************************************************
** Getter for updatedFields
*******************************************************************************/
public Map<String, QFieldMetaData> getUpdatedFields()
{
return (this.updatedFields);
}
/*******************************************************************************
** Setter for updatedFields
*******************************************************************************/
public void setUpdatedFields(Map<String, QFieldMetaData> updatedFields)
{
this.updatedFields = updatedFields;
}
/*******************************************************************************
** Fluent setter for updatedFields
*******************************************************************************/
public ProcessMetaDataAdjustment withUpdatedFields(Map<String, QFieldMetaData> updatedFields)
{
this.updatedFields = updatedFields;
return (this);
}
}

View File

@ -29,6 +29,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
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;
/******************************************************************************* /*******************************************************************************
@ -40,10 +41,11 @@ public class ProcessState implements Serializable
private Map<String, Serializable> values = new HashMap<>(); private Map<String, Serializable> values = new HashMap<>();
private List<String> stepList = new ArrayList<>(); private List<String> stepList = new ArrayList<>();
private Optional<String> nextStepName = Optional.empty(); private Optional<String> nextStepName = Optional.empty();
private Optional<String> backStepName = Optional.empty();
private boolean isStepBack = false;
private ProcessMetaDataAdjustment processMetaDataAdjustment = null; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// maybe, remove this altogether - just let the frontend compute & send if needed... but how does it know last version...? //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private List<QFrontendStepMetaData> updatedFrontendStepList = null;
@ -124,39 +126,6 @@ public class ProcessState implements Serializable
/*******************************************************************************
** Getter for backStepName
**
*******************************************************************************/
public Optional<String> getBackStepName()
{
return backStepName;
}
/*******************************************************************************
** Setter for backStepName
**
*******************************************************************************/
public void setBackStepName(String backStepName)
{
this.backStepName = Optional.of(backStepName);
}
/*******************************************************************************
** clear out the value of backStepName (set the Optional to empty)
**
*******************************************************************************/
public void clearBackStepName()
{
this.backStepName = Optional.empty();
}
/******************************************************************************* /*******************************************************************************
** Getter for stepList ** Getter for stepList
** **
@ -179,67 +148,33 @@ public class ProcessState implements Serializable
/******************************************************************************* /*******************************************************************************
** Getter for processMetaDataAdjustment ** Getter for updatedFrontendStepList
*******************************************************************************/ *******************************************************************************/
public ProcessMetaDataAdjustment getProcessMetaDataAdjustment() public List<QFrontendStepMetaData> getUpdatedFrontendStepList()
{ {
return (this.processMetaDataAdjustment); return (this.updatedFrontendStepList);
} }
/******************************************************************************* /*******************************************************************************
** Setter for processMetaDataAdjustment ** Setter for updatedFrontendStepList
*******************************************************************************/ *******************************************************************************/
public void setProcessMetaDataAdjustment(ProcessMetaDataAdjustment processMetaDataAdjustment) public void setUpdatedFrontendStepList(List<QFrontendStepMetaData> updatedFrontendStepList)
{ {
this.processMetaDataAdjustment = processMetaDataAdjustment; this.updatedFrontendStepList = updatedFrontendStepList;
} }
/******************************************************************************* /*******************************************************************************
** Fluent setter for processMetaDataAdjustment ** Fluent setter for updatedFrontendStepList
*******************************************************************************/ *******************************************************************************/
public ProcessState withProcessMetaDataAdjustment(ProcessMetaDataAdjustment processMetaDataAdjustment) public ProcessState withUpdatedFrontendStepList(List<QFrontendStepMetaData> updatedFrontendStepList)
{ {
this.processMetaDataAdjustment = processMetaDataAdjustment; this.updatedFrontendStepList = updatedFrontendStepList;
return (this); return (this);
} }
/*******************************************************************************
** Getter for isStepBack
*******************************************************************************/
public boolean getIsStepBack()
{
return (this.isStepBack);
}
/*******************************************************************************
** Setter for isStepBack
*******************************************************************************/
public void setIsStepBack(boolean isStepBack)
{
this.isStepBack = isStepBack;
}
/*******************************************************************************
** Fluent setter for isStepBack
*******************************************************************************/
public ProcessState withIsStepBack(boolean isStepBack)
{
this.isStepBack = isStepBack;
return (this);
}
} }

View File

@ -53,7 +53,6 @@ public class ProcessSummaryLine implements ProcessSummaryLineInterface
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
private ArrayList<Serializable> primaryKeys; private ArrayList<Serializable> primaryKeys;
private ArrayList<String> bulletsOfText;
/******************************************************************************* /*******************************************************************************
@ -498,35 +497,4 @@ public class ProcessSummaryLine implements ProcessSummaryLineInterface
return (this); return (this);
} }
/*******************************************************************************
** Getter for bulletsOfText
*******************************************************************************/
public ArrayList<String> getBulletsOfText()
{
return (this.bulletsOfText);
}
/*******************************************************************************
** Setter for bulletsOfText
*******************************************************************************/
public void setBulletsOfText(ArrayList<String> bulletsOfText)
{
this.bulletsOfText = bulletsOfText;
}
/*******************************************************************************
** Fluent setter for bulletsOfText
*******************************************************************************/
public ProcessSummaryLine withBulletsOfText(ArrayList<String> bulletsOfText)
{
this.bulletsOfText = bulletsOfText;
return (this);
}
} }

View File

@ -419,17 +419,6 @@ public class RunBackendStepInput extends AbstractActionInput
/*******************************************************************************
** Accessor for processState's isStepBack attribute
**
*******************************************************************************/
public boolean getIsStepBack()
{
return processState.getIsStepBack();
}
/******************************************************************************* /*******************************************************************************
** Accessor for processState - protected, because we generally want to access ** Accessor for processState - protected, because we generally want to access
** its members through wrapper methods, we think ** its members through wrapper methods, we think

View File

@ -374,13 +374,7 @@ public class RunBackendStepOutput extends AbstractActionOutput implements Serial
.map(step -> (QFrontendStepMetaData) step) .map(step -> (QFrontendStepMetaData) step)
.toList()); .toList());
ProcessMetaDataAdjustment processMetaDataAdjustment = getProcessMetaDataAdjustment(); setUpdatedFrontendStepList(updatedFrontendStepList);
if(processMetaDataAdjustment == null)
{
processMetaDataAdjustment = new ProcessMetaDataAdjustment();
}
processMetaDataAdjustment.setUpdatedFrontendStepList(updatedFrontendStepList);
setProcessMetaDataAdjustment(processMetaDataAdjustment);
} }
@ -417,21 +411,21 @@ public class RunBackendStepOutput extends AbstractActionOutput implements Serial
/******************************************************************************* /*******************************************************************************
** Getter for ProcessMetaDataAdjustment (pass-through to processState) ** Getter for updatedFrontendStepList
*******************************************************************************/ *******************************************************************************/
public ProcessMetaDataAdjustment getProcessMetaDataAdjustment() public List<QFrontendStepMetaData> getUpdatedFrontendStepList()
{ {
return (this.processState.getProcessMetaDataAdjustment()); return (this.processState.getUpdatedFrontendStepList());
} }
/******************************************************************************* /*******************************************************************************
** Setter for updatedFrontendStepList (pass-through to processState) ** Setter for updatedFrontendStepList
*******************************************************************************/ *******************************************************************************/
public void setProcessMetaDataAdjustment(ProcessMetaDataAdjustment processMetaDataAdjustment) public void setUpdatedFrontendStepList(List<QFrontendStepMetaData> updatedFrontendStepList)
{ {
this.processState.setProcessMetaDataAdjustment(processMetaDataAdjustment); this.processState.setUpdatedFrontendStepList(updatedFrontendStepList);
} }
} }

View File

@ -49,7 +49,6 @@ public class RunProcessInput extends AbstractActionInput
private ProcessState processState; private ProcessState processState;
private FrontendStepBehavior frontendStepBehavior = FrontendStepBehavior.BREAK; private FrontendStepBehavior frontendStepBehavior = FrontendStepBehavior.BREAK;
private String startAfterStep; private String startAfterStep;
private String startAtStep;
private String processUUID; private String processUUID;
private AsyncJobCallback asyncJobCallback; private AsyncJobCallback asyncJobCallback;
@ -452,35 +451,4 @@ public class RunProcessInput extends AbstractActionInput
{ {
return asyncJobCallback; return asyncJobCallback;
} }
/*******************************************************************************
** Getter for startAtStep
*******************************************************************************/
public String getStartAtStep()
{
return (this.startAtStep);
}
/*******************************************************************************
** Setter for startAtStep
*******************************************************************************/
public void setStartAtStep(String startAtStep)
{
this.startAtStep = startAtStep;
}
/*******************************************************************************
** Fluent setter for startAtStep
*******************************************************************************/
public RunProcessInput withStartAtStep(String startAtStep)
{
this.startAtStep = startAtStep;
return (this);
}
} }

View File

@ -33,7 +33,6 @@ 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.model.metadata.processes.QFrontendStepMetaData;
import com.kingsrook.qqq.backend.core.utils.ObjectUtils;
import com.kingsrook.qqq.backend.core.utils.ValueUtils; import com.kingsrook.qqq.backend.core.utils.ValueUtils;
@ -337,12 +336,7 @@ public class RunProcessOutput extends AbstractActionOutput implements Serializab
*******************************************************************************/ *******************************************************************************/
public void setUpdatedFrontendStepList(List<QFrontendStepMetaData> updatedFrontendStepList) public void setUpdatedFrontendStepList(List<QFrontendStepMetaData> updatedFrontendStepList)
{ {
if(this.processState.getProcessMetaDataAdjustment() == null) this.processState.setUpdatedFrontendStepList(updatedFrontendStepList);
{
this.processState.setProcessMetaDataAdjustment(new ProcessMetaDataAdjustment());
}
this.processState.getProcessMetaDataAdjustment().setUpdatedFrontendStepList(updatedFrontendStepList);
} }
@ -352,27 +346,7 @@ public class RunProcessOutput extends AbstractActionOutput implements Serializab
*******************************************************************************/ *******************************************************************************/
public List<QFrontendStepMetaData> getUpdatedFrontendStepList() public List<QFrontendStepMetaData> getUpdatedFrontendStepList()
{ {
return ObjectUtils.tryElse(() -> this.processState.getProcessMetaDataAdjustment().getUpdatedFrontendStepList(), null); return this.processState.getUpdatedFrontendStepList();
}
/*******************************************************************************
** Getter for processMetaDataAdjustment
*******************************************************************************/
public ProcessMetaDataAdjustment getProcessMetaDataAdjustment()
{
return (this.processState.getProcessMetaDataAdjustment());
}
/*******************************************************************************
** Setter for processMetaDataAdjustment
*******************************************************************************/
public void setProcessMetaDataAdjustment(ProcessMetaDataAdjustment processMetaDataAdjustment)
{
this.processState.setProcessMetaDataAdjustment(processMetaDataAdjustment);
} }
} }

View File

@ -1,66 +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.model.actions.tables.query;
/***************************************************************************
** Possible behaviors for doing interpretValues on a filter, and a criteria
** has a variable value (either as a string-that-looks-like-a-variable,
** as in ${input.foreignId} for a PVS filter, or a FilterVariableExpression),
** and a value for that variable isn't available.
**
** Used in conjunction with FilterUseCase and its implementations, e.g.,
** PossibleValueSearchFilterUseCase.
***************************************************************************/
public enum CriteriaMissingInputValueBehavior
{
//////////////////////////////////////////////////////////////////////
// this was the original behavior, before we added this enum. but, //
// it doesn't ever seem entirely valid, and isn't currently used. //
//////////////////////////////////////////////////////////////////////
INTERPRET_AS_NULL_VALUE,
//////////////////////////////////////////////////////////////////////////
// make the criteria behave as though it's not in the filter at all. //
// effectively by changing its operator to TRUE, so it always matches. //
// original intended use is for possible-values on query screens, //
// where a foreign-id isn't present, so we want to show all PV options. //
//////////////////////////////////////////////////////////////////////////
REMOVE_FROM_FILTER,
//////////////////////////////////////////////////////////////////////////////////////
// make the criteria such that it makes no rows ever match. //
// e.g., changes it to a FALSE. I suppose, within an OR, that might //
// not be powerful enough... but, it solves the immediate use-case in //
// front of us, which is forms, where a PV field should show no values //
// until a foreign key field has a value. //
// Note that this use-case used to have the same end-effect by such //
// variables being interpreted as nulls - but this approach feels more intentional. //
//////////////////////////////////////////////////////////////////////////////////////
MAKE_NO_MATCHES,
///////////////////////////////////////////////////////////////////////////////////////////
// throw an exception if a value isn't available. This is the overall default, //
// and originally was what we did for FilterVariableExpressions, e.g., for saved reports //
///////////////////////////////////////////////////////////////////////////////////////////
THROW_EXCEPTION
}

View File

@ -1,58 +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.model.actions.tables.query;
/*******************************************************************************
** Interface where we can associate behaviors with various use cases for
** QQueryFilters - the original being, how to handle (in the interpretValues
** method) how to handle missing input values.
**
** Includes a default implementation, with a default behavior - which is to
** throw an exception upon missing criteria variable values.
*******************************************************************************/
public interface FilterUseCase
{
FilterUseCase DEFAULT = new DefaultFilterUseCase();
/***************************************************************************
**
***************************************************************************/
CriteriaMissingInputValueBehavior getDefaultCriteriaMissingInputValueBehavior();
/***************************************************************************
**
***************************************************************************/
class DefaultFilterUseCase implements FilterUseCase
{
/***************************************************************************
**
***************************************************************************/
@Override
public CriteriaMissingInputValueBehavior getDefaultCriteriaMissingInputValueBehavior()
{
return CriteriaMissingInputValueBehavior.THROW_EXCEPTION;
}
}
}

View File

@ -82,7 +82,7 @@ public class JoinsContext
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// we will get a TON of more output if this gets turned up, so be cautious // // we will get a TON of more output if this gets turned up, so be cautious //
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
private Level logLevel = Level.OFF; private Level logLevel = Level.OFF;
private Level logLevelForFilter = Level.OFF; private Level logLevelForFilter = Level.OFF;
@ -404,12 +404,6 @@ public class JoinsContext
chainIsInner = false; chainIsInner = false;
} }
if(hasAllAccessKey(recordSecurityLock))
{
queryJoin.withType(QueryJoin.Type.LEFT);
chainIsInner = false;
}
addQueryJoin(queryJoin, "forRecordSecurityLock (non-flipped)", "- "); addQueryJoin(queryJoin, "forRecordSecurityLock (non-flipped)", "- ");
addedQueryJoins.add(queryJoin); addedQueryJoins.add(queryJoin);
tmpTable = instance.getTable(join.getRightTable()); tmpTable = instance.getTable(join.getRightTable());
@ -429,12 +423,6 @@ public class JoinsContext
chainIsInner = false; chainIsInner = false;
} }
if(hasAllAccessKey(recordSecurityLock))
{
queryJoin.withType(QueryJoin.Type.LEFT);
chainIsInner = false;
}
addQueryJoin(queryJoin, "forRecordSecurityLock (flipped)", "- "); addQueryJoin(queryJoin, "forRecordSecurityLock (flipped)", "- ");
addedQueryJoins.add(queryJoin); addedQueryJoins.add(queryJoin);
tmpTable = instance.getTable(join.getLeftTable()); tmpTable = instance.getTable(join.getLeftTable());
@ -468,53 +456,44 @@ public class JoinsContext
/***************************************************************************
**
***************************************************************************/
private boolean hasAllAccessKey(RecordSecurityLock recordSecurityLock)
{
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// check if the key type has an all-access key, and if so, if it's set to true for the current user/session //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
QSecurityKeyType securityKeyType = instance.getSecurityKeyType(recordSecurityLock.getSecurityKeyType());
if(StringUtils.hasContent(securityKeyType.getAllAccessKeyName()))
{
QSession session = QContext.getQSession();
if(session.hasSecurityKeyValue(securityKeyType.getAllAccessKeyName(), true, QFieldType.BOOLEAN))
{
return (true);
}
}
return (false);
}
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
private void addSubFilterForRecordSecurityLock(RecordSecurityLock recordSecurityLock, QTableMetaData table, String tableNameOrAlias, boolean isOuter, QueryJoin sourceQueryJoin) private void addSubFilterForRecordSecurityLock(RecordSecurityLock recordSecurityLock, QTableMetaData table, String tableNameOrAlias, boolean isOuter, QueryJoin sourceQueryJoin)
{ {
boolean haveAllAccessKey = hasAllAccessKey(recordSecurityLock); QSession session = QContext.getQSession();
if(haveAllAccessKey)
{
if(sourceQueryJoin != null)
{
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// in case the queryJoin object is re-used between queries, and its security criteria need to be different (!!), reset it //
// this can be exposed in tests - maybe not entirely expected in real-world, but seems safe enough //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
sourceQueryJoin.withSecurityCriteria(new ArrayList<>());
}
//////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////
// if we're in an AND filter, then we don't need a criteria for this lock, so return. // // check if the key type has an all-access key, and if so, if it's set to true for the current user/session //
//////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////
boolean inAnAndFilter = securityFilterCursor.getBooleanOperator() == QQueryFilter.BooleanOperator.AND; QSecurityKeyType securityKeyType = instance.getSecurityKeyType(recordSecurityLock.getSecurityKeyType());
if(inAnAndFilter) boolean haveAllAccessKey = false;
if(StringUtils.hasContent(securityKeyType.getAllAccessKeyName()))
{
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// if we have all-access on this key, then we don't need a criterion for it (as long as we're in an AND filter) //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if(session.hasSecurityKeyValue(securityKeyType.getAllAccessKeyName(), true, QFieldType.BOOLEAN))
{ {
return; haveAllAccessKey = true;
if(sourceQueryJoin != null)
{
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// in case the queryJoin object is re-used between queries, and its security criteria need to be different (!!), reset it //
// this can be exposed in tests - maybe not entirely expected in real-world, but seems safe enough //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
sourceQueryJoin.withSecurityCriteria(new ArrayList<>());
}
////////////////////////////////////////////////////////////////////////////////////////
// if we're in an AND filter, then we don't need a criteria for this lock, so return. //
////////////////////////////////////////////////////////////////////////////////////////
boolean inAnAndFilter = securityFilterCursor.getBooleanOperator() == QQueryFilter.BooleanOperator.AND;
if(inAnAndFilter)
{
return;
}
} }
} }
@ -566,7 +545,7 @@ public class JoinsContext
} }
else else
{ {
List<Serializable> securityKeyValues = QContext.getQSession().getSecurityKeyValues(recordSecurityLock.getSecurityKeyType(), type); List<Serializable> securityKeyValues = session.getSecurityKeyValues(recordSecurityLock.getSecurityKeyType(), type);
if(CollectionUtils.nullSafeIsEmpty(securityKeyValues)) if(CollectionUtils.nullSafeIsEmpty(securityKeyValues))
{ {
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -528,27 +528,8 @@ public class QQueryFilter implements Serializable, Cloneable
** Note - it may be very important that you call this method on a clone of a ** Note - it may be very important that you call this method on a clone of a
** QQueryFilter - e.g., if it's one that defined in metaData, and that we don't ** QQueryFilter - e.g., if it's one that defined in metaData, and that we don't
** want to be (permanently) changed!! ** want to be (permanently) changed!!
**
** This overload does not take in a FilterUseCase - it uses FilterUseCase.DEFAULT
******************************************************************************/
public void interpretValues(Map<String, Serializable> inputValues) throws QException
{
interpretValues(inputValues, FilterUseCase.DEFAULT);
}
/*******************************************************************************
** Replace any criteria values that look like ${input.XXX} with the value of XXX
** from the supplied inputValues map - where the handling of missing values
** is specified in the inputted FilterUseCase parameter
**
** Note - it may be very important that you call this method on a clone of a
** QQueryFilter - e.g., if it's one that defined in metaData, and that we don't
** want to be (permanently) changed!!
**
*******************************************************************************/ *******************************************************************************/
public void interpretValues(Map<String, Serializable> inputValues, FilterUseCase useCase) throws QException public void interpretValues(Map<String, Serializable> inputValues) throws QException
{ {
List<Exception> caughtExceptions = new ArrayList<>(); List<Exception> caughtExceptions = new ArrayList<>();
@ -564,9 +545,6 @@ public class QQueryFilter implements Serializable, Cloneable
{ {
try try
{ {
Serializable interpretedValue = value;
Exception caughtException = null;
if(value instanceof AbstractFilterExpression<?>) if(value instanceof AbstractFilterExpression<?>)
{ {
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
@ -575,54 +553,17 @@ public class QQueryFilter implements Serializable, Cloneable
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
if(value instanceof FilterVariableExpression filterVariableExpression) if(value instanceof FilterVariableExpression filterVariableExpression)
{ {
try newValues.add(filterVariableExpression.evaluateInputValues(inputValues));
{
interpretedValue = filterVariableExpression.evaluateInputValues(inputValues);
}
catch(Exception e)
{
caughtException = e;
interpretedValue = InputNotFound.instance;
}
} }
} else
else
{
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// for non-expressions, cast the value to a string, and see if it can be resolved a variable. //
// there are 3 possible cases here: //
// 1: it doesn't look like a variable, so it just comes back as a string version of whatever went in. //
// 2: it was resolved from a variable to a value, e.g., ${input.someVar} => someValue //
// 3: it looked like a variable, but no value for that variable was present in the interpreter's value //
// map - so we'll get back the InputNotFound.instance. //
/////////////////////////////////////////////////////////////////////////////////////////////////////////
String valueAsString = ValueUtils.getValueAsString(value);
interpretedValue = variableInterpreter.interpretForObject(valueAsString, InputNotFound.instance);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// if interpreting a value returned the not-found value, or an empty string, //
// then decide how to handle the missing value, based on the use-case input //
// Note: questionable, using "" here, but that's what reality is passing a lot for cases we want to treat as missing... //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if(interpretedValue == InputNotFound.instance || "".equals(interpretedValue))
{
CriteriaMissingInputValueBehavior missingInputValueBehavior = getMissingInputValueBehavior(useCase);
switch(missingInputValueBehavior)
{ {
case REMOVE_FROM_FILTER -> criterion.setOperator(QCriteriaOperator.TRUE); newValues.add(value);
case MAKE_NO_MATCHES -> criterion.setOperator(QCriteriaOperator.FALSE);
case INTERPRET_AS_NULL_VALUE -> newValues.add(null);
/////////////////////////////////////////////////
// handle case in the default: THROW_EXCEPTION //
/////////////////////////////////////////////////
default -> throw (Objects.requireNonNullElseGet(caughtException, () -> new QUserFacingException("Missing value for criteria on field: " + criterion.getFieldName())));
} }
} }
else else
{ {
String valueAsString = ValueUtils.getValueAsString(value);
Serializable interpretedValue = variableInterpreter.interpretForObject(valueAsString);
newValues.add(interpretedValue); newValues.add(interpretedValue);
} }
} }
@ -645,44 +586,6 @@ public class QQueryFilter implements Serializable, Cloneable
/***************************************************************************
** Note: in the original build of this, it felt like we *might* want to be
** able to specify these behaviors at the individual criteria level, where
** the implementation would be to add to QFilterCriteria:
** - Map<FilterUseCase, CriteriaMissingInputValueBehavior> missingInputValueBehaviors;
** - CriteriaMissingInputValueBehavior getMissingInputValueBehaviorForUseCase(FilterUseCase useCase) {}
*
** (and maybe do that in a sub-class of QFilterCriteria, so it isn't always
** there? idk...) and then here we'd call:
** - CriteriaMissingInputValueBehavior missingInputValueBehavior = criterion.getMissingInputValueBehaviorForUseCase(useCase);
*
** But, we don't actually have that use-case at hand now, so - let's keep it
** just at the level we need for now.
**
***************************************************************************/
private CriteriaMissingInputValueBehavior getMissingInputValueBehavior(FilterUseCase useCase)
{
if(useCase == null)
{
useCase = FilterUseCase.DEFAULT;
}
CriteriaMissingInputValueBehavior missingInputValueBehavior = useCase.getDefaultCriteriaMissingInputValueBehavior();
if(missingInputValueBehavior == null)
{
missingInputValueBehavior = useCase.getDefaultCriteriaMissingInputValueBehavior();
}
if(missingInputValueBehavior == null)
{
missingInputValueBehavior = FilterUseCase.DEFAULT.getDefaultCriteriaMissingInputValueBehavior();
}
return (missingInputValueBehavior);
}
/******************************************************************************* /*******************************************************************************
** Getter for skip ** Getter for skip
*******************************************************************************/ *******************************************************************************/
@ -775,28 +678,4 @@ public class QQueryFilter implements Serializable, Cloneable
{ {
return Objects.hash(criteria, orderBys, booleanOperator, subFilters, skip, limit); return Objects.hash(criteria, orderBys, booleanOperator, subFilters, skip, limit);
} }
/***************************************************************************
** "Token" object to be used as the defaultIfLooksLikeVariableButNotFound
** parameter to variableInterpreter.interpretForObject, so we can be
** very clear that we got this default back (e.g., instead of a null,
** which could maybe mean something else?)
***************************************************************************/
private static final class InputNotFound implements Serializable
{
private static InputNotFound instance = new InputNotFound();
/*******************************************************************************
** private singleton constructor
*******************************************************************************/
private InputNotFound()
{
}
}
} }

View File

@ -66,14 +66,6 @@ public class QueryInput extends AbstractTableActionInput implements QueryOrGetIn
private List<QueryJoin> queryJoins = null; private List<QueryJoin> queryJoins = null;
private boolean selectDistinct = false; private boolean selectDistinct = false;
/////////////////////////////////////////////////////////////////////////////
// if this set is null, then the default (all fields) should be included //
// if it's an empty set, that should throw an error //
// or if there are any fields in it that aren't valid fields on the table, //
// or in a selected queryJoin. //
/////////////////////////////////////////////////////////////////////////////
private Set<String> fieldNamesToInclude;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// if you say you want to includeAssociations, you can limit which ones by passing them in associationNamesToInclude. // // if you say you want to includeAssociations, you can limit which ones by passing them in associationNamesToInclude. //
// if you leave it null, you get all associations defined on the table. if you pass it as empty, you get none. // // if you leave it null, you get all associations defined on the table. if you pass it as empty, you get none. //
@ -694,35 +686,4 @@ public class QueryInput extends AbstractTableActionInput implements QueryOrGetIn
return (queryHints.contains(queryHint)); return (queryHints.contains(queryHint));
} }
/*******************************************************************************
** Getter for fieldNamesToInclude
*******************************************************************************/
public Set<String> getFieldNamesToInclude()
{
return (this.fieldNamesToInclude);
}
/*******************************************************************************
** Setter for fieldNamesToInclude
*******************************************************************************/
public void setFieldNamesToInclude(Set<String> fieldNamesToInclude)
{
this.fieldNamesToInclude = fieldNamesToInclude;
}
/*******************************************************************************
** Fluent setter for fieldNamesToInclude
*******************************************************************************/
public QueryInput withFieldNamesToInclude(Set<String> fieldNamesToInclude)
{
this.fieldNamesToInclude = fieldNamesToInclude;
return (this);
}
} }

View File

@ -25,7 +25,6 @@ package com.kingsrook.qqq.backend.core.model.actions.tables.query.expressions;
import java.io.Serializable; import java.io.Serializable;
import java.util.Map; import java.util.Map;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
/******************************************************************************* /*******************************************************************************
@ -36,7 +35,7 @@ public abstract class AbstractFilterExpression<T extends Serializable> implement
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public abstract T evaluate(QFieldMetaData field) throws QException; public abstract T evaluate() throws QException;
@ -48,7 +47,7 @@ public abstract class AbstractFilterExpression<T extends Serializable> implement
*******************************************************************************/ *******************************************************************************/
public T evaluateInputValues(Map<String, Serializable> inputValues) throws QException public T evaluateInputValues(Map<String, Serializable> inputValues) throws QException
{ {
return evaluate(null); return evaluate();
} }

View File

@ -26,7 +26,6 @@ import java.io.Serializable;
import java.util.Map; import java.util.Map;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException; import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.utils.ValueUtils; import com.kingsrook.qqq.backend.core.utils.ValueUtils;
@ -46,7 +45,7 @@ public class FilterVariableExpression extends AbstractFilterExpression<Serializa
** **
*******************************************************************************/ *******************************************************************************/
@Override @Override
public Serializable evaluate(QFieldMetaData field) throws QException public Serializable evaluate() throws QException
{ {
throw (new QUserFacingException("Missing variable value.")); throw (new QUserFacingException("Missing variable value."));
} }

View File

@ -22,42 +22,23 @@
package com.kingsrook.qqq.backend.core.model.actions.tables.query.expressions; package com.kingsrook.qqq.backend.core.model.actions.tables.query.expressions;
import java.io.Serializable;
import java.time.Instant; import java.time.Instant;
import java.time.ZoneId;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public class Now extends AbstractFilterExpression<Serializable> public class Now extends AbstractFilterExpression<Instant>
{ {
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
@Override @Override
public Serializable evaluate(QFieldMetaData field) throws QException public Instant evaluate() throws QException
{ {
QFieldType type = field == null ? QFieldType.DATE_TIME : field.getType(); return (Instant.now());
if(type.equals(QFieldType.DATE_TIME))
{
return (Instant.now());
}
else if(type.equals(QFieldType.DATE))
{
ZoneId zoneId = ValueUtils.getSessionOrInstanceZoneId();
return (Instant.now().atZone(zoneId).toLocalDate());
}
else
{
throw (new QException("Unsupported field type [" + type + "]"));
}
} }
} }

View File

@ -22,24 +22,19 @@
package com.kingsrook.qqq.backend.core.model.actions.tables.query.expressions; package com.kingsrook.qqq.backend.core.model.actions.tables.query.expressions;
import java.io.Serializable;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public class NowWithOffset extends AbstractFilterExpression<Serializable> public class NowWithOffset extends AbstractFilterExpression<Instant>
{ {
private Operator operator; private Operator operator;
private int amount; private int amount;
@ -128,30 +123,7 @@ public class NowWithOffset extends AbstractFilterExpression<Serializable>
** **
*******************************************************************************/ *******************************************************************************/
@Override @Override
public Serializable evaluate(QFieldMetaData field) throws QException public Instant evaluate() throws QException
{
QFieldType type = field == null ? QFieldType.DATE_TIME : field.getType();
if(type.equals(QFieldType.DATE_TIME))
{
return (evaluateForDateTime());
}
else if(type.equals(QFieldType.DATE))
{
return (evaluateForDate());
}
else
{
throw (new QException("Unsupported field type [" + type + "]"));
}
}
/***************************************************************************
**
***************************************************************************/
private Instant evaluateForDateTime()
{ {
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// Instant doesn't let us plus/minus WEEK, MONTH, or YEAR... // // Instant doesn't let us plus/minus WEEK, MONTH, or YEAR... //
@ -175,26 +147,6 @@ public class NowWithOffset extends AbstractFilterExpression<Serializable>
/***************************************************************************
**
***************************************************************************/
private LocalDate evaluateForDate()
{
ZoneId zoneId = ValueUtils.getSessionOrInstanceZoneId();
LocalDate now = Instant.now().atZone(zoneId).toLocalDate();
if(operator.equals(Operator.PLUS))
{
return (now.plus(amount, timeUnit));
}
else
{
return (now.minus(amount, timeUnit));
}
}
/******************************************************************************* /*******************************************************************************
** Getter for operator ** Getter for operator
** **

View File

@ -22,32 +22,27 @@
package com.kingsrook.qqq.backend.core.model.actions.tables.query.expressions; package com.kingsrook.qqq.backend.core.model.actions.tables.query.expressions;
import java.io.Serializable;
import java.time.DayOfWeek; import java.time.DayOfWeek;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.temporal.ChronoField; import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.exceptions.QRuntimeException; import com.kingsrook.qqq.backend.core.exceptions.QRuntimeException;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.utils.ValueUtils; import com.kingsrook.qqq.backend.core.utils.ValueUtils;
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public class ThisOrLastPeriod extends AbstractFilterExpression<Serializable> public class ThisOrLastPeriod extends AbstractFilterExpression<Instant>
{ {
private Operator operator; private Operator operator;
private ChronoUnit timeUnit; private ChronoUnit timeUnit;
/*************************************************************************** /***************************************************************************
** **
***************************************************************************/ ***************************************************************************/
@ -93,7 +88,7 @@ public class ThisOrLastPeriod extends AbstractFilterExpression<Serializable>
** Factory ** Factory
** **
*******************************************************************************/ *******************************************************************************/
public static ThisOrLastPeriod last(ChronoUnit timeUnit) public static ThisOrLastPeriod last(int amount, ChronoUnit timeUnit)
{ {
return (new ThisOrLastPeriod(Operator.LAST, timeUnit)); return (new ThisOrLastPeriod(Operator.LAST, timeUnit));
} }
@ -104,31 +99,7 @@ public class ThisOrLastPeriod extends AbstractFilterExpression<Serializable>
** **
*******************************************************************************/ *******************************************************************************/
@Override @Override
public Serializable evaluate(QFieldMetaData field) throws QException public Instant evaluate() throws QException
{
QFieldType type = field == null ? QFieldType.DATE_TIME : field.getType();
if(type.equals(QFieldType.DATE_TIME))
{
return (evaluateForDateTime());
}
else if(type.equals(QFieldType.DATE))
{
// return (evaluateForDateTime());
return (evaluateForDate());
}
else
{
throw (new QException("Unsupported field type [" + type + "]"));
}
}
/***************************************************************************
**
***************************************************************************/
private Instant evaluateForDateTime()
{ {
ZoneId zoneId = ValueUtils.getSessionOrInstanceZoneId(); ZoneId zoneId = ValueUtils.getSessionOrInstanceZoneId();
@ -183,57 +154,7 @@ public class ThisOrLastPeriod extends AbstractFilterExpression<Serializable>
return operator.equals(Operator.THIS) ? startOfThisYear : startOfLastYear; return operator.equals(Operator.THIS) ? startOfThisYear : startOfLastYear;
} }
default -> throw (new QRuntimeException("Unsupported unit: " + timeUnit)); default -> throw (new QRuntimeException("Unsupported timeUnit: " + timeUnit));
}
}
/*******************************************************************************
**
*******************************************************************************/
public LocalDate evaluateForDate()
{
ZoneId zoneId = ValueUtils.getSessionOrInstanceZoneId();
LocalDate today = Instant.now().atZone(zoneId).toLocalDate();
switch(timeUnit)
{
case DAYS ->
{
return operator.equals(Operator.THIS) ? today : today.minusDays(1);
}
case WEEKS ->
{
LocalDate startOfThisWeek = today;
while(startOfThisWeek.get(ChronoField.DAY_OF_WEEK) != DayOfWeek.SUNDAY.getValue())
{
////////////////////////////////////////
// go backwards until sunday is found //
////////////////////////////////////////
startOfThisWeek = startOfThisWeek.minusDays(1);
}
return operator.equals(Operator.THIS) ? startOfThisWeek : startOfThisWeek.minusDays(7);
}
case MONTHS ->
{
Instant startOfThisMonth = ValueUtils.getStartOfMonthInZoneId(zoneId.getId());
LocalDateTime startOfThisMonthLDT = LocalDateTime.ofInstant(startOfThisMonth, ZoneId.of(zoneId.getId()));
LocalDateTime startOfLastMonthLDT = startOfThisMonthLDT.minusMonths(1);
Instant startOfLastMonth = startOfLastMonthLDT.toInstant(ZoneId.of(zoneId.getId()).getRules().getOffset(Instant.now()));
return (operator.equals(Operator.THIS) ? startOfThisMonth : startOfLastMonth).atZone(zoneId).toLocalDate();
}
case YEARS ->
{
Instant startOfThisYear = ValueUtils.getStartOfYearInZoneId(zoneId.getId());
LocalDateTime startOfThisYearLDT = LocalDateTime.ofInstant(startOfThisYear, zoneId);
LocalDateTime startOfLastYearLDT = startOfThisYearLDT.minusYears(1);
Instant startOfLastYear = startOfLastYearLDT.toInstant(zoneId.getRules().getOffset(Instant.now()));
return (operator.equals(Operator.THIS) ? startOfThisYear : startOfLastYear).atZone(zoneId).toLocalDate();
}
default -> throw (new QRuntimeException("Unsupported unit: " + timeUnit));
} }
} }

View File

@ -22,17 +22,15 @@
package com.kingsrook.qqq.backend.core.model.actions.tables.storage; package com.kingsrook.qqq.backend.core.model.actions.tables.storage;
import java.io.Serializable;
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput; import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
/******************************************************************************* /*******************************************************************************
** Input for Storage actions. ** Input for Storage actions.
*******************************************************************************/ *******************************************************************************/
public class StorageInput extends AbstractTableActionInput implements Serializable public class StorageInput extends AbstractTableActionInput
{ {
private String reference; private String reference;
private String contentType;
@ -76,35 +74,4 @@ public class StorageInput extends AbstractTableActionInput implements Serializab
return (this); return (this);
} }
/*******************************************************************************
** Getter for contentType
*******************************************************************************/
public String getContentType()
{
return (this.contentType);
}
/*******************************************************************************
** Setter for contentType
*******************************************************************************/
public void setContentType(String contentType)
{
this.contentType = contentType;
}
/*******************************************************************************
** Fluent setter for contentType
*******************************************************************************/
public StorageInput withContentType(String contentType)
{
this.contentType = contentType;
return (this);
}
} }

View File

@ -38,10 +38,9 @@ public class SearchPossibleValueSourceInput extends AbstractActionInput implemen
private QQueryFilter defaultQueryFilter; private QQueryFilter defaultQueryFilter;
private String searchTerm; private String searchTerm;
private List<Serializable> idList; private List<Serializable> idList;
private List<String> labelList;
private Integer skip = 0; private Integer skip = 0;
private Integer limit = 250; private Integer limit = 100;
@ -282,35 +281,4 @@ public class SearchPossibleValueSourceInput extends AbstractActionInput implemen
this.limit = limit; this.limit = limit;
return (this); return (this);
} }
/*******************************************************************************
** Getter for labelList
*******************************************************************************/
public List<String> getLabelList()
{
return (this.labelList);
}
/*******************************************************************************
** Setter for labelList
*******************************************************************************/
public void setLabelList(List<String> labelList)
{
this.labelList = labelList;
}
/*******************************************************************************
** Fluent setter for labelList
*******************************************************************************/
public SearchPossibleValueSourceInput withLabelList(List<String> labelList)
{
this.labelList = labelList;
return (this);
}
} }

View File

@ -35,7 +35,6 @@ public class SearchPossibleValueSourceOutput extends AbstractActionOutput
{ {
private List<QPossibleValue<?>> results = new ArrayList<>(); private List<QPossibleValue<?>> results = new ArrayList<>();
private String warning;
/******************************************************************************* /*******************************************************************************
@ -89,35 +88,4 @@ public class SearchPossibleValueSourceOutput extends AbstractActionOutput
return (this); return (this);
} }
/*******************************************************************************
** Getter for warning
*******************************************************************************/
public String getWarning()
{
return (this.warning);
}
/*******************************************************************************
** Setter for warning
*******************************************************************************/
public void setWarning(String warning)
{
this.warning = warning;
}
/*******************************************************************************
** Fluent setter for warning
*******************************************************************************/
public SearchPossibleValueSourceOutput withWarning(String warning)
{
this.warning = warning;
return (this);
}
} }

View File

@ -220,7 +220,7 @@ public class AuditsMetaDataProvider
.withRecordLabelFormat("%s %s") .withRecordLabelFormat("%s %s")
.withRecordLabelFields("auditTableId", "recordId") .withRecordLabelFields("auditTableId", "recordId")
.withPrimaryKeyField("id") .withPrimaryKeyField("id")
.withField(new QFieldMetaData("id", QFieldType.LONG)) .withField(new QFieldMetaData("id", QFieldType.INTEGER))
.withField(new QFieldMetaData("auditTableId", QFieldType.INTEGER).withPossibleValueSourceName(TABLE_NAME_AUDIT_TABLE)) .withField(new QFieldMetaData("auditTableId", QFieldType.INTEGER).withPossibleValueSourceName(TABLE_NAME_AUDIT_TABLE))
.withField(new QFieldMetaData("auditUserId", QFieldType.INTEGER).withPossibleValueSourceName(TABLE_NAME_AUDIT_USER)) .withField(new QFieldMetaData("auditUserId", QFieldType.INTEGER).withPossibleValueSourceName(TABLE_NAME_AUDIT_USER))
.withField(new QFieldMetaData("recordId", QFieldType.INTEGER)) .withField(new QFieldMetaData("recordId", QFieldType.INTEGER))
@ -243,8 +243,8 @@ public class AuditsMetaDataProvider
.withRecordLabelFormat("%s") .withRecordLabelFormat("%s")
.withRecordLabelFields("id") .withRecordLabelFields("id")
.withPrimaryKeyField("id") .withPrimaryKeyField("id")
.withField(new QFieldMetaData("id", QFieldType.LONG)) .withField(new QFieldMetaData("id", QFieldType.INTEGER))
.withField(new QFieldMetaData("auditId", QFieldType.LONG).withPossibleValueSourceName(TABLE_NAME_AUDIT)) .withField(new QFieldMetaData("auditId", QFieldType.INTEGER).withPossibleValueSourceName(TABLE_NAME_AUDIT))
.withField(new QFieldMetaData("message", QFieldType.STRING).withMaxLength(250).withBehavior(ValueTooLongBehavior.TRUNCATE_ELLIPSIS)) .withField(new QFieldMetaData("message", QFieldType.STRING).withMaxLength(250).withBehavior(ValueTooLongBehavior.TRUNCATE_ELLIPSIS))
.withField(new QFieldMetaData("fieldName", QFieldType.STRING).withMaxLength(100).withBehavior(ValueTooLongBehavior.TRUNCATE_ELLIPSIS)) .withField(new QFieldMetaData("fieldName", QFieldType.STRING).withMaxLength(100).withBehavior(ValueTooLongBehavior.TRUNCATE_ELLIPSIS))
.withField(new QFieldMetaData("oldValue", QFieldType.STRING).withMaxLength(250).withBehavior(ValueTooLongBehavior.TRUNCATE_ELLIPSIS)) .withField(new QFieldMetaData("oldValue", QFieldType.STRING).withMaxLength(250).withBehavior(ValueTooLongBehavior.TRUNCATE_ELLIPSIS))

View File

@ -22,9 +22,6 @@
package com.kingsrook.qqq.backend.core.model.dashboard.widgets; package com.kingsrook.qqq.backend.core.model.dashboard.widgets;
import java.util.List;
/******************************************************************************* /*******************************************************************************
** Model containing datastructure expected by frontend alert widget ** Model containing datastructure expected by frontend alert widget
** **
@ -43,10 +40,8 @@ public class AlertData extends QWidgetData
private String html; private String html;
private AlertType alertType; private AlertType alertType;
private Boolean hideWidget = false;
private List<String> bulletList;
@ -144,66 +139,4 @@ public class AlertData extends QWidgetData
return (this); return (this);
} }
/*******************************************************************************
** Getter for hideWidget
*******************************************************************************/
public boolean getHideWidget()
{
return (this.hideWidget);
}
/*******************************************************************************
** Setter for hideWidget
*******************************************************************************/
public void setHideWidget(boolean hideWidget)
{
this.hideWidget = hideWidget;
}
/*******************************************************************************
** Fluent setter for hideWidget
*******************************************************************************/
public AlertData withHideWidget(boolean hideWidget)
{
this.hideWidget = hideWidget;
return (this);
}
/*******************************************************************************
** Getter for bulletList
*******************************************************************************/
public List<String> getBulletList()
{
return (this.bulletList);
}
/*******************************************************************************
** Setter for bulletList
*******************************************************************************/
public void setBulletList(List<String> bulletList)
{
this.bulletList = bulletList;
}
/*******************************************************************************
** Fluent setter for bulletList
*******************************************************************************/
public AlertData withBulletList(List<String> bulletList)
{
this.bulletList = bulletList;
return (this);
}
} }

View File

@ -39,14 +39,9 @@ public class ChildRecordListData extends QWidgetData
private QueryOutput queryOutput; private QueryOutput queryOutput;
private QTableMetaData childTableMetaData; private QTableMetaData childTableMetaData;
private String tableName;
private String tablePath; private String tablePath;
private String viewAllLink; private String viewAllLink;
private Integer totalRows; private Integer totalRows;
private Boolean disableRowClick = false;
private Boolean allowRecordEdit = false;
private Boolean allowRecordDelete = false;
private Boolean isInProcess = false;
private boolean canAddChildRecord = false; private boolean canAddChildRecord = false;
private Map<String, Serializable> defaultValuesForNewChildRecords; private Map<String, Serializable> defaultValuesForNewChildRecords;
@ -357,173 +352,4 @@ public class ChildRecordListData extends QWidgetData
return (this); return (this);
} }
/*******************************************************************************
** Getter for tableName
*******************************************************************************/
public String getTableName()
{
return (this.tableName);
}
/*******************************************************************************
** Setter for tableName
*******************************************************************************/
public void setTableName(String tableName)
{
this.tableName = tableName;
}
/*******************************************************************************
** Fluent setter for tableName
*******************************************************************************/
public ChildRecordListData withTableName(String tableName)
{
this.tableName = tableName;
return (this);
}
/*******************************************************************************
** Fluent setter for tablePath
*******************************************************************************/
public ChildRecordListData withTablePath(String tablePath)
{
this.tablePath = tablePath;
return (this);
}
/*******************************************************************************
** Getter for disableRowClick
*******************************************************************************/
public Boolean getDisableRowClick()
{
return (this.disableRowClick);
}
/*******************************************************************************
** Setter for disableRowClick
*******************************************************************************/
public void setDisableRowClick(Boolean disableRowClick)
{
this.disableRowClick = disableRowClick;
}
/*******************************************************************************
** Fluent setter for disableRowClick
*******************************************************************************/
public ChildRecordListData withDisableRowClick(Boolean disableRowClick)
{
this.disableRowClick = disableRowClick;
return (this);
}
/*******************************************************************************
** Getter for allowRecordEdit
*******************************************************************************/
public Boolean getAllowRecordEdit()
{
return (this.allowRecordEdit);
}
/*******************************************************************************
** Setter for allowRecordEdit
*******************************************************************************/
public void setAllowRecordEdit(Boolean allowRecordEdit)
{
this.allowRecordEdit = allowRecordEdit;
}
/*******************************************************************************
** Fluent setter for allowRecordEdit
*******************************************************************************/
public ChildRecordListData withAllowRecordEdit(Boolean allowRecordEdit)
{
this.allowRecordEdit = allowRecordEdit;
return (this);
}
/*******************************************************************************
** Getter for allowRecordDelete
*******************************************************************************/
public Boolean getAllowRecordDelete()
{
return (this.allowRecordDelete);
}
/*******************************************************************************
** Setter for allowRecordDelete
*******************************************************************************/
public void setAllowRecordDelete(Boolean allowRecordDelete)
{
this.allowRecordDelete = allowRecordDelete;
}
/*******************************************************************************
** Fluent setter for allowRecordDelete
*******************************************************************************/
public ChildRecordListData withAllowRecordDelete(Boolean allowRecordDelete)
{
this.allowRecordDelete = allowRecordDelete;
return (this);
}
/*******************************************************************************
** Getter for isInProcess
*******************************************************************************/
public Boolean getIsInProcess()
{
return (this.isInProcess);
}
/*******************************************************************************
** Setter for isInProcess
*******************************************************************************/
public void setIsInProcess(Boolean isInProcess)
{
this.isInProcess = isInProcess;
}
/*******************************************************************************
** Fluent setter for isInProcess
*******************************************************************************/
public ChildRecordListData withIsInProcess(Boolean isInProcess)
{
this.isInProcess = isInProcess;
return (this);
}
} }

View File

@ -40,24 +40,9 @@ public class CompositeWidgetData extends AbstractBlockWidgetData<CompositeWidget
{ {
private List<AbstractBlockWidgetData<?, ?, ?, ?>> blocks = new ArrayList<>(); private List<AbstractBlockWidgetData<?, ?, ?, ?>> blocks = new ArrayList<>();
private ModalMode modalMode; private Map<String, Serializable> styleOverrides = new HashMap<>();
private Layout layout;
/***************************************************************************
**
***************************************************************************/
public enum ModalMode
{
MODAL
}
private Layout layout;
private Map<String, Serializable> styleOverrides = new HashMap<>();
private String overlayHtml;
private Map<String, Serializable> overlayStyleOverrides = new HashMap<>();
@ -66,14 +51,12 @@ public class CompositeWidgetData extends AbstractBlockWidgetData<CompositeWidget
*******************************************************************************/ *******************************************************************************/
public enum Layout public enum Layout
{ {
///////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////
// note, these are used in QQQ FMD CompositeWidget.tsx // // note, these are used in QQQ FMD CompositeWidgetData.tsx //
// and qqq-android CompositeWidgetBlock.kt // /////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
FLEX_COLUMN, FLEX_COLUMN,
FLEX_ROW_WRAPPED, FLEX_ROW_WRAPPED,
FLEX_ROW_SPACE_BETWEEN, FLEX_ROW_SPACE_BETWEEN,
FLEX_ROW_CENTER,
TABLE_SUB_ROW_DETAILS, TABLE_SUB_ROW_DETAILS,
BADGES_WRAPPER BADGES_WRAPPER
} }
@ -235,122 +218,4 @@ public class CompositeWidgetData extends AbstractBlockWidgetData<CompositeWidget
return (this); return (this);
} }
/*******************************************************************************
** Getter for overlayHtml
*******************************************************************************/
public String getOverlayHtml()
{
return (this.overlayHtml);
}
/*******************************************************************************
** Setter for overlayHtml
*******************************************************************************/
public void setOverlayHtml(String overlayHtml)
{
this.overlayHtml = overlayHtml;
}
/*******************************************************************************
** Fluent setter for overlayHtml
*******************************************************************************/
public CompositeWidgetData withOverlayHtml(String overlayHtml)
{
this.overlayHtml = overlayHtml;
return (this);
}
/*******************************************************************************
** Getter for overlayStyleOverrides
*******************************************************************************/
public Map<String, Serializable> getOverlayStyleOverrides()
{
return (this.overlayStyleOverrides);
}
/*******************************************************************************
** Setter for overlayStyleOverrides
*******************************************************************************/
public void setOverlayStyleOverrides(Map<String, Serializable> overlayStyleOverrides)
{
this.overlayStyleOverrides = overlayStyleOverrides;
}
/*******************************************************************************
** Fluent setter for overlayStyleOverrides
*******************************************************************************/
public CompositeWidgetData withOverlayStyleOverrides(Map<String, Serializable> overlayStyleOverrides)
{
this.overlayStyleOverrides = overlayStyleOverrides;
return (this);
}
/*******************************************************************************
**
*******************************************************************************/
public CompositeWidgetData withOverlayStyleOverride(String key, Serializable value)
{
addOverlayStyleOverride(key, value);
return (this);
}
/*******************************************************************************
**
*******************************************************************************/
public void addOverlayStyleOverride(String key, Serializable value)
{
if(this.overlayStyleOverrides == null)
{
this.overlayStyleOverrides = new HashMap<>();
}
this.overlayStyleOverrides.put(key, value);
}
/*******************************************************************************
** Getter for modalMode
*******************************************************************************/
public ModalMode getModalMode()
{
return (this.modalMode);
}
/*******************************************************************************
** Setter for modalMode
*******************************************************************************/
public void setModalMode(ModalMode modalMode)
{
this.modalMode = modalMode;
}
/*******************************************************************************
** Fluent setter for modalMode
*******************************************************************************/
public CompositeWidgetData withModalMode(ModalMode modalMode)
{
this.modalMode = modalMode;
return (this);
}
} }

View File

@ -31,7 +31,7 @@ import java.util.Map;
** Base class for the data returned by rendering a Widget. ** Base class for the data returned by rendering a Widget.
** **
*******************************************************************************/ *******************************************************************************/
public abstract class QWidgetData implements Serializable public abstract class QWidgetData
{ {
private String label; private String label;
private String sublabel; private String sublabel;

View File

@ -24,7 +24,6 @@ package com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.CompositeWidgetData;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.QWidgetData; import com.kingsrook.qqq.backend.core.model.dashboard.widgets.QWidgetData;
@ -52,11 +51,6 @@ public abstract class AbstractBlockWidgetData<
private V values; private V values;
private SX styles; private SX styles;
///////////////////////////////////////////////////////////////////////////////////
// optional field name to act as a 'guard' for the block - e.g., only include it //
// if the value for this field is true //
///////////////////////////////////////////////////////////////////////////////////
private String conditional;
/******************************************************************************* /*******************************************************************************
@ -209,19 +203,6 @@ public abstract class AbstractBlockWidgetData<
/*******************************************************************************
** Fluent setter for tooltip
**
*******************************************************************************/
@SuppressWarnings("unchecked")
public T withTooltip(CompositeWidgetData data)
{
this.tooltip = new BlockTooltip(data);
return (T) (this);
}
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
@ -417,7 +398,6 @@ public abstract class AbstractBlockWidgetData<
} }
/******************************************************************************* /*******************************************************************************
** Getter for blockId ** Getter for blockId
*******************************************************************************/ *******************************************************************************/
@ -449,34 +429,4 @@ public abstract class AbstractBlockWidgetData<
} }
/*******************************************************************************
** Getter for conditional
*******************************************************************************/
public String getConditional()
{
return (this.conditional);
}
/*******************************************************************************
** Setter for conditional
*******************************************************************************/
public void setConditional(String conditional)
{
this.conditional = conditional;
}
/*******************************************************************************
** Fluent setter for conditional
*******************************************************************************/
public AbstractBlockWidgetData withConditional(String conditional)
{
this.conditional = conditional;
return (this);
}
} }

View File

@ -22,18 +22,14 @@
package com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks; package com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.CompositeWidgetData;
/******************************************************************************* /*******************************************************************************
** A tooltip used within a (widget) block. ** A tooltip used within a (widget) block.
** **
*******************************************************************************/ *******************************************************************************/
public class BlockTooltip public class BlockTooltip
{ {
private CompositeWidgetData blockData; private String title;
private String title; private Placement placement = Placement.BOTTOM;
private Placement placement = Placement.BOTTOM;
@ -66,17 +62,6 @@ public class BlockTooltip
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public BlockTooltip(CompositeWidgetData blockData)
{
this.blockData = blockData;
}
/******************************************************************************* /*******************************************************************************
** Getter for title ** Getter for title
*******************************************************************************/ *******************************************************************************/
@ -137,35 +122,4 @@ public class BlockTooltip
return (this); return (this);
} }
/*******************************************************************************
** Getter for blockData
*******************************************************************************/
public CompositeWidgetData getBlockData()
{
return (this.blockData);
}
/*******************************************************************************
** Setter for blockData
*******************************************************************************/
public void setBlockData(CompositeWidgetData blockData)
{
this.blockData = blockData;
}
/*******************************************************************************
** Fluent setter for blockData
*******************************************************************************/
public BlockTooltip withBlockData(CompositeWidgetData blockData)
{
this.blockData = blockData;
return (this);
}
} }

View File

@ -1,45 +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.model.dashboard.widgets.blocks.audio;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.AbstractBlockWidgetData;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.base.BaseSlots;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.base.BaseStyles;
/*******************************************************************************
** block that plays an audio file
*******************************************************************************/
public class AudioBlockData extends AbstractBlockWidgetData<AudioBlockData, AudioValues, BaseSlots, BaseStyles>
{
/*******************************************************************************
**
*******************************************************************************/
@Override
public String getBlockTypeName()
{
return "AUDIO";
}
}

View File

@ -1,130 +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.model.dashboard.widgets.blocks.audio;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.BlockValuesInterface;
/*******************************************************************************
**
*******************************************************************************/
public class AudioValues implements BlockValuesInterface
{
private String path;
private boolean showControls = false;
private boolean autoPlay = true;
/*******************************************************************************
** Getter for path
*******************************************************************************/
public String getPath()
{
return (this.path);
}
/*******************************************************************************
** Setter for path
*******************************************************************************/
public void setPath(String path)
{
this.path = path;
}
/*******************************************************************************
** Fluent setter for path
*******************************************************************************/
public AudioValues withPath(String path)
{
this.path = path;
return (this);
}
/*******************************************************************************
** Getter for showControls
*******************************************************************************/
public boolean getShowControls()
{
return (this.showControls);
}
/*******************************************************************************
** Setter for showControls
*******************************************************************************/
public void setShowControls(boolean showControls)
{
this.showControls = showControls;
}
/*******************************************************************************
** Fluent setter for showControls
*******************************************************************************/
public AudioValues withShowControls(boolean showControls)
{
this.showControls = showControls;
return (this);
}
/*******************************************************************************
** Getter for autoPlay
*******************************************************************************/
public boolean getAutoPlay()
{
return (this.autoPlay);
}
/*******************************************************************************
** Setter for autoPlay
*******************************************************************************/
public void setAutoPlay(boolean autoPlay)
{
this.autoPlay = autoPlay;
}
/*******************************************************************************
** Fluent setter for autoPlay
*******************************************************************************/
public AudioValues withAutoPlay(boolean autoPlay)
{
this.autoPlay = autoPlay;
return (this);
}
}

View File

@ -30,335 +30,4 @@ import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.BlockStyles
*******************************************************************************/ *******************************************************************************/
public class BaseStyles implements BlockStylesInterface public class BaseStyles implements BlockStylesInterface
{ {
private Directional<String> padding;
private String backgroundColor;
/***************************************************************************
**
***************************************************************************/
public static class Directional<T>
{
private T top;
private T bottom;
private T left;
private T right;
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public Directional()
{
}
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public Directional(T top, T right, T bottom, T left)
{
this.top = top;
this.right = right;
this.bottom = bottom;
this.left = left;
}
/***************************************************************************
**
***************************************************************************/
public static <T> Directional<T> of(T top, T right, T bottom, T left)
{
return (new Directional<>(top, right, bottom, left));
}
/***************************************************************************
**
***************************************************************************/
public static <T> Directional<T> of(T value)
{
return (new Directional<>(value, value, value, value));
}
/***************************************************************************
**
***************************************************************************/
public static <T> Directional<T> ofTop(T top)
{
return (new Directional<>(top, null, null, null));
}
/***************************************************************************
**
***************************************************************************/
public static <T> Directional<T> ofRight(T right)
{
return (new Directional<>(null, right, null, null));
}
/***************************************************************************
**
***************************************************************************/
public static <T> Directional<T> ofBottom(T bottom)
{
return (new Directional<>(null, null, bottom, null));
}
/***************************************************************************
**
***************************************************************************/
public static <T> Directional<T> ofLeft(T left)
{
return (new Directional<>(null, null, null, left));
}
/***************************************************************************
**
***************************************************************************/
public static <T> Directional<T> ofX(T x)
{
return (new Directional<>(null, x, null, x));
}
/***************************************************************************
**
***************************************************************************/
public static <T> Directional<T> ofY(T y)
{
return (new Directional<>(y, null, y, null));
}
/***************************************************************************
**
***************************************************************************/
public static <T> Directional<T> ofXY(T x, T y)
{
return (new Directional<>(y, x, y, x));
}
/*******************************************************************************
** Getter for top
**
*******************************************************************************/
public T getTop()
{
return top;
}
/*******************************************************************************
** Setter for top
**
*******************************************************************************/
public void setTop(T top)
{
this.top = top;
}
/*******************************************************************************
** Fluent setter for top
**
*******************************************************************************/
public Directional<T> withTop(T top)
{
this.top = top;
return (this);
}
/*******************************************************************************
** Getter for bottom
**
*******************************************************************************/
public T getBottom()
{
return bottom;
}
/*******************************************************************************
** Setter for bottom
**
*******************************************************************************/
public void setBottom(T bottom)
{
this.bottom = bottom;
}
/*******************************************************************************
** Fluent setter for bottom
**
*******************************************************************************/
public Directional<T> withBottom(T bottom)
{
this.bottom = bottom;
return (this);
}
/*******************************************************************************
** Getter for left
**
*******************************************************************************/
public T getLeft()
{
return left;
}
/*******************************************************************************
** Setter for left
**
*******************************************************************************/
public void setLeft(T left)
{
this.left = left;
}
/*******************************************************************************
** Fluent setter for left
**
*******************************************************************************/
public Directional<T> withLeft(T left)
{
this.left = left;
return (this);
}
/*******************************************************************************
** Getter for right
**
*******************************************************************************/
public T getRight()
{
return right;
}
/*******************************************************************************
** Setter for right
**
*******************************************************************************/
public void setRight(T right)
{
this.right = right;
}
/*******************************************************************************
** Fluent setter for right
**
*******************************************************************************/
public Directional<T> withRight(T right)
{
this.right = right;
return (this);
}
}
/*******************************************************************************
** Getter for padding
*******************************************************************************/
public Directional<String> getPadding()
{
return (this.padding);
}
/*******************************************************************************
** Setter for padding
*******************************************************************************/
public void setPadding(Directional<String> padding)
{
this.padding = padding;
}
/*******************************************************************************
** Fluent setter for padding
*******************************************************************************/
public BaseStyles withPadding(Directional<String> padding)
{
this.padding = padding;
return (this);
}
/*******************************************************************************
** Getter for backgroundColor
*******************************************************************************/
public String getBackgroundColor()
{
return (this.backgroundColor);
}
/*******************************************************************************
** Setter for backgroundColor
*******************************************************************************/
public void setBackgroundColor(String backgroundColor)
{
this.backgroundColor = backgroundColor;
}
/*******************************************************************************
** Fluent setter for backgroundColor
*******************************************************************************/
public BaseStyles withBackgroundColor(String backgroundColor)
{
this.backgroundColor = backgroundColor;
return (this);
}
} }

View File

@ -1,46 +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.model.dashboard.widgets.blocks.button;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.AbstractBlockWidgetData;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.base.BaseSlots;
/*******************************************************************************
** a button (for a process - not sure yet what this could do in a standalone
** widget?) to submit the process screen to run a specific action (e.g., not just
** 'next'), or do other control-ish things
*******************************************************************************/
public class ButtonBlockData extends AbstractBlockWidgetData<ButtonBlockData, ButtonValues, BaseSlots, ButtonStyles>
{
/*******************************************************************************
**
*******************************************************************************/
@Override
public String getBlockTypeName()
{
return "BUTTON";
}
}

View File

@ -1,143 +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.model.dashboard.widgets.blocks.button;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.BlockStylesInterface;
/*******************************************************************************
**
*******************************************************************************/
public class ButtonStyles implements BlockStylesInterface
{
private String color;
private String format;
/***************************************************************************
**
***************************************************************************/
public enum StandardColor
{
SUCCESS,
WARNING,
ERROR,
INFO,
MUTED
}
/***************************************************************************
**
***************************************************************************/
public enum StandardFormat
{
OUTLINED,
FILLED,
TEXT
}
/*******************************************************************************
** Getter for color
*******************************************************************************/
public String getColor()
{
return (this.color);
}
/*******************************************************************************
** Setter for color
*******************************************************************************/
public void setColor(String color)
{
this.color = color;
}
/*******************************************************************************
** Fluent setter for color
*******************************************************************************/
public ButtonStyles withColor(String color)
{
this.color = color;
return (this);
}
/*******************************************************************************
** Getter for format
*******************************************************************************/
public String getFormat()
{
return (this.format);
}
/*******************************************************************************
** Setter for format
*******************************************************************************/
public void setFormat(String format)
{
this.format = format;
}
/*******************************************************************************
** Fluent setter for format
*******************************************************************************/
public ButtonStyles withFormat(String format)
{
this.format = format;
return (this);
}
/*******************************************************************************
** Setter for format
*******************************************************************************/
public void setFormat(StandardFormat format)
{
this.format = (format == null ? null : format.name().toLowerCase());
}
/*******************************************************************************
** Fluent setter for format
*******************************************************************************/
public ButtonStyles withFormat(StandardFormat format)
{
setFormat(format);
return (this);
}
}

View File

@ -1,218 +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.model.dashboard.widgets.blocks.button;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.BlockValuesInterface;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
/*******************************************************************************
**
*******************************************************************************/
public class ButtonValues implements BlockValuesInterface
{
private String label;
private String actionCode;
private String controlCode;
private QIcon startIcon;
private QIcon endIcon;
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public ButtonValues()
{
}
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public ButtonValues(String label, String actionCode)
{
setLabel(label);
setActionCode(actionCode);
}
/*******************************************************************************
** 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 ButtonValues withLabel(String label)
{
this.label = label;
return (this);
}
/*******************************************************************************
** Getter for actionCode
*******************************************************************************/
public String getActionCode()
{
return (this.actionCode);
}
/*******************************************************************************
** Setter for actionCode
*******************************************************************************/
public void setActionCode(String actionCode)
{
this.actionCode = actionCode;
}
/*******************************************************************************
** Fluent setter for actionCode
*******************************************************************************/
public ButtonValues withActionCode(String actionCode)
{
this.actionCode = actionCode;
return (this);
}
/*******************************************************************************
** Getter for startIcon
*******************************************************************************/
public QIcon getStartIcon()
{
return (this.startIcon);
}
/*******************************************************************************
** Setter for startIcon
*******************************************************************************/
public void setStartIcon(QIcon startIcon)
{
this.startIcon = startIcon;
}
/*******************************************************************************
** Fluent setter for startIcon
*******************************************************************************/
public ButtonValues withStartIcon(QIcon startIcon)
{
this.startIcon = startIcon;
return (this);
}
/*******************************************************************************
** Getter for endIcon
*******************************************************************************/
public QIcon getEndIcon()
{
return (this.endIcon);
}
/*******************************************************************************
** Setter for endIcon
*******************************************************************************/
public void setEndIcon(QIcon endIcon)
{
this.endIcon = endIcon;
}
/*******************************************************************************
** Fluent setter for endIcon
*******************************************************************************/
public ButtonValues withEndIcon(QIcon endIcon)
{
this.endIcon = endIcon;
return (this);
}
/*******************************************************************************
** Getter for controlCode
*******************************************************************************/
public String getControlCode()
{
return (this.controlCode);
}
/*******************************************************************************
** Setter for controlCode
*******************************************************************************/
public void setControlCode(String controlCode)
{
this.controlCode = controlCode;
}
/*******************************************************************************
** Fluent setter for controlCode
*******************************************************************************/
public ButtonValues withControlCode(String controlCode)
{
this.controlCode = controlCode;
return (this);
}
}

View File

@ -1,44 +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.model.dashboard.widgets.blocks.image;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.AbstractBlockWidgetData;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.base.BaseSlots;
/*******************************************************************************
** block to display an image
*******************************************************************************/
public class ImageBlockData extends AbstractBlockWidgetData<ImageBlockData, ImageValues, BaseSlots, ImageStyles>
{
/*******************************************************************************
**
*******************************************************************************/
@Override
public String getBlockTypeName()
{
return "IMAGE";
}
}

View File

@ -1,108 +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.model.dashboard.widgets.blocks.image;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.base.BaseStyles;
/*******************************************************************************
**
*******************************************************************************/
public class ImageStyles extends BaseStyles
{
private String width;
private String height;
/*******************************************************************************
** Fluent setter for padding
*******************************************************************************/
@Override
public ImageStyles withPadding(Directional<String> padding)
{
super.setPadding(padding);
return (this);
}
/*******************************************************************************
** Getter for width
*******************************************************************************/
public String getWidth()
{
return (this.width);
}
/*******************************************************************************
** Setter for width
*******************************************************************************/
public void setWidth(String width)
{
this.width = width;
}
/*******************************************************************************
** Fluent setter for width
*******************************************************************************/
public ImageStyles withWidth(String width)
{
this.width = width;
return (this);
}
/*******************************************************************************
** Getter for height
*******************************************************************************/
public String getHeight()
{
return (this.height);
}
/*******************************************************************************
** Setter for height
*******************************************************************************/
public void setHeight(String height)
{
this.height = height;
}
/*******************************************************************************
** Fluent setter for height
*******************************************************************************/
public ImageStyles withHeight(String height)
{
this.height = height;
return (this);
}
}

View File

@ -1,98 +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.model.dashboard.widgets.blocks.image;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.BlockValuesInterface;
/*******************************************************************************
**
*******************************************************************************/
public class ImageValues implements BlockValuesInterface
{
private String path;
private String alt;
/*******************************************************************************
** Getter for path
*******************************************************************************/
public String getPath()
{
return (this.path);
}
/*******************************************************************************
** Setter for path
*******************************************************************************/
public void setPath(String path)
{
this.path = path;
}
/*******************************************************************************
** Fluent setter for path
*******************************************************************************/
public ImageValues withPath(String path)
{
this.path = path;
return (this);
}
/*******************************************************************************
** Getter for alt
*******************************************************************************/
public String getAlt()
{
return (this.alt);
}
/*******************************************************************************
** Setter for alt
*******************************************************************************/
public void setAlt(String alt)
{
this.alt = alt;
}
/*******************************************************************************
** Fluent setter for alt
*******************************************************************************/
public ImageValues withAlt(String alt)
{
this.alt = alt;
return (this);
}
}

View File

@ -1,45 +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.model.dashboard.widgets.blocks.inputfield;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.AbstractBlockWidgetData;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.base.BaseSlots;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.base.BaseStyles;
/*******************************************************************************
** block to display an input field - initially targeted at widgets-in-processes
*******************************************************************************/
public class InputFieldBlockData extends AbstractBlockWidgetData<InputFieldBlockData, InputFieldValues, BaseSlots, BaseStyles>
{
/*******************************************************************************
**
*******************************************************************************/
@Override
public String getBlockTypeName()
{
return "INPUT_FIELD";
}
}

View File

@ -1,217 +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.model.dashboard.widgets.blocks.inputfield;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.BlockValuesInterface;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
/*******************************************************************************
**
*******************************************************************************/
public class InputFieldValues implements BlockValuesInterface
{
private QFieldMetaData fieldMetaData;
private Boolean autoFocus;
private Boolean submitOnEnter;
private Boolean hideSoftKeyboard;
private String placeholder;
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public InputFieldValues()
{
}
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public InputFieldValues(QFieldMetaData fieldMetaData)
{
setFieldMetaData(fieldMetaData);
}
/*******************************************************************************
** Getter for fieldMetaData
*******************************************************************************/
public QFieldMetaData getFieldMetaData()
{
return (this.fieldMetaData);
}
/*******************************************************************************
** Setter for fieldMetaData
*******************************************************************************/
public void setFieldMetaData(QFieldMetaData fieldMetaData)
{
this.fieldMetaData = fieldMetaData;
}
/*******************************************************************************
** Fluent setter for fieldMetaData
*******************************************************************************/
public InputFieldValues withFieldMetaData(QFieldMetaData fieldMetaData)
{
this.fieldMetaData = fieldMetaData;
return (this);
}
/*******************************************************************************
** Getter for autoFocus
*******************************************************************************/
public Boolean getAutoFocus()
{
return (this.autoFocus);
}
/*******************************************************************************
** Setter for autoFocus
*******************************************************************************/
public void setAutoFocus(Boolean autoFocus)
{
this.autoFocus = autoFocus;
}
/*******************************************************************************
** Fluent setter for autoFocus
*******************************************************************************/
public InputFieldValues withAutoFocus(Boolean autoFocus)
{
this.autoFocus = autoFocus;
return (this);
}
/*******************************************************************************
** Getter for submitOnEnter
*******************************************************************************/
public Boolean getSubmitOnEnter()
{
return (this.submitOnEnter);
}
/*******************************************************************************
** Setter for submitOnEnter
*******************************************************************************/
public void setSubmitOnEnter(Boolean submitOnEnter)
{
this.submitOnEnter = submitOnEnter;
}
/*******************************************************************************
** Fluent setter for submitOnEnter
*******************************************************************************/
public InputFieldValues withSubmitOnEnter(Boolean submitOnEnter)
{
this.submitOnEnter = submitOnEnter;
return (this);
}
/*******************************************************************************
** Getter for placeholder
*******************************************************************************/
public String getPlaceholder()
{
return (this.placeholder);
}
/*******************************************************************************
** Setter for placeholder
*******************************************************************************/
public void setPlaceholder(String placeholder)
{
this.placeholder = placeholder;
}
/*******************************************************************************
** Fluent setter for placeholder
*******************************************************************************/
public InputFieldValues withPlaceholder(String placeholder)
{
this.placeholder = placeholder;
return (this);
}
/*******************************************************************************
** Getter for hideSoftKeyboard
*******************************************************************************/
public Boolean getHideSoftKeyboard()
{
return (this.hideSoftKeyboard);
}
/*******************************************************************************
** Setter for hideSoftKeyboard
*******************************************************************************/
public void setHideSoftKeyboard(Boolean hideSoftKeyboard)
{
this.hideSoftKeyboard = hideSoftKeyboard;
}
/*******************************************************************************
** Fluent setter for hideSoftKeyboard
*******************************************************************************/
public InputFieldValues withHideSoftKeyboard(Boolean hideSoftKeyboard)
{
this.hideSoftKeyboard = hideSoftKeyboard;
return (this);
}
}

View File

@ -30,325 +30,4 @@ import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.BlockStyles
*******************************************************************************/ *******************************************************************************/
public class TextStyles implements BlockStylesInterface public class TextStyles implements BlockStylesInterface
{ {
private String color;
private String format;
private String weight;
private String size;
/***************************************************************************
**
***************************************************************************/
public enum StandardColor
{
SUCCESS,
WARNING,
ERROR,
INFO,
MUTED
}
/***************************************************************************
**
***************************************************************************/
public enum StandardFormat
{
DEFAULT,
ALERT,
BANNER
}
/***************************************************************************
**
***************************************************************************/
public enum StandardSize
{
LARGEST,
HEADLINE,
TITLE,
BODY,
SMALLEST
}
/***************************************************************************
**
***************************************************************************/
public enum StandardWeight
{
EXTRA_LIGHT("extralight"),
THIN("thin"),
MEDIUM("medium"),
SEMI_BOLD("semibold"),
BLACK("black"),
BOLD("bold"),
EXTRA_BOLD("extrabold"),
W100("100"),
W200("200"),
W300("300"),
W400("400"),
W500("500"),
W600("600"),
W700("700"),
W800("800"),
W900("900");
private final String value;
/*******************************************************************************
** Constructor
**
*******************************************************************************/
StandardWeight(String value)
{
this.value = value;
}
/*******************************************************************************
** Getter for value
**
*******************************************************************************/
public String getValue()
{
return value;
}
}
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public TextStyles()
{
}
/***************************************************************************
**
***************************************************************************/
public TextStyles(StandardColor standardColor)
{
setColor(standardColor);
}
/*******************************************************************************
** Getter for format
*******************************************************************************/
public String getFormat()
{
return (this.format);
}
/*******************************************************************************
** Setter for format
*******************************************************************************/
public void setFormat(String format)
{
this.format = format;
}
/*******************************************************************************
** Fluent setter for format
*******************************************************************************/
public TextStyles withFormat(String format)
{
this.format = format;
return (this);
}
/*******************************************************************************
** Setter for format
*******************************************************************************/
public void setFormat(StandardFormat format)
{
this.format = format == null ? null : format.name().toLowerCase();
}
/*******************************************************************************
** Fluent setter for format
*******************************************************************************/
public TextStyles withFormat(StandardFormat format)
{
this.setFormat(format);
return (this);
}
/*******************************************************************************
** Getter for weight
*******************************************************************************/
public String getWeight()
{
return (this.weight);
}
/*******************************************************************************
** Setter for weight
*******************************************************************************/
public void setWeight(String weight)
{
this.weight = weight;
}
/*******************************************************************************
** Fluent setter for weight
*******************************************************************************/
public TextStyles withWeight(String weight)
{
this.weight = weight;
return (this);
}
/*******************************************************************************
** Setter for weight
*******************************************************************************/
public void setWeight(StandardWeight weight)
{
setWeight(weight == null ? null : weight.getValue());
}
/*******************************************************************************
** Fluent setter for weight
*******************************************************************************/
public TextStyles withWeight(StandardWeight weight)
{
setWeight(weight);
return (this);
}
/*******************************************************************************
** Getter for size
*******************************************************************************/
public String getSize()
{
return (this.size);
}
/*******************************************************************************
** Setter for size
*******************************************************************************/
public void setSize(String size)
{
this.size = size;
}
/*******************************************************************************
** Fluent setter for size
*******************************************************************************/
public TextStyles withSize(String size)
{
this.size = size;
return (this);
}
/*******************************************************************************
** Setter for size
*******************************************************************************/
public void setSize(StandardSize size)
{
this.size = (size == null ? null : size.name().toLowerCase());
}
/*******************************************************************************
** Fluent setter for size
*******************************************************************************/
public TextStyles withSize(StandardSize size)
{
setSize(size);
return (this);
}
/*******************************************************************************
** Getter for color
*******************************************************************************/
public String getColor()
{
return (this.color);
}
/*******************************************************************************
** Setter for color
*******************************************************************************/
public void setColor(String color)
{
this.color = color;
}
/*******************************************************************************
** Fluent setter for color
*******************************************************************************/
public TextStyles withColor(String color)
{
this.color = color;
return (this);
}
/*******************************************************************************
** Setter for color
*******************************************************************************/
public void setColor(StandardColor color)
{
this.color = color == null ? null : color.name();
}
/*******************************************************************************
** Fluent setter for color
*******************************************************************************/
public TextStyles withColor(StandardColor color)
{
setColor(color);
return (this);
}
} }

View File

@ -23,7 +23,6 @@ package com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.text;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.BlockValuesInterface; import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.BlockValuesInterface;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
/******************************************************************************* /*******************************************************************************
@ -33,9 +32,6 @@ public class TextValues implements BlockValuesInterface
{ {
private String text; private String text;
private QIcon startIcon;
private QIcon endIcon;
/******************************************************************************* /*******************************************************************************
@ -88,66 +84,4 @@ public class TextValues implements BlockValuesInterface
return (this); return (this);
} }
/*******************************************************************************
** Getter for startIcon
*******************************************************************************/
public QIcon getStartIcon()
{
return (this.startIcon);
}
/*******************************************************************************
** Setter for startIcon
*******************************************************************************/
public void setStartIcon(QIcon startIcon)
{
this.startIcon = startIcon;
}
/*******************************************************************************
** Fluent setter for startIcon
*******************************************************************************/
public TextValues withStartIcon(QIcon startIcon)
{
this.startIcon = startIcon;
return (this);
}
/*******************************************************************************
** Getter for endIcon
*******************************************************************************/
public QIcon getEndIcon()
{
return (this.endIcon);
}
/*******************************************************************************
** Setter for endIcon
*******************************************************************************/
public void setEndIcon(QIcon endIcon)
{
this.endIcon = endIcon;
}
/*******************************************************************************
** Fluent setter for endIcon
*******************************************************************************/
public TextValues withEndIcon(QIcon endIcon)
{
this.endIcon = endIcon;
return (this);
}
} }

View File

@ -22,6 +22,9 @@
package com.kingsrook.qqq.backend.core.model.metadata; package com.kingsrook.qqq.backend.core.model.metadata;
import com.kingsrook.qqq.backend.core.model.MetaDataProducerInterface;
/******************************************************************************* /*******************************************************************************
** Abstract class that knows how to produce meta data objects. Useful with ** Abstract class that knows how to produce meta data objects. Useful with
** MetaDataProducerHelper, to put point at a package full of these, and populate ** MetaDataProducerHelper, to put point at a package full of these, and populate

View File

@ -22,6 +22,7 @@
package com.kingsrook.qqq.backend.core.model.metadata; package com.kingsrook.qqq.backend.core.model.metadata;
import java.io.IOException;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.ArrayList; import java.util.ArrayList;
@ -29,12 +30,14 @@ 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.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;
import com.kingsrook.qqq.backend.core.model.MetaDataProducerInterface;
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaData; import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaData;
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.model.metadata.layout.QAppMetaData; import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
import com.kingsrook.qqq.backend.core.utils.ClassPathUtils;
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair; import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
@ -48,6 +51,8 @@ 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
{ {
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@ -82,7 +87,7 @@ public class MetaDataProducerHelper
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
// find all the meta data producer classes in (and under) the package // // find all the meta data producer classes in (and under) the package //
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
classesInPackage = ClassPathUtils.getClassesInPackage(packageName); classesInPackage = getClassesInPackage(packageName);
} }
catch(Exception e) catch(Exception e)
{ {
@ -171,4 +176,51 @@ public class MetaDataProducerHelper
} }
/*******************************************************************************
** from https://stackoverflow.com/questions/520328/can-you-find-all-classes-in-a-package-using-reflection
** (since the original, from ChatGPT, didn't work in jars, despite GPT hallucinating that it would)
*******************************************************************************/
private static List<Class<?>> getClassesInPackage(String packageName) throws IOException
{
List<Class<?>> classes = new ArrayList<>();
ClassLoader loader = Thread.currentThread().getContextClassLoader();
for(ClassPath.ClassInfo info : getTopLevelClasses(loader))
{
if(info.getName().startsWith(packageName))
{
classes.add(info.load());
}
}
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;
}
} }

View File

@ -43,7 +43,6 @@ import com.kingsrook.qqq.backend.core.model.metadata.audits.QAuditRules;
import com.kingsrook.qqq.backend.core.model.metadata.authentication.QAuthenticationMetaData; import com.kingsrook.qqq.backend.core.model.metadata.authentication.QAuthenticationMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.automation.QAutomationProviderMetaData; import com.kingsrook.qqq.backend.core.model.metadata.automation.QAutomationProviderMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.branding.QBrandingMetaData; import com.kingsrook.qqq.backend.core.model.metadata.branding.QBrandingMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataInterface; import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataInterface;
import com.kingsrook.qqq.backend.core.model.metadata.frontend.AppTreeNode; import com.kingsrook.qqq.backend.core.model.metadata.frontend.AppTreeNode;
import com.kingsrook.qqq.backend.core.model.metadata.frontend.AppTreeNodeType; import com.kingsrook.qqq.backend.core.model.metadata.frontend.AppTreeNodeType;
@ -114,8 +113,6 @@ public class QInstance
private QPermissionRules defaultPermissionRules = QPermissionRules.defaultInstance(); private QPermissionRules defaultPermissionRules = QPermissionRules.defaultInstance();
private QAuditRules defaultAuditRules = QAuditRules.defaultInstanceLevelNone(); private QAuditRules defaultAuditRules = QAuditRules.defaultInstanceLevelNone();
private QCodeReference metaDataFilter = null;
////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////
// todo - lock down the object (no more changes allowed) after it's been validated? // // todo - lock down the object (no more changes allowed) after it's been validated? //
// if doing so, may need to copy all of the collections into read-only versions... // // if doing so, may need to copy all of the collections into read-only versions... //
@ -1488,35 +1485,4 @@ public class QInstance
QInstanceHelpContentManager.removeHelpContentByRoleSetFromList(roles, listForSlot); QInstanceHelpContentManager.removeHelpContentByRoleSetFromList(roles, listForSlot);
} }
/*******************************************************************************
** Getter for metaDataFilter
*******************************************************************************/
public QCodeReference getMetaDataFilter()
{
return (this.metaDataFilter);
}
/*******************************************************************************
** Setter for metaDataFilter
*******************************************************************************/
public void setMetaDataFilter(QCodeReference metaDataFilter)
{
this.metaDataFilter = metaDataFilter;
}
/*******************************************************************************
** Fluent setter for metaDataFilter
*******************************************************************************/
public QInstance withMetaDataFilter(QCodeReference metaDataFilter)
{
this.metaDataFilter = metaDataFilter;
return (this);
}
} }

View File

@ -177,7 +177,7 @@ public class QAuthenticationMetaData implements TopLevelMetaDataInterface
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public QAuthenticationMetaData withValues(Map<String, String> values) public QAuthenticationMetaData withVales(Map<String, String> values)
{ {
this.values = values; this.values = values;
return (this); return (this);

View File

@ -1,58 +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.model.metadata.code;
/*******************************************************************************
** Specialized type of QCodeReference that takes a lambda function object.
**
** Originally intended for more concise setup of backend steps in tests - but,
** may be generally useful.
*******************************************************************************/
public class QCodeReferenceLambda<T> extends QCodeReference
{
private final T lambda;
/***************************************************************************
**
***************************************************************************/
public QCodeReferenceLambda(T lambda)
{
this.lambda = lambda;
this.setCodeType(QCodeType.JAVA);
this.setName("[Lambda:" + lambda.toString() + "]");
}
/*******************************************************************************
** Getter for lambda
**
*******************************************************************************/
public T getLambda()
{
return lambda;
}
}

View File

@ -41,7 +41,6 @@ public enum AdornmentType
RENDER_HTML, RENDER_HTML,
REVEAL, REVEAL,
FILE_DOWNLOAD, FILE_DOWNLOAD,
FILE_UPLOAD,
ERROR; ERROR;
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// keep these values in sync with AdornmentType.ts in qqq-frontend-core // // keep these values in sync with AdornmentType.ts in qqq-frontend-core //
@ -165,65 +164,4 @@ public enum AdornmentType
} }
} }
/*******************************************************************************
**
*******************************************************************************/
public static class FileUploadAdornment
{
public static String FORMAT = "format";
public static String WIDTH = "width";
/***************************************************************************
**
***************************************************************************/
public static FieldAdornment newFieldAdornment()
{
return (new FieldAdornment(AdornmentType.FILE_UPLOAD));
}
/***************************************************************************
**
***************************************************************************/
public static Pair<String, String> formatDragAndDrop()
{
return (Pair.of(FORMAT, "dragAndDrop"));
}
/***************************************************************************
**
***************************************************************************/
public static Pair<String, String> formatButton()
{
return (Pair.of(FORMAT, "button"));
}
/***************************************************************************
**
***************************************************************************/
public static Pair<String, String> widthFull()
{
return (Pair.of(WIDTH, "full"));
}
/***************************************************************************
**
***************************************************************************/
public static Pair<String, String> widthHalf()
{
return (Pair.of(WIDTH, "half"));
}
}
} }

View File

@ -177,7 +177,7 @@ public class FieldAdornment
** Fluent setter for values ** Fluent setter for values
** **
*******************************************************************************/ *******************************************************************************/
public FieldAdornment withValue(Pair<String, ? extends Serializable> value) public FieldAdornment withValue(Pair<String, Serializable> value)
{ {
return (withValue(value.getA(), value.getB())); return (withValue(value.getA(), value.getB()));
} }

View File

@ -42,7 +42,6 @@ import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
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.help.HelpRole; import com.kingsrook.qqq.backend.core.model.metadata.help.HelpRole;
import com.kingsrook.qqq.backend.core.model.metadata.help.QHelpContent; import com.kingsrook.qqq.backend.core.model.metadata.help.QHelpContent;
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
import com.kingsrook.qqq.backend.core.model.metadata.security.FieldSecurityLock; import com.kingsrook.qqq.backend.core.model.metadata.security.FieldSecurityLock;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.StringUtils; import com.kingsrook.qqq.backend.core.utils.StringUtils;
@ -74,12 +73,10 @@ public class QFieldMetaData implements Cloneable
// propose doing that in a secondary field, e.g., "onlyEditableOn=insert|update" // // propose doing that in a secondary field, e.g., "onlyEditableOn=insert|update" //
/////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////
private String displayFormat = "%s"; private String displayFormat = "%s";
private Serializable defaultValue; private Serializable defaultValue;
private String possibleValueSourceName;
private String possibleValueSourceName; private QQueryFilter possibleValueSourceFilter;
private QQueryFilter possibleValueSourceFilter;
private QPossibleValueSource inlinePossibleValueSource;
private Integer maxLength; private Integer maxLength;
private Set<FieldBehavior<?>> behaviors; private Set<FieldBehavior<?>> behaviors;
@ -1061,35 +1058,4 @@ public class QFieldMetaData implements Cloneable
QInstanceHelpContentManager.removeHelpContentByRoleSetFromList(roles, this.helpContents); QInstanceHelpContentManager.removeHelpContentByRoleSetFromList(roles, this.helpContents);
} }
/*******************************************************************************
** Getter for inlinePossibleValueSource
*******************************************************************************/
public QPossibleValueSource getInlinePossibleValueSource()
{
return (this.inlinePossibleValueSource);
}
/*******************************************************************************
** Setter for inlinePossibleValueSource
*******************************************************************************/
public void setInlinePossibleValueSource(QPossibleValueSource inlinePossibleValueSource)
{
this.inlinePossibleValueSource = inlinePossibleValueSource;
}
/*******************************************************************************
** Fluent setter for inlinePossibleValueSource
*******************************************************************************/
public QFieldMetaData withInlinePossibleValueSource(QPossibleValueSource inlinePossibleValueSource)
{
this.inlinePossibleValueSource = inlinePossibleValueSource;
return (this);
}
} }

View File

@ -27,7 +27,6 @@ import java.time.Instant;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalTime; import java.time.LocalTime;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
/******************************************************************************* /*******************************************************************************
@ -101,16 +100,6 @@ public enum QFieldType
/***************************************************************************
**
***************************************************************************/
public String getMixedCaseLabel()
{
return StringUtils.allCapsToMixedCase(name());
}
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/

View File

@ -26,7 +26,6 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData; import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData; import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
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;
@ -46,7 +45,7 @@ public class AppTreeNode
private String label; private String label;
private List<AppTreeNode> children; private List<AppTreeNode> children;
private QIcon icon; private String iconName;
@ -83,7 +82,7 @@ public class AppTreeNode
if(appChildMetaData.getIcon() != null) if(appChildMetaData.getIcon() != null)
{ {
// todo - propagate icons from parents, if they aren't set here... // todo - propagate icons from parents, if they aren't set here...
this.icon = appChildMetaData.getIcon(); this.iconName = appChildMetaData.getIcon().getName();
} }
} }
@ -139,18 +138,7 @@ public class AppTreeNode
*******************************************************************************/ *******************************************************************************/
public String getIconName() public String getIconName()
{ {
return (icon == null ? null : icon.getName()); return iconName;
}
/*******************************************************************************
** Getter for icon
**
*******************************************************************************/
public QIcon getIcon()
{
return icon;
} }

View File

@ -32,7 +32,6 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataOutput; import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataOutput;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData; import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppSection; import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppSection;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QSupplementalAppMetaData; import com.kingsrook.qqq.backend.core.model.metadata.layout.QSupplementalAppMetaData;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
@ -46,7 +45,7 @@ public class QFrontendAppMetaData
{ {
private String name; private String name;
private String label; private String label;
private QIcon icon; private String iconName;
private List<String> widgets = new ArrayList<>(); private List<String> widgets = new ArrayList<>();
private List<AppTreeNode> children = new ArrayList<>(); private List<AppTreeNode> children = new ArrayList<>();
@ -57,7 +56,6 @@ public class QFrontendAppMetaData
private Map<String, QSupplementalAppMetaData> supplementalAppMetaData; private Map<String, QSupplementalAppMetaData> supplementalAppMetaData;
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
@ -65,7 +63,11 @@ public class QFrontendAppMetaData
{ {
this.name = appMetaData.getName(); this.name = appMetaData.getName();
this.label = appMetaData.getLabel(); this.label = appMetaData.getLabel();
this.icon = appMetaData.getIcon();
if(appMetaData.getIcon() != null)
{
this.iconName = appMetaData.getIcon().getName();
}
List<String> filteredWidgets = CollectionUtils.nonNullList(appMetaData.getWidgets()).stream().filter(n -> metaDataOutput.getWidgets().containsKey(n)).toList(); List<String> filteredWidgets = CollectionUtils.nonNullList(appMetaData.getWidgets()).stream().filter(n -> metaDataOutput.getWidgets().containsKey(n)).toList();
if(CollectionUtils.nullSafeHasContents(filteredWidgets)) if(CollectionUtils.nullSafeHasContents(filteredWidgets))
@ -79,10 +81,6 @@ public class QFrontendAppMetaData
List<String> filteredTables = CollectionUtils.nonNullList(section.getTables()).stream().filter(n -> metaDataOutput.getTables().containsKey(n)).toList(); List<String> filteredTables = CollectionUtils.nonNullList(section.getTables()).stream().filter(n -> metaDataOutput.getTables().containsKey(n)).toList();
List<String> filteredProcesses = CollectionUtils.nonNullList(section.getProcesses()).stream().filter(n -> metaDataOutput.getProcesses().containsKey(n)).toList(); List<String> filteredProcesses = CollectionUtils.nonNullList(section.getProcesses()).stream().filter(n -> metaDataOutput.getProcesses().containsKey(n)).toList();
List<String> filteredReports = CollectionUtils.nonNullList(section.getReports()).stream().filter(n -> metaDataOutput.getReports().containsKey(n)).toList(); List<String> filteredReports = CollectionUtils.nonNullList(section.getReports()).stream().filter(n -> metaDataOutput.getReports().containsKey(n)).toList();
//////////////////////////////////////////////////////
// only include the section if it has some contents //
//////////////////////////////////////////////////////
if(!filteredTables.isEmpty() || !filteredProcesses.isEmpty() || !filteredReports.isEmpty()) if(!filteredTables.isEmpty() || !filteredProcesses.isEmpty() || !filteredReports.isEmpty())
{ {
QAppSection clonedSection = section.clone(); QAppSection clonedSection = section.clone();
@ -176,7 +174,18 @@ public class QFrontendAppMetaData
*******************************************************************************/ *******************************************************************************/
public String getIconName() public String getIconName()
{ {
return (icon == null ? null : icon.getName()); return iconName;
}
/*******************************************************************************
** Setter for iconName
**
*******************************************************************************/
public void setIconName(String iconName)
{
this.iconName = iconName;
} }
@ -226,15 +235,4 @@ public class QFrontendAppMetaData
{ {
return supplementalAppMetaData; return supplementalAppMetaData;
} }
/*******************************************************************************
** Getter for icon
**
*******************************************************************************/
public QIcon getIcon()
{
return icon;
}
} }

View File

@ -33,7 +33,6 @@ import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldBehaviorForFron
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.fields.QFieldType; import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.model.metadata.help.QHelpContent; import com.kingsrook.qqq.backend.core.model.metadata.help.QHelpContent;
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
@ -43,7 +42,7 @@ import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
* *
*******************************************************************************/ *******************************************************************************/
@JsonInclude(Include.NON_NULL) @JsonInclude(Include.NON_NULL)
public class QFrontendFieldMetaData implements Serializable public class QFrontendFieldMetaData
{ {
private String name; private String name;
private String label; private String label;
@ -57,7 +56,6 @@ public class QFrontendFieldMetaData implements Serializable
private List<FieldAdornment> adornments; private List<FieldAdornment> adornments;
private List<QHelpContent> helpContents; private List<QHelpContent> helpContents;
private QPossibleValueSource inlinePossibleValueSource;
private List<FieldBehaviorForFrontend> behaviors; private List<FieldBehaviorForFrontend> behaviors;
@ -83,7 +81,6 @@ public class QFrontendFieldMetaData implements Serializable
this.adornments = fieldMetaData.getAdornments(); this.adornments = fieldMetaData.getAdornments();
this.defaultValue = fieldMetaData.getDefaultValue(); this.defaultValue = fieldMetaData.getDefaultValue();
this.helpContents = fieldMetaData.getHelpContents(); this.helpContents = fieldMetaData.getHelpContents();
this.inlinePossibleValueSource = fieldMetaData.getInlinePossibleValueSource();
for(FieldBehavior<?> behavior : CollectionUtils.nonNullCollection(fieldMetaData.getBehaviors())) for(FieldBehavior<?> behavior : CollectionUtils.nonNullCollection(fieldMetaData.getBehaviors()))
{ {
@ -221,17 +218,6 @@ public class QFrontendFieldMetaData implements Serializable
/*******************************************************************************
** Getter for inlinePossibleValueSource
**
*******************************************************************************/
public QPossibleValueSource getInlinePossibleValueSource()
{
return inlinePossibleValueSource;
}
/******************************************************************************* /*******************************************************************************
** Getter for fieldBehaviors ** Getter for fieldBehaviors
** **

View File

@ -29,10 +29,8 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionsHelper; import com.kingsrook.qqq.backend.core.actions.permissions.PermissionsHelper;
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput; import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData; 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.processes.QStateMachineStep;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
@ -49,10 +47,9 @@ public class QFrontendProcessMetaData
private String tableName; private String tableName;
private boolean isHidden; private boolean isHidden;
private QIcon icon; private String iconName;
private List<QFrontendStepMetaData> frontendSteps; private List<QFrontendStepMetaData> frontendSteps;
private String stepFlow;
private boolean hasPermission; private boolean hasPermission;
@ -71,27 +68,15 @@ public class QFrontendProcessMetaData
this.label = processMetaData.getLabel(); this.label = processMetaData.getLabel();
this.tableName = processMetaData.getTableName(); this.tableName = processMetaData.getTableName();
this.isHidden = processMetaData.getIsHidden(); this.isHidden = processMetaData.getIsHidden();
this.stepFlow = processMetaData.getStepFlow().toString();
if(includeSteps) if(includeSteps)
{ {
if(CollectionUtils.nullSafeHasContents(processMetaData.getStepList())) if(CollectionUtils.nullSafeHasContents(processMetaData.getStepList()))
{ {
this.frontendSteps = switch(processMetaData.getStepFlow()) this.frontendSteps = processMetaData.getStepList().stream()
{ .filter(QFrontendStepMetaData.class::isInstance)
case LINEAR -> processMetaData.getStepList().stream() .map(QFrontendStepMetaData.class::cast)
.filter(QFrontendStepMetaData.class::isInstance) .collect(Collectors.toList());
.map(QFrontendStepMetaData.class::cast)
.collect(Collectors.toList());
case STATE_MACHINE -> processMetaData.getAllSteps().values().stream()
.filter(QStateMachineStep.class::isInstance)
.map(QStateMachineStep.class::cast)
.flatMap(step -> step.getSubSteps().stream())
.filter(QFrontendStepMetaData.class::isInstance)
.map(QFrontendStepMetaData.class::cast)
.collect(Collectors.toList());
};
} }
else else
{ {
@ -99,7 +84,10 @@ public class QFrontendProcessMetaData
} }
} }
this.icon = processMetaData.getIcon(); if(processMetaData.getIcon() != null)
{
this.iconName = processMetaData.getIcon().getName();
}
hasPermission = PermissionsHelper.hasProcessPermission(actionInput, name); hasPermission = PermissionsHelper.hasProcessPermission(actionInput, name);
} }
@ -178,7 +166,7 @@ public class QFrontendProcessMetaData
*******************************************************************************/ *******************************************************************************/
public String getIconName() public String getIconName()
{ {
return icon == null ? null : icon.getName(); return iconName;
} }
@ -192,25 +180,4 @@ public class QFrontendProcessMetaData
return hasPermission; return hasPermission;
} }
/*******************************************************************************
** Getter for stepFlow
**
*******************************************************************************/
public String getStepFlow()
{
return stepFlow;
}
/*******************************************************************************
** Getter for icon
**
*******************************************************************************/
public QIcon getIcon()
{
return icon;
}
} }

View File

@ -24,7 +24,7 @@ package com.kingsrook.qqq.backend.core.model.metadata.frontend;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
@ -40,7 +40,6 @@ 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.QFieldMetaData; import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.help.QHelpContent; import com.kingsrook.qqq.backend.core.model.metadata.help.QHelpContent;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
import com.kingsrook.qqq.backend.core.model.metadata.sharing.ShareableTableMetaData; import com.kingsrook.qqq.backend.core.model.metadata.sharing.ShareableTableMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability; import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
import com.kingsrook.qqq.backend.core.model.metadata.tables.ExposedJoin; import com.kingsrook.qqq.backend.core.model.metadata.tables.ExposedJoin;
@ -62,7 +61,7 @@ public class QFrontendTableMetaData
private String label; private String label;
private boolean isHidden; private boolean isHidden;
private String primaryKeyField; private String primaryKeyField;
private QIcon icon; private String iconName;
private Map<String, QFrontendFieldMetaData> fields; private Map<String, QFrontendFieldMetaData> fields;
private List<QFieldSection> sections; private List<QFieldSection> sections;
@ -157,7 +156,10 @@ public class QFrontendTableMetaData
} }
} }
this.icon = tableMetaData.getIcon(); if(tableMetaData.getIcon() != null)
{
this.iconName = tableMetaData.getIcon().getName();
}
setCapabilities(backendForTable, tableMetaData); setCapabilities(backendForTable, tableMetaData);
@ -183,7 +185,7 @@ public class QFrontendTableMetaData
*******************************************************************************/ *******************************************************************************/
private void setCapabilities(QBackendMetaData backend, QTableMetaData table) private void setCapabilities(QBackendMetaData backend, QTableMetaData table)
{ {
Set<Capability> enabledCapabilities = new LinkedHashSet<>(); Set<Capability> enabledCapabilities = new HashSet<>();
for(Capability capability : Capability.values()) for(Capability capability : Capability.values())
{ {
if(table.isCapabilityEnabled(backend, capability)) if(table.isCapabilityEnabled(backend, capability))
@ -273,7 +275,7 @@ public class QFrontendTableMetaData
*******************************************************************************/ *******************************************************************************/
public String getIconName() public String getIconName()
{ {
return (icon == null ? null : icon.getName()); return iconName;
} }
@ -395,16 +397,4 @@ public class QFrontendTableMetaData
{ {
return helpContents; return helpContents;
} }
/*******************************************************************************
** Getter for icon
**
*******************************************************************************/
public QIcon getIcon()
{
return icon;
}
} }

View File

@ -1,72 +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.model.metadata.possiblevalues;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.CriteriaMissingInputValueBehavior;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.FilterUseCase;
/*******************************************************************************
** FilterUseCase implementation for the ways that possible value searches
** are performed, and where we want to have different behaviors for criteria
** that are missing an input value. That is, either for a:
**
** - FORM - e.g., creating a new record, or in a process - where we want a
** missing filter value to basically block you from selecting a value in the
** PVS field - e.g., you must enter some other foreign-key value before choosing
** from this possible value - at least that's the use-case we know of now.
**
** - FILTER - e.g., a query screen - where there isn't really quite the same
** scenario of choosing that foreign-key value first - so, such a PVS should
** list all its values (e.g., a criteria missing an input value should be
** removed from the filter).
*******************************************************************************/
public enum PossibleValueSearchFilterUseCase implements FilterUseCase
{
FORM(CriteriaMissingInputValueBehavior.MAKE_NO_MATCHES),
FILTER(CriteriaMissingInputValueBehavior.REMOVE_FROM_FILTER);
private final CriteriaMissingInputValueBehavior defaultCriteriaMissingInputValueBehavior;
/***************************************************************************
**
***************************************************************************/
PossibleValueSearchFilterUseCase(CriteriaMissingInputValueBehavior defaultCriteriaMissingInputValueBehavior)
{
this.defaultCriteriaMissingInputValueBehavior = defaultCriteriaMissingInputValueBehavior;
}
/*******************************************************************************
** Getter for defaultCriteriaMissingInputValueBehavior
**
*******************************************************************************/
public CriteriaMissingInputValueBehavior getDefaultCriteriaMissingInputValueBehavior()
{
return defaultCriteriaMissingInputValueBehavior;
}
}

View File

@ -1,38 +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.model.metadata.processes;
/*******************************************************************************
** Possible ways the steps of a process can flow.
**
** LINEAR - (the default) - the list of steps in the process are executed in-order
**
** STATE_MACHINE - concept of "states", each which has a backend & frontend step;
** a backend step can (must?) set the field "stepState" (or "nextStepName") to
** say what the next (frontend) step is.
*******************************************************************************/
public enum ProcessStepFlow
{
LINEAR,
STATE_MACHINE
}

View File

@ -29,9 +29,6 @@ public enum QComponentType
{ {
HELP_TEXT, HELP_TEXT,
BULK_EDIT_FORM, BULK_EDIT_FORM,
BULK_LOAD_FILE_MAPPING_FORM,
BULK_LOAD_VALUE_MAPPING_FORM,
BULK_LOAD_PROFILE_FORM,
VALIDATION_REVIEW_SCREEN, VALIDATION_REVIEW_SCREEN,
EDIT_FORM, EDIT_FORM,
VIEW_FORM, VIEW_FORM,

View File

@ -47,9 +47,6 @@ public class QFrontendStepMetaData extends QStepMetaData
private List<QFieldMetaData> recordListFields; private List<QFieldMetaData> recordListFields;
private Map<String, QFieldMetaData> formFieldMap; private Map<String, QFieldMetaData> formFieldMap;
private String format;
private String backStepName;
private List<QHelpContent> helpContents; private List<QHelpContent> helpContents;
@ -406,66 +403,4 @@ public class QFrontendStepMetaData extends QStepMetaData
QInstanceHelpContentManager.removeHelpContentByRoleSetFromList(roles, this.helpContents); QInstanceHelpContentManager.removeHelpContentByRoleSetFromList(roles, this.helpContents);
} }
/*******************************************************************************
** Getter for format
*******************************************************************************/
public String getFormat()
{
return (this.format);
}
/*******************************************************************************
** Setter for format
*******************************************************************************/
public void setFormat(String format)
{
this.format = format;
}
/*******************************************************************************
** Fluent setter for format
*******************************************************************************/
public QFrontendStepMetaData withFormat(String format)
{
this.format = format;
return (this);
}
/*******************************************************************************
** Getter for backStepName
*******************************************************************************/
public String getBackStepName()
{
return (this.backStepName);
}
/*******************************************************************************
** Setter for backStepName
*******************************************************************************/
public void setBackStepName(String backStepName)
{
this.backStepName = backStepName;
}
/*******************************************************************************
** Fluent setter for backStepName
*******************************************************************************/
public QFrontendStepMetaData withBackStepName(String backStepName)
{
this.backStepName = backStepName;
return (this);
}
} }

View File

@ -57,7 +57,6 @@ public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissi
private Integer minInputRecords = null; private Integer minInputRecords = null;
private Integer maxInputRecords = null; private Integer maxInputRecords = null;
private ProcessStepFlow stepFlow = ProcessStepFlow.LINEAR;
private List<QStepMetaData> stepList; // these are the steps that are ran, by-default, in the order they are ran in private List<QStepMetaData> stepList; // these are the steps that are ran, by-default, in the order they are ran in
private Map<String, QStepMetaData> steps; // this is the full map of possible steps private Map<String, QStepMetaData> steps; // this is the full map of possible steps
@ -214,10 +213,11 @@ public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissi
/*************************************************************************** /*******************************************************************************
** add a step to the stepList and map
** **
***************************************************************************/ *******************************************************************************/
public QProcessMetaData withStep(QStepMetaData step) public QProcessMetaData addStep(QStepMetaData step)
{ {
int index = 0; int index = 0;
if(this.stepList != null) if(this.stepList != null)
@ -231,23 +231,11 @@ public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissi
/*******************************************************************************
** add a step to the stepList and map
**
*******************************************************************************/
@Deprecated(since = "withStep was added")
public QProcessMetaData addStep(QStepMetaData step)
{
return (withStep(step));
}
/******************************************************************************* /*******************************************************************************
** add a step to the stepList (at the specified index) and the step map ** add a step to the stepList (at the specified index) and the step map
** **
*******************************************************************************/ *******************************************************************************/
public QProcessMetaData withStep(int index, QStepMetaData step) public QProcessMetaData addStep(int index, QStepMetaData step)
{ {
if(this.stepList == null) if(this.stepList == null)
{ {
@ -272,23 +260,11 @@ public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissi
/*******************************************************************************
** add a step to the stepList (at the specified index) and the step map
**
*******************************************************************************/
@Deprecated(since = "withStep was added")
public QProcessMetaData addStep(int index, QStepMetaData step)
{
return (withStep(index, step));
}
/******************************************************************************* /*******************************************************************************
** add a step ONLY to the step map - NOT the list w/ default execution order. ** add a step ONLY to the step map - NOT the list w/ default execution order.
** **
*******************************************************************************/ *******************************************************************************/
public QProcessMetaData withOptionalStep(QStepMetaData step) public QProcessMetaData addOptionalStep(QStepMetaData step)
{ {
if(this.steps == null) if(this.steps == null)
{ {
@ -307,18 +283,6 @@ public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissi
/*******************************************************************************
** add a step ONLY to the step map - NOT the list w/ default execution order.
**
*******************************************************************************/
@Deprecated(since = "withOptionalStep was added")
public QProcessMetaData addOptionalStep(QStepMetaData step)
{
return (withOptionalStep(step));
}
/******************************************************************************* /*******************************************************************************
** Setter for stepList ** Setter for stepList
** **
@ -335,26 +299,7 @@ public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissi
*******************************************************************************/ *******************************************************************************/
public QStepMetaData getStep(String stepName) public QStepMetaData getStep(String stepName)
{ {
if(steps.containsKey(stepName)) return (steps.get(stepName));
{
return steps.get(stepName);
}
for(QStepMetaData step : steps.values())
{
if(step instanceof QStateMachineStep stateMachineStep)
{
for(QStepMetaData subStep : stateMachineStep.getSubSteps())
{
if(subStep.getName().equals(stepName))
{
return (subStep);
}
}
}
}
return (null);
} }
@ -835,35 +780,4 @@ public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissi
return (this); return (this);
} }
/*******************************************************************************
** Getter for stepFlow
*******************************************************************************/
public ProcessStepFlow getStepFlow()
{
return (this.stepFlow);
}
/*******************************************************************************
** Setter for stepFlow
*******************************************************************************/
public void setStepFlow(ProcessStepFlow stepFlow)
{
this.stepFlow = stepFlow;
}
/*******************************************************************************
** Fluent setter for stepFlow
*******************************************************************************/
public QProcessMetaData withStepFlow(ProcessStepFlow stepFlow)
{
this.stepFlow = stepFlow;
return (this);
}
} }

View File

@ -1,188 +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.model.metadata.processes;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
/*******************************************************************************
** A step for a state-machine flow based Process.
**
** Consists of 1 or 2 sub-steps, which are frontend and/or backend.
*******************************************************************************/
public class QStateMachineStep extends QStepMetaData
{
private List<QStepMetaData> subSteps = new ArrayList<>();
private String defaultNextStepName;
/*******************************************************************************
** Constructor
**
*******************************************************************************/
private QStateMachineStep(List<QStepMetaData> subSteps)
{
setStepType("stateMachine");
this.subSteps.addAll(subSteps);
}
/***************************************************************************
**
***************************************************************************/
public static QStateMachineStep frontendOnly(String name, QFrontendStepMetaData frontendStepMetaData)
{
if(!StringUtils.hasContent(frontendStepMetaData.getName()))
{
frontendStepMetaData.setName(name + ".frontend");
}
return (new QStateMachineStep(List.of(frontendStepMetaData)).withName(name));
}
/***************************************************************************
**
***************************************************************************/
public static QStateMachineStep backendOnly(String name, QBackendStepMetaData backendStepMetaData)
{
if(!StringUtils.hasContent(backendStepMetaData.getName()))
{
backendStepMetaData.setName(name + ".backend");
}
return (new QStateMachineStep(List.of(backendStepMetaData)).withName(name));
}
/***************************************************************************
**
***************************************************************************/
public static QStateMachineStep frontendThenBackend(String name, QFrontendStepMetaData frontendStepMetaData, QBackendStepMetaData backendStepMetaData)
{
if(!StringUtils.hasContent(frontendStepMetaData.getName()))
{
frontendStepMetaData.setName(name + ".frontend");
}
if(!StringUtils.hasContent(backendStepMetaData.getName()))
{
backendStepMetaData.setName(name + ".backend");
}
return (new QStateMachineStep(List.of(frontendStepMetaData, backendStepMetaData)).withName(name));
}
/***************************************************************************
**
***************************************************************************/
@Override
public QStateMachineStep withName(String name)
{
super.withName(name);
return (this);
}
/***************************************************************************
**
***************************************************************************/
@Override
public QStateMachineStep withLabel(String label)
{
super.withLabel(label);
return (this);
}
/*******************************************************************************
** Getter for subSteps
**
*******************************************************************************/
public List<QStepMetaData> getSubSteps()
{
return subSteps;
}
/*******************************************************************************
** Getter for defaultNextStepName
*******************************************************************************/
public String getDefaultNextStepName()
{
return (this.defaultNextStepName);
}
/*******************************************************************************
** Setter for defaultNextStepName
*******************************************************************************/
public void setDefaultNextStepName(String defaultNextStepName)
{
this.defaultNextStepName = defaultNextStepName;
}
/*******************************************************************************
** Fluent setter for defaultNextStepName
*******************************************************************************/
public QStateMachineStep withDefaultNextStepName(String defaultNextStepName)
{
this.defaultNextStepName = defaultNextStepName;
return (this);
}
/*******************************************************************************
** Get a list of all of the input fields used by this step (all of its sub-steps)
*******************************************************************************/
@JsonIgnore
@Override
public List<QFieldMetaData> getInputFields()
{
List<QFieldMetaData> rs = new ArrayList<>();
for(QStepMetaData subStep : subSteps)
{
rs.addAll(subStep.getInputFields());
}
return (rs);
}
}

View File

@ -32,13 +32,6 @@ import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
/******************************************************************************* /*******************************************************************************
** Meta-data definition of a source of data for a report (e.g., a table and query ** Meta-data definition of a source of data for a report (e.g., a table and query
** filter or custom-code reference). ** filter or custom-code reference).
**
** Runs in 3 modes:
**
** - If a customRecordSource is specified, then that code is executed to get the records.
** - else, if a sourceTable is specified, then the corresponding queryFilter
** (optionally along with queryJoins and queryInputCustomizer) is used.
** - else a staticDataSupplier is used.
*******************************************************************************/ *******************************************************************************/
public class QReportDataSource public class QReportDataSource
{ {
@ -51,7 +44,6 @@ public class QReportDataSource
private QCodeReference queryInputCustomizer; private QCodeReference queryInputCustomizer;
private QCodeReference staticDataSupplier; private QCodeReference staticDataSupplier;
private QCodeReference customRecordSource;
@ -273,35 +265,4 @@ public class QReportDataSource
return (this); return (this);
} }
/*******************************************************************************
** Getter for customRecordSource
*******************************************************************************/
public QCodeReference getCustomRecordSource()
{
return (this.customRecordSource);
}
/*******************************************************************************
** Setter for customRecordSource
*******************************************************************************/
public void setCustomRecordSource(QCodeReference customRecordSource)
{
this.customRecordSource = customRecordSource;
}
/*******************************************************************************
** Fluent setter for customRecordSource
*******************************************************************************/
public QReportDataSource withCustomRecordSource(QCodeReference customRecordSource)
{
this.customRecordSource = customRecordSource;
return (this);
}
} }

View File

@ -23,7 +23,7 @@ package com.kingsrook.qqq.backend.core.model.metadata.sharing;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerInterface; import com.kingsrook.qqq.backend.core.model.MetaDataProducerInterface;
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.possiblevalues.QPossibleValueSource; import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
import com.kingsrook.qqq.backend.core.processes.implementations.sharing.ShareScope; import com.kingsrook.qqq.backend.core.processes.implementations.sharing.ShareScope;

View File

@ -1,285 +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.model.savedbulkloadprofiles;
import java.time.Instant;
import com.kingsrook.qqq.backend.core.exceptions.QException;
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.DynamicDefaultValueBehavior;
import com.kingsrook.qqq.backend.core.model.metadata.fields.ValueTooLongBehavior;
import com.kingsrook.qqq.backend.core.model.metadata.tables.TablesPossibleValueSourceMetaDataProvider;
/*******************************************************************************
** Entity bean for the savedBulkLoadProfile table
*******************************************************************************/
public class SavedBulkLoadProfile extends QRecordEntity
{
public static final String TABLE_NAME = "savedBulkLoadProfile";
@QField(isEditable = false)
private Integer id;
@QField(isEditable = false)
private Instant createDate;
@QField(isEditable = false)
private Instant modifyDate;
@QField(isRequired = true, maxLength = 250, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS, label = "Profile Name")
private String label;
@QField(possibleValueSourceName = TablesPossibleValueSourceMetaDataProvider.NAME, maxLength = 250, valueTooLongBehavior = ValueTooLongBehavior.ERROR, label = "Table", isRequired = true)
private String tableName;
@QField(maxLength = 250, valueTooLongBehavior = ValueTooLongBehavior.ERROR, dynamicDefaultValueBehavior = DynamicDefaultValueBehavior.USER_ID, label = "Owner")
private String userId;
@QField(label = "Mapping JSON")
private String mappingJson;
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public SavedBulkLoadProfile()
{
}
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public SavedBulkLoadProfile(QRecord qRecord) throws QException
{
populateFromQRecord(qRecord);
}
/*******************************************************************************
** Getter for id
**
*******************************************************************************/
public Integer getId()
{
return id;
}
/*******************************************************************************
** Setter for id
**
*******************************************************************************/
public void setId(Integer id)
{
this.id = id;
}
/*******************************************************************************
** Getter for createDate
**
*******************************************************************************/
public Instant getCreateDate()
{
return createDate;
}
/*******************************************************************************
** Setter for createDate
**
*******************************************************************************/
public void setCreateDate(Instant createDate)
{
this.createDate = createDate;
}
/*******************************************************************************
** Getter for modifyDate
**
*******************************************************************************/
public Instant getModifyDate()
{
return modifyDate;
}
/*******************************************************************************
** Setter for modifyDate
**
*******************************************************************************/
public void setModifyDate(Instant modifyDate)
{
this.modifyDate = modifyDate;
}
/*******************************************************************************
** Getter for label
**
*******************************************************************************/
public String getLabel()
{
return label;
}
/*******************************************************************************
** Setter for label
**
*******************************************************************************/
public void setLabel(String label)
{
this.label = label;
}
/*******************************************************************************
** Fluent setter for label
**
*******************************************************************************/
public SavedBulkLoadProfile withLabel(String label)
{
this.label = label;
return (this);
}
/*******************************************************************************
** Getter for tableName
**
*******************************************************************************/
public String getTableName()
{
return tableName;
}
/*******************************************************************************
** Setter for tableName
**
*******************************************************************************/
public void setTableName(String tableName)
{
this.tableName = tableName;
}
/*******************************************************************************
** Fluent setter for tableName
**
*******************************************************************************/
public SavedBulkLoadProfile withTableName(String tableName)
{
this.tableName = tableName;
return (this);
}
/*******************************************************************************
** Getter for userId
**
*******************************************************************************/
public String getUserId()
{
return userId;
}
/*******************************************************************************
** Setter for userId
**
*******************************************************************************/
public void setUserId(String userId)
{
this.userId = userId;
}
/*******************************************************************************
** Fluent setter for userId
**
*******************************************************************************/
public SavedBulkLoadProfile withUserId(String userId)
{
this.userId = userId;
return (this);
}
/*******************************************************************************
** Getter for mappingJson
*******************************************************************************/
public String getMappingJson()
{
return (this.mappingJson);
}
/*******************************************************************************
** Setter for mappingJson
*******************************************************************************/
public void setMappingJson(String mappingJson)
{
this.mappingJson = mappingJson;
}
/*******************************************************************************
** Fluent setter for mappingJson
*******************************************************************************/
public SavedBulkLoadProfile withMappingJson(String mappingJson)
{
this.mappingJson = mappingJson;
return (this);
}
}

View File

@ -1,169 +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.model.savedbulkloadprofiles;
import java.util.List;
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.joins.JoinOn;
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinType;
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
import com.kingsrook.qqq.backend.core.model.metadata.sharing.ShareScopePossibleValueMetaDataProducer;
import com.kingsrook.qqq.backend.core.model.metadata.sharing.ShareableAudienceType;
import com.kingsrook.qqq.backend.core.model.metadata.sharing.ShareableTableMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
import com.kingsrook.qqq.backend.core.model.metadata.tables.UniqueKey;
import com.kingsrook.qqq.backend.core.processes.implementations.savedbulkloadprofiles.DeleteSavedBulkLoadProfileProcess;
import com.kingsrook.qqq.backend.core.processes.implementations.savedbulkloadprofiles.QuerySavedBulkLoadProfileProcess;
import com.kingsrook.qqq.backend.core.processes.implementations.savedbulkloadprofiles.StoreSavedBulkLoadProfileProcess;
/*******************************************************************************
**
*******************************************************************************/
public class SavedBulkLoadProfileMetaDataProvider
{
public static final String SHARED_SAVED_BULK_LOAD_PROFILE_JOIN_SAVED_BULK_LOAD_PROFILE = "sharedSavedBulkLoadProfileJoinSavedBulkLoadProfile";
/*******************************************************************************
**
*******************************************************************************/
public void defineAll(QInstance instance, String recordTablesBackendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
{
instance.addTable(defineSavedBulkLoadProfileTable(recordTablesBackendName, backendDetailEnricher));
instance.addPossibleValueSource(QPossibleValueSource.newForTable(SavedBulkLoadProfile.TABLE_NAME));
/////////////////////////////////////
// todo - param to enable sharing? //
/////////////////////////////////////
instance.addTable(defineSharedSavedBulkLoadProfileTable(recordTablesBackendName, backendDetailEnricher));
instance.addJoin(defineSharedSavedBulkLoadProfileJoinSavedBulkLoadProfile());
if(instance.getPossibleValueSource(ShareScopePossibleValueMetaDataProducer.NAME) == null)
{
instance.addPossibleValueSource(new ShareScopePossibleValueMetaDataProducer().produce(new QInstance()));
}
////////////////////////////////////
// processes for working with 'em //
////////////////////////////////////
instance.add(StoreSavedBulkLoadProfileProcess.getProcessMetaData());
instance.add(QuerySavedBulkLoadProfileProcess.getProcessMetaData());
instance.add(DeleteSavedBulkLoadProfileProcess.getProcessMetaData());
}
/*******************************************************************************
**
*******************************************************************************/
private QJoinMetaData defineSharedSavedBulkLoadProfileJoinSavedBulkLoadProfile()
{
return (new QJoinMetaData()
.withName(SHARED_SAVED_BULK_LOAD_PROFILE_JOIN_SAVED_BULK_LOAD_PROFILE)
.withLeftTable(SharedSavedBulkLoadProfile.TABLE_NAME)
.withRightTable(SavedBulkLoadProfile.TABLE_NAME)
.withType(JoinType.MANY_TO_ONE)
.withJoinOn(new JoinOn("savedBulkLoadProfileId", "id")));
}
/*******************************************************************************
**
*******************************************************************************/
public QTableMetaData defineSavedBulkLoadProfileTable(String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
{
QTableMetaData table = new QTableMetaData()
.withName(SavedBulkLoadProfile.TABLE_NAME)
.withLabel("Bulk Load Profile")
.withIcon(new QIcon().withName("drive_folder_upload"))
.withRecordLabelFormat("%s")
.withRecordLabelFields("label")
.withBackendName(backendName)
.withPrimaryKeyField("id")
.withFieldsFromEntity(SavedBulkLoadProfile.class)
.withAuditRules(new QAuditRules().withAuditLevel(AuditLevel.FIELD))
.withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "label", "tableName")))
.withSection(new QFieldSection("data", new QIcon().withName("text_snippet"), Tier.T2, List.of("mappingJson")).withIsHidden(true))
.withSection(new QFieldSection("hidden", new QIcon().withName("text_snippet"), Tier.T2, List.of("userId")).withIsHidden(true))
.withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate")));
// todo - want one of these?
// table.getField("queryFilterJson").withBehavior(SavedReportJsonFieldDisplayValueFormatter.getInstance());
table.withShareableTableMetaData(new ShareableTableMetaData()
.withSharedRecordTableName(SharedSavedBulkLoadProfile.TABLE_NAME)
.withAssetIdFieldName("savedBulkLoadProfileId")
.withScopeFieldName("scope")
.withThisTableOwnerIdFieldName("userId")
.withAudienceType(new ShareableAudienceType().withName("user").withFieldName("userId")));
if(backendDetailEnricher != null)
{
backendDetailEnricher.accept(table);
}
return (table);
}
/*******************************************************************************
**
*******************************************************************************/
public QTableMetaData defineSharedSavedBulkLoadProfileTable(String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
{
QTableMetaData table = new QTableMetaData()
.withName(SharedSavedBulkLoadProfile.TABLE_NAME)
.withLabel("Shared Bulk Load Profile")
.withIcon(new QIcon().withName("share"))
.withRecordLabelFormat("%s")
.withRecordLabelFields("savedBulkLoadProfileId")
.withBackendName(backendName)
.withUniqueKey(new UniqueKey("savedBulkLoadProfileId", "userId"))
.withPrimaryKeyField("id")
.withFieldsFromEntity(SharedSavedBulkLoadProfile.class)
// todo - security key
.withAuditRules(new QAuditRules().withAuditLevel(AuditLevel.FIELD))
.withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "savedBulkLoadProfileId", "userId")))
.withSection(new QFieldSection("data", new QIcon().withName("text_snippet"), Tier.T2, List.of("scope")))
.withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate")));
if(backendDetailEnricher != null)
{
backendDetailEnricher.accept(table);
}
return (table);
}
}

View File

@ -1,268 +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.model.savedbulkloadprofiles;
import java.time.Instant;
import com.kingsrook.qqq.backend.core.exceptions.QException;
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.sharing.ShareScopePossibleValueMetaDataProducer;
/*******************************************************************************
** Entity bean for the shared saved bulk load profile table
*******************************************************************************/
public class SharedSavedBulkLoadProfile extends QRecordEntity
{
public static final String TABLE_NAME = "sharedSavedBulkLoadProfile";
@QField(isEditable = false)
private Integer id;
@QField(isEditable = false)
private Instant createDate;
@QField(isEditable = false)
private Instant modifyDate;
@QField(possibleValueSourceName = SavedBulkLoadProfile.TABLE_NAME, label = "Bulk Load Profile")
private Integer savedBulkLoadProfileId;
@QField(label = "User")
private String userId;
@QField(possibleValueSourceName = ShareScopePossibleValueMetaDataProducer.NAME)
private String scope;
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public SharedSavedBulkLoadProfile()
{
}
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public SharedSavedBulkLoadProfile(QRecord qRecord) throws QException
{
populateFromQRecord(qRecord);
}
/*******************************************************************************
** 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 SharedSavedBulkLoadProfile 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 SharedSavedBulkLoadProfile 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 SharedSavedBulkLoadProfile withModifyDate(Instant modifyDate)
{
this.modifyDate = modifyDate;
return (this);
}
/*******************************************************************************
** Getter for userId
*******************************************************************************/
public String getUserId()
{
return (this.userId);
}
/*******************************************************************************
** Setter for userId
*******************************************************************************/
public void setUserId(String userId)
{
this.userId = userId;
}
/*******************************************************************************
** Fluent setter for userId
*******************************************************************************/
public SharedSavedBulkLoadProfile withUserId(String userId)
{
this.userId = userId;
return (this);
}
/*******************************************************************************
** Getter for scope
*******************************************************************************/
public String getScope()
{
return (this.scope);
}
/*******************************************************************************
** Setter for scope
*******************************************************************************/
public void setScope(String scope)
{
this.scope = scope;
}
/*******************************************************************************
** Fluent setter for scope
*******************************************************************************/
public SharedSavedBulkLoadProfile withScope(String scope)
{
this.scope = scope;
return (this);
}
/*******************************************************************************
** Getter for savedBulkLoadProfileId
*******************************************************************************/
public Integer getSavedBulkLoadProfileId()
{
return (this.savedBulkLoadProfileId);
}
/*******************************************************************************
** Setter for savedBulkLoadProfileId
*******************************************************************************/
public void setSavedBulkLoadProfileId(Integer savedBulkLoadProfileId)
{
this.savedBulkLoadProfileId = savedBulkLoadProfileId;
}
/*******************************************************************************
** Fluent setter for savedBulkLoadProfileId
*******************************************************************************/
public SharedSavedBulkLoadProfile withSavedBulkLoadProfileId(Integer savedBulkLoadProfileId)
{
this.savedBulkLoadProfileId = savedBulkLoadProfileId;
return (this);
}
}

View File

@ -26,13 +26,13 @@ import java.io.Serializable;
import java.util.List; import java.util.List;
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;
import com.kingsrook.qqq.backend.core.model.MetaDataProducerInterface;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportFormatPossibleValueEnum; import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportFormatPossibleValueEnum;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
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.MetaDataProducerInterface;
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.fields.QFieldType; import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
@ -114,7 +114,7 @@ public class ScheduledReportSyncToScheduledJobProcess extends AbstractTableSyncT
scheduledJob = new ScheduledJob(); scheduledJob = new ScheduledJob();
scheduledJob.setLabel("Scheduled Report " + scheduledReport.getId()); scheduledJob.setLabel("Scheduled Report " + scheduledReport.getId());
scheduledJob.setDescription("Job to run Scheduled Report Id " + scheduledReport.getId() scheduledJob.setDescription("Job to run Scheduled Report Id " + scheduledReport.getId()
+ " (which runs Report Id " + scheduledReport.getSavedReportId() + ")"); + " (which runs Report Id " + scheduledReport.getSavedReportId() + ")");
scheduledJob.setSchedulerName(runBackendStepInput.getValueString(SCHEDULER_NAME_FIELD_NAME)); scheduledJob.setSchedulerName(runBackendStepInput.getValueString(SCHEDULER_NAME_FIELD_NAME));
scheduledJob.setType(ScheduledJobType.PROCESS.name()); scheduledJob.setType(ScheduledJobType.PROCESS.name());
scheduledJob.setForeignKeyType(getScheduledJobForeignKeyType()); scheduledJob.setForeignKeyType(getScheduledJobForeignKeyType());

View File

@ -48,7 +48,7 @@ public class QSession implements Serializable, Cloneable
private QUser user; private QUser user;
private String uuid; private String uuid;
private Set<String> permissions; private Set<String> permissions;
private Map<String, List<Serializable>> securityKeyValues; private Map<String, List<Serializable>> securityKeyValues;
private Map<String, Serializable> backendVariants; private Map<String, Serializable> backendVariants;
@ -360,38 +360,12 @@ public class QSession implements Serializable, Cloneable
return (false); return (false);
} }
List<Serializable> values = securityKeyValues.get(keyName); List<Serializable> values = securityKeyValues.get(keyName);
Serializable valueAsType = ValueUtils.getValueAsFieldType(fieldType, value);
Serializable valueAsType;
try
{
valueAsType = ValueUtils.getValueAsFieldType(fieldType, value);
}
catch(Exception e)
{
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// an exception in getValueAsFieldType would indicate, e.g., a non-number string trying to come back as integer. //
// so - assume that any such mismatch means the value isn't in the session. //
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
return (false);
}
for(Serializable keyValue : values) for(Serializable keyValue : values)
{ {
Serializable keyValueAsType = null; Serializable keyValueAsType = ValueUtils.getValueAsFieldType(fieldType, keyValue);
try if(keyValueAsType.equals(valueAsType))
{
keyValueAsType = ValueUtils.getValueAsFieldType(fieldType, keyValue);
}
catch(Exception e)
{
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// an exception in getValueAsFieldType would indicate, e.g., a non-number string trying to come back as integer. //
// so - assume that any such mismatch means this key isn't a match.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}
if(valueAsType.equals(keyValueAsType))
{ {
return (true); return (true);
} }
@ -587,7 +561,6 @@ public class QSession implements Serializable, Cloneable
} }
/******************************************************************************* /*******************************************************************************
** Getter for valuesForFrontend ** Getter for valuesForFrontend
*******************************************************************************/ *******************************************************************************/
@ -618,7 +591,6 @@ public class QSession implements Serializable, Cloneable
} }
/******************************************************************************* /*******************************************************************************
** Fluent setter for a single valuesForFrontend ** Fluent setter for a single valuesForFrontend
*******************************************************************************/ *******************************************************************************/
@ -632,4 +604,5 @@ public class QSession implements Serializable, Cloneable
return (this); return (this);
} }
} }

View File

@ -247,7 +247,10 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////
// allow customizer to do custom things here, if so desired // // allow customizer to do custom things here, if so desired //
////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////
finalCustomizeSession(qInstance, qSession); if(getCustomizer() != null)
{
getCustomizer().finalCustomizeSession(qInstance, qSession);
}
return (qSession); return (qSession);
} }
@ -308,7 +311,10 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////
// allow customizer to do custom things here, if so desired // // allow customizer to do custom things here, if so desired //
////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////
finalCustomizeSession(qInstance, qSession); if(getCustomizer() != null)
{
getCustomizer().finalCustomizeSession(qInstance, qSession);
}
return (qSession); return (qSession);
} }
@ -354,23 +360,6 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
/***************************************************************************
**
***************************************************************************/
private void finalCustomizeSession(QInstance qInstance, QSession qSession)
{
if(getCustomizer() != null)
{
QContext.withTemporaryContext(QContext.capture(), () ->
{
QContext.setQSession(getChickenAndEggSession());
getCustomizer().finalCustomizeSession(qInstance, qSession);
});
}
}
/******************************************************************************* /*******************************************************************************
** Insert a session as a new record into userSession table ** Insert a session as a new record into userSession table
*******************************************************************************/ *******************************************************************************/

Some files were not shown because too many files have changed in this diff Show More