mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-22 23:18:45 +00:00
Compare commits
2 Commits
snapshot-f
...
snapshot-f
Author | SHA1 | Date | |
---|---|---|---|
99a786e4ab | |||
65ab6bfa87 |
@ -213,6 +213,18 @@
|
|||||||
<property name="tokens" value="VARIABLE_DEF"/>
|
<property name="tokens" value="VARIABLE_DEF"/>
|
||||||
<property name="allowSamelineMultipleAnnotations" value="true"/>
|
<property name="allowSamelineMultipleAnnotations" value="true"/>
|
||||||
</module>
|
</module>
|
||||||
|
<module name="NonEmptyAtclauseDescription"/>
|
||||||
|
<!-- <module name="JavadocTagContinuationIndentation"/> -->
|
||||||
|
<!--
|
||||||
|
<module name="SummaryJavadoc">
|
||||||
|
<property name="forbiddenSummaryFragments" value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
|
||||||
|
</module>
|
||||||
|
-->
|
||||||
|
<!-- <module name="JavadocParagraph"/> -->
|
||||||
|
<module name="AtclauseOrder">
|
||||||
|
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
|
||||||
|
<property name="target" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
|
||||||
|
</module>
|
||||||
<module name="JavadocMethod">
|
<module name="JavadocMethod">
|
||||||
<property name="allowMissingParamTags" value="true"/>
|
<property name="allowMissingParamTags" value="true"/>
|
||||||
<property name="allowMissingReturnTag" value="true"/>
|
<property name="allowMissingReturnTag" value="true"/>
|
||||||
@ -221,14 +233,23 @@
|
|||||||
<module name="MissingJavadocMethod">
|
<module name="MissingJavadocMethod">
|
||||||
<property name="scope" value="private"/>
|
<property name="scope" value="private"/>
|
||||||
</module>
|
</module>
|
||||||
<module name="MissingJavadocType">
|
|
||||||
<property name="scope" value="private"/>
|
|
||||||
</module>
|
|
||||||
<module name="MethodName">
|
<module name="MethodName">
|
||||||
<property name="format" value="^[a-z][a-zA-Z0-9_]*$"/>
|
<property name="format" value="^[a-z][a-zA-Z0-9_]*$"/>
|
||||||
<message key="name.invalidPattern"
|
<message key="name.invalidPattern"
|
||||||
value="Method name ''{0}'' must match pattern ''{1}''."/>
|
value="Method name ''{0}'' must match pattern ''{1}''."/>
|
||||||
</module>
|
</module>
|
||||||
|
<module name="SingleLineJavadoc">
|
||||||
|
<property name="ignoreInlineTags" value="false"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="MagicNumber">
|
||||||
|
<property name="severity" value="info"/>
|
||||||
|
<property name="tokens" value="NUM_DOUBLE, NUM_FLOAT, NUM_INT"/>
|
||||||
|
<property name="ignoreNumbers" value="0, 1, 2, 3, 4, 5, 6, 7, 8"/>
|
||||||
|
<property name="ignoreFieldDeclaration" value="true"/>
|
||||||
|
<property name="ignoreAnnotation" value="true"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
<module name="EmptyCatchBlock">
|
<module name="EmptyCatchBlock">
|
||||||
<property name="exceptionVariableName" value="expected"/>
|
<property name="exceptionVariableName" value="expected"/>
|
||||||
</module>
|
</module>
|
||||||
|
@ -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]
|
||||||
----
|
----
|
||||||
|
6
pom.xml
6
pom.xml
@ -46,11 +46,12 @@
|
|||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<revision>0.23.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>
|
||||||
<maven.compiler.release>17</maven.compiler.release>
|
<maven.compiler.source>17</maven.compiler.source>
|
||||||
|
<maven.compiler.target>17</maven.compiler.target>
|
||||||
<maven.compiler.showDeprecation>true</maven.compiler.showDeprecation>
|
<maven.compiler.showDeprecation>true</maven.compiler.showDeprecation>
|
||||||
<maven.compiler.showWarnings>true</maven.compiler.showWarnings>
|
<maven.compiler.showWarnings>true</maven.compiler.showWarnings>
|
||||||
<coverage.haltOnFailure>true</coverage.haltOnFailure>
|
<coverage.haltOnFailure>true</coverage.haltOnFailure>
|
||||||
@ -167,7 +168,6 @@
|
|||||||
<violationSeverity>warning</violationSeverity>
|
<violationSeverity>warning</violationSeverity>
|
||||||
<excludes>**/target/generated-sources/*.*</excludes>
|
<excludes>**/target/generated-sources/*.*</excludes>
|
||||||
<!-- <linkXRef>false</linkXRef> -->
|
<!-- <linkXRef>false</linkXRef> -->
|
||||||
<includeTestSourceDirectory>true</includeTestSourceDirectory>
|
|
||||||
</configuration>
|
</configuration>
|
||||||
<goals>
|
<goals>
|
||||||
<goal>check</goal>
|
<goal>check</goal>
|
||||||
|
@ -37,7 +37,7 @@ import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
|||||||
**
|
**
|
||||||
** Note: One would imagine that this class shouldn't ever implement Serializable...
|
** Note: One would imagine that this class shouldn't ever implement Serializable...
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class QBackendTransaction implements AutoCloseable
|
public class QBackendTransaction
|
||||||
{
|
{
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
@ -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...");
|
||||||
|
@ -344,9 +344,6 @@ public class RecordAutomationStatusUpdater
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
**
|
|
||||||
***************************************************************************/
|
|
||||||
private record Key(QTableMetaData table, TriggerEvent triggerEvent) {}
|
private record Key(QTableMetaData table, TriggerEvent triggerEvent) {}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -55,10 +55,10 @@ public abstract class AbstractPreInsertCustomizer implements TableCustomizerInte
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
/////////////////////////////////////////////////////////////////////////////////
|
||||||
** allow the customizer to specify when it should be executed as part of the
|
// allow the customizer to specify when it should be executed as part of the //
|
||||||
** insert action. default (per method in this class) is AFTER_ALL_VALIDATIONS
|
// insert action. default (per method in this class) is AFTER_ALL_VALIDATIONS //
|
||||||
***************************************************************************/
|
/////////////////////////////////////////////////////////////////////////////////
|
||||||
public enum WhenToRun
|
public enum WhenToRun
|
||||||
{
|
{
|
||||||
BEFORE_ALL_VALIDATIONS,
|
BEFORE_ALL_VALIDATIONS,
|
||||||
|
@ -28,7 +28,6 @@ import java.util.Iterator;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
||||||
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.exceptions.QRuntimeException;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||||
@ -50,9 +49,6 @@ import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public abstract class ChildInserterPostInsertCustomizer extends AbstractPostInsertCustomizer
|
public abstract class ChildInserterPostInsertCustomizer extends AbstractPostInsertCustomizer
|
||||||
{
|
{
|
||||||
/***************************************************************************
|
|
||||||
**
|
|
||||||
***************************************************************************/
|
|
||||||
public enum RelationshipType
|
public enum RelationshipType
|
||||||
{
|
{
|
||||||
PARENT_POINTS_AT_CHILD,
|
PARENT_POINTS_AT_CHILD,
|
||||||
@ -101,7 +97,7 @@ public abstract class ChildInserterPostInsertCustomizer extends AbstractPostInse
|
|||||||
List<QRecord> rs = records;
|
List<QRecord> rs = records;
|
||||||
List<QRecord> childrenToInsert = new ArrayList<>();
|
List<QRecord> childrenToInsert = new ArrayList<>();
|
||||||
QTableMetaData table = getInsertInput().getTable();
|
QTableMetaData table = getInsertInput().getTable();
|
||||||
QTableMetaData childTable = QContext.getQInstance().getTable(getChildTableName());
|
QTableMetaData childTable = getInsertInput().getInstance().getTable(getChildTableName());
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// iterate over the inserted records, building a list child records to insert //
|
// iterate over the inserted records, building a list child records to insert //
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,6 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter;
|
import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter;
|
||||||
import com.kingsrook.qqq.backend.core.actions.values.SearchPossibleValueSourceAction;
|
import com.kingsrook.qqq.backend.core.actions.values.SearchPossibleValueSourceAction;
|
||||||
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.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;
|
||||||
@ -103,7 +102,7 @@ public abstract class AbstractWidgetRenderer
|
|||||||
String possibleValueSourceName = dropdownData.getPossibleValueSourceName();
|
String possibleValueSourceName = dropdownData.getPossibleValueSourceName();
|
||||||
if(possibleValueSourceName != null)
|
if(possibleValueSourceName != null)
|
||||||
{
|
{
|
||||||
QPossibleValueSource possibleValueSource = QContext.getQInstance().getPossibleValueSource(possibleValueSourceName);
|
QPossibleValueSource possibleValueSource = input.getInstance().getPossibleValueSource(possibleValueSourceName);
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// this looks complicated, but is just look for a label in the dropdown data and if found use it, //
|
// this looks complicated, but is just look for a label in the dropdown data and if found use it, //
|
||||||
|
@ -34,7 +34,6 @@ import com.kingsrook.qqq.backend.core.actions.tables.CountAction;
|
|||||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||||
import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter;
|
import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter;
|
||||||
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.QNotFoundException;
|
import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException;
|
||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
@ -182,10 +181,10 @@ public class ChildRecordListRenderer extends AbstractWidgetRenderer
|
|||||||
{
|
{
|
||||||
String widgetLabel = input.getQueryParams().get("widgetLabel");
|
String widgetLabel = input.getQueryParams().get("widgetLabel");
|
||||||
String joinName = input.getQueryParams().get("joinName");
|
String joinName = input.getQueryParams().get("joinName");
|
||||||
QJoinMetaData join = QContext.getQInstance().getJoin(joinName);
|
QJoinMetaData join = input.getInstance().getJoin(joinName);
|
||||||
String id = input.getQueryParams().get("id");
|
String id = input.getQueryParams().get("id");
|
||||||
QTableMetaData leftTable = QContext.getQInstance().getTable(join.getLeftTable());
|
QTableMetaData leftTable = input.getInstance().getTable(join.getLeftTable());
|
||||||
QTableMetaData rightTable = QContext.getQInstance().getTable(join.getRightTable());
|
QTableMetaData rightTable = input.getInstance().getTable(join.getRightTable());
|
||||||
|
|
||||||
Integer maxRows = null;
|
Integer maxRows = null;
|
||||||
if(StringUtils.hasContent(input.getQueryParams().get("maxRows")))
|
if(StringUtils.hasContent(input.getQueryParams().get("maxRows")))
|
||||||
@ -253,7 +252,7 @@ public class ChildRecordListRenderer extends AbstractWidgetRenderer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String tablePath = QContext.getQInstance().getTablePath(rightTable.getName());
|
String tablePath = input.getInstance().getTablePath(rightTable.getName());
|
||||||
String viewAllLink = tablePath == null ? null : (tablePath + "?filter=" + URLEncoder.encode(JsonUtils.toJson(filter), Charset.defaultCharset()));
|
String viewAllLink = tablePath == null ? null : (tablePath + "?filter=" + URLEncoder.encode(JsonUtils.toJson(filter), Charset.defaultCharset()));
|
||||||
|
|
||||||
ChildRecordListData widgetData = new ChildRecordListData(widgetLabel, queryOutput, rightTable, tablePath, viewAllLink, totalRows);
|
ChildRecordListData widgetData = new ChildRecordListData(widgetLabel, queryOutput, rightTable, tablePath, viewAllLink, totalRows);
|
||||||
@ -279,9 +278,7 @@ public class ChildRecordListRenderer extends AbstractWidgetRenderer
|
|||||||
Map<String, Serializable> widgetValues = input.getWidgetMetaData().getDefaultValues();
|
Map<String, Serializable> widgetValues = input.getWidgetMetaData().getDefaultValues();
|
||||||
if(widgetValues.containsKey("disabledFieldsForNewChildRecords"))
|
if(widgetValues.containsKey("disabledFieldsForNewChildRecords"))
|
||||||
{
|
{
|
||||||
@SuppressWarnings("unchecked")
|
widgetData.setDisabledFieldsForNewChildRecords((Set<String>) widgetValues.get("disabledFieldsForNewChildRecords"));
|
||||||
Set<String> disabledFieldsForNewChildRecords = (Set<String>) widgetValues.get("disabledFieldsForNewChildRecords");
|
|
||||||
widgetData.setDisabledFieldsForNewChildRecords(disabledFieldsForNewChildRecords);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -24,7 +24,6 @@ package com.kingsrook.qqq.backend.core.actions.dashboard.widgets;
|
|||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||||
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.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;
|
||||||
@ -58,7 +57,7 @@ public class ProcessWidgetRenderer extends AbstractWidgetRenderer
|
|||||||
setupDropdowns(input, widgetMetaData, data);
|
setupDropdowns(input, widgetMetaData, data);
|
||||||
|
|
||||||
String processName = (String) widgetMetaData.getDefaultValues().get(WIDGET_PROCESS_NAME);
|
String processName = (String) widgetMetaData.getDefaultValues().get(WIDGET_PROCESS_NAME);
|
||||||
QProcessMetaData processMetaData = QContext.getQInstance().getProcess(processName);
|
QProcessMetaData processMetaData = input.getInstance().getProcess(processName);
|
||||||
data.setProcessMetaData(processMetaData);
|
data.setProcessMetaData(processMetaData);
|
||||||
|
|
||||||
data.setDefaultValues(new HashMap<>(input.getQueryParams()));
|
data.setDefaultValues(new HashMap<>(input.getQueryParams()));
|
||||||
|
@ -30,7 +30,6 @@ 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.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.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
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;
|
||||||
@ -73,7 +72,7 @@ public class MetaDataAction
|
|||||||
// map tables to frontend metadata //
|
// map tables to frontend metadata //
|
||||||
/////////////////////////////////////
|
/////////////////////////////////////
|
||||||
Map<String, QFrontendTableMetaData> tables = new LinkedHashMap<>();
|
Map<String, QFrontendTableMetaData> tables = new LinkedHashMap<>();
|
||||||
for(Map.Entry<String, QTableMetaData> entry : QContext.getQInstance().getTables().entrySet())
|
for(Map.Entry<String, QTableMetaData> entry : metaDataInput.getInstance().getTables().entrySet())
|
||||||
{
|
{
|
||||||
String tableName = entry.getKey();
|
String tableName = entry.getKey();
|
||||||
QTableMetaData table = entry.getValue();
|
QTableMetaData table = entry.getValue();
|
||||||
@ -84,7 +83,7 @@ public class MetaDataAction
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
QBackendMetaData backendForTable = QContext.getQInstance().getBackendForTable(tableName);
|
QBackendMetaData backendForTable = metaDataInput.getInstance().getBackendForTable(tableName);
|
||||||
tables.put(tableName, new QFrontendTableMetaData(metaDataInput, backendForTable, table, false, false));
|
tables.put(tableName, new QFrontendTableMetaData(metaDataInput, backendForTable, table, false, false));
|
||||||
treeNodes.put(tableName, new AppTreeNode(table));
|
treeNodes.put(tableName, new AppTreeNode(table));
|
||||||
}
|
}
|
||||||
@ -97,7 +96,7 @@ public class MetaDataAction
|
|||||||
// map processes to frontend metadata //
|
// map processes to frontend metadata //
|
||||||
////////////////////////////////////////
|
////////////////////////////////////////
|
||||||
Map<String, QFrontendProcessMetaData> processes = new LinkedHashMap<>();
|
Map<String, QFrontendProcessMetaData> processes = new LinkedHashMap<>();
|
||||||
for(Map.Entry<String, QProcessMetaData> entry : QContext.getQInstance().getProcesses().entrySet())
|
for(Map.Entry<String, QProcessMetaData> entry : metaDataInput.getInstance().getProcesses().entrySet())
|
||||||
{
|
{
|
||||||
String processName = entry.getKey();
|
String processName = entry.getKey();
|
||||||
QProcessMetaData process = entry.getValue();
|
QProcessMetaData process = entry.getValue();
|
||||||
@ -117,7 +116,7 @@ public class MetaDataAction
|
|||||||
// map reports to frontend metadata //
|
// map reports to frontend metadata //
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
Map<String, QFrontendReportMetaData> reports = new LinkedHashMap<>();
|
Map<String, QFrontendReportMetaData> reports = new LinkedHashMap<>();
|
||||||
for(Map.Entry<String, QReportMetaData> entry : QContext.getQInstance().getReports().entrySet())
|
for(Map.Entry<String, QReportMetaData> entry : metaDataInput.getInstance().getReports().entrySet())
|
||||||
{
|
{
|
||||||
String reportName = entry.getKey();
|
String reportName = entry.getKey();
|
||||||
QReportMetaData report = entry.getValue();
|
QReportMetaData report = entry.getValue();
|
||||||
@ -137,7 +136,7 @@ public class MetaDataAction
|
|||||||
// map widgets to frontend metadata //
|
// map widgets to frontend metadata //
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
Map<String, QFrontendWidgetMetaData> widgets = new LinkedHashMap<>();
|
Map<String, QFrontendWidgetMetaData> widgets = new LinkedHashMap<>();
|
||||||
for(Map.Entry<String, QWidgetMetaDataInterface> entry : QContext.getQInstance().getWidgets().entrySet())
|
for(Map.Entry<String, QWidgetMetaDataInterface> entry : metaDataInput.getInstance().getWidgets().entrySet())
|
||||||
{
|
{
|
||||||
String widgetName = entry.getKey();
|
String widgetName = entry.getKey();
|
||||||
QWidgetMetaDataInterface widget = entry.getValue();
|
QWidgetMetaDataInterface widget = entry.getValue();
|
||||||
@ -155,7 +154,7 @@ public class MetaDataAction
|
|||||||
///////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////
|
||||||
// sort apps - by sortOrder (integer), then by label //
|
// sort apps - by sortOrder (integer), then by label //
|
||||||
///////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////
|
||||||
List<QAppMetaData> sortedApps = QContext.getQInstance().getApps().values().stream()
|
List<QAppMetaData> sortedApps = metaDataInput.getInstance().getApps().values().stream()
|
||||||
.sorted(Comparator.comparing((QAppMetaData a) -> a.getSortOrder())
|
.sorted(Comparator.comparing((QAppMetaData a) -> a.getSortOrder())
|
||||||
.thenComparing((QAppMetaData a) -> a.getLabel()))
|
.thenComparing((QAppMetaData a) -> a.getLabel()))
|
||||||
.toList();
|
.toList();
|
||||||
@ -212,14 +211,14 @@ public class MetaDataAction
|
|||||||
////////////////////////////////////
|
////////////////////////////////////
|
||||||
// add branding metadata if found //
|
// add branding metadata if found //
|
||||||
////////////////////////////////////
|
////////////////////////////////////
|
||||||
if(QContext.getQInstance().getBranding() != null)
|
if(metaDataInput.getInstance().getBranding() != null)
|
||||||
{
|
{
|
||||||
metaDataOutput.setBranding(QContext.getQInstance().getBranding());
|
metaDataOutput.setBranding(metaDataInput.getInstance().getBranding());
|
||||||
}
|
}
|
||||||
|
|
||||||
metaDataOutput.setEnvironmentValues(QContext.getQInstance().getEnvironmentValues());
|
metaDataOutput.setEnvironmentValues(metaDataInput.getInstance().getEnvironmentValues());
|
||||||
|
|
||||||
metaDataOutput.setHelpContents(QContext.getQInstance().getHelpContent());
|
metaDataOutput.setHelpContents(metaDataInput.getInstance().getHelpContent());
|
||||||
|
|
||||||
// todo post-customization - can do whatever w/ the result if you want?
|
// todo post-customization - can do whatever w/ the result if you want?
|
||||||
|
|
||||||
|
@ -23,7 +23,6 @@ package com.kingsrook.qqq.backend.core.actions.metadata;
|
|||||||
|
|
||||||
|
|
||||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||||
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.QNotFoundException;
|
import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.metadata.ProcessMetaDataInput;
|
import com.kingsrook.qqq.backend.core.model.actions.metadata.ProcessMetaDataInput;
|
||||||
@ -48,7 +47,7 @@ public class ProcessMetaDataAction
|
|||||||
// todo pre-customization - just get to modify the request?
|
// todo pre-customization - just get to modify the request?
|
||||||
ProcessMetaDataOutput processMetaDataOutput = new ProcessMetaDataOutput();
|
ProcessMetaDataOutput processMetaDataOutput = new ProcessMetaDataOutput();
|
||||||
|
|
||||||
QProcessMetaData process = QContext.getQInstance().getProcess(processMetaDataInput.getProcessName());
|
QProcessMetaData process = processMetaDataInput.getInstance().getProcess(processMetaDataInput.getProcessName());
|
||||||
if(process == null)
|
if(process == null)
|
||||||
{
|
{
|
||||||
throw (new QNotFoundException("Process [" + processMetaDataInput.getProcessName() + "] was not found."));
|
throw (new QNotFoundException("Process [" + processMetaDataInput.getProcessName() + "] was not found."));
|
||||||
|
@ -23,7 +23,6 @@ package com.kingsrook.qqq.backend.core.actions.metadata;
|
|||||||
|
|
||||||
|
|
||||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||||
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.QNotFoundException;
|
import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.metadata.TableMetaDataInput;
|
import com.kingsrook.qqq.backend.core.model.actions.metadata.TableMetaDataInput;
|
||||||
@ -49,12 +48,12 @@ public class TableMetaDataAction
|
|||||||
// todo pre-customization - just get to modify the request?
|
// todo pre-customization - just get to modify the request?
|
||||||
TableMetaDataOutput tableMetaDataOutput = new TableMetaDataOutput();
|
TableMetaDataOutput tableMetaDataOutput = new TableMetaDataOutput();
|
||||||
|
|
||||||
QTableMetaData table = QContext.getQInstance().getTable(tableMetaDataInput.getTableName());
|
QTableMetaData table = tableMetaDataInput.getInstance().getTable(tableMetaDataInput.getTableName());
|
||||||
if(table == null)
|
if(table == null)
|
||||||
{
|
{
|
||||||
throw (new QNotFoundException("Table [" + tableMetaDataInput.getTableName() + "] was not found."));
|
throw (new QNotFoundException("Table [" + tableMetaDataInput.getTableName() + "] was not found."));
|
||||||
}
|
}
|
||||||
QBackendMetaData backendForTable = QContext.getQInstance().getBackendForTable(table.getName());
|
QBackendMetaData backendForTable = tableMetaDataInput.getInstance().getBackendForTable(table.getName());
|
||||||
tableMetaDataOutput.setTable(new QFrontendTableMetaData(tableMetaDataInput, backendForTable, table, true, true));
|
tableMetaDataOutput.setTable(new QFrontendTableMetaData(tableMetaDataInput, backendForTable, table, true, true));
|
||||||
|
|
||||||
// todo post-customization - can do whatever w/ the result if you want
|
// todo post-customization - can do whatever w/ the result if you want
|
||||||
|
@ -49,7 +49,6 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -79,12 +78,6 @@ public class PermissionsHelper
|
|||||||
warnAboutPermissionSubTypeForTables(permissionSubType);
|
warnAboutPermissionSubTypeForTables(permissionSubType);
|
||||||
QTableMetaData table = QContext.getQInstance().getTable(tableName);
|
QTableMetaData table = QContext.getQInstance().getTable(tableName);
|
||||||
|
|
||||||
if(table == null)
|
|
||||||
{
|
|
||||||
LOG.info("Throwing a permission denied exception in response to a non-existent table name", logPair("tableName", tableName));
|
|
||||||
throw (new QPermissionDeniedException("Permission denied."));
|
|
||||||
}
|
|
||||||
|
|
||||||
commonCheckPermissionThrowing(getEffectivePermissionRules(table, QContext.getQInstance()), permissionSubType, table.getName());
|
commonCheckPermissionThrowing(getEffectivePermissionRules(table, QContext.getQInstance()), permissionSubType, table.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,14 +184,7 @@ public class PermissionsHelper
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public static void checkProcessPermissionThrowing(AbstractActionInput actionInput, String processName, Map<String, Serializable> processValues) throws QPermissionDeniedException
|
public static void checkProcessPermissionThrowing(AbstractActionInput actionInput, String processName, Map<String, Serializable> processValues) throws QPermissionDeniedException
|
||||||
{
|
{
|
||||||
QProcessMetaData process = QContext.getQInstance().getProcess(processName);
|
QProcessMetaData process = QContext.getQInstance().getProcess(processName);
|
||||||
|
|
||||||
if(process == null)
|
|
||||||
{
|
|
||||||
LOG.info("Throwing a permission denied exception in response to a non-existent process name", logPair("processName", processName));
|
|
||||||
throw (new QPermissionDeniedException("Permission denied."));
|
|
||||||
}
|
|
||||||
|
|
||||||
QPermissionRules effectivePermissionRules = getEffectivePermissionRules(process, QContext.getQInstance());
|
QPermissionRules effectivePermissionRules = getEffectivePermissionRules(process, QContext.getQInstance());
|
||||||
|
|
||||||
if(effectivePermissionRules.getCustomPermissionChecker() != null)
|
if(effectivePermissionRules.getCustomPermissionChecker() != null)
|
||||||
@ -240,13 +226,6 @@ public class PermissionsHelper
|
|||||||
public static void checkAppPermissionThrowing(AbstractActionInput actionInput, String appName) throws QPermissionDeniedException
|
public static void checkAppPermissionThrowing(AbstractActionInput actionInput, String appName) throws QPermissionDeniedException
|
||||||
{
|
{
|
||||||
QAppMetaData app = QContext.getQInstance().getApp(appName);
|
QAppMetaData app = QContext.getQInstance().getApp(appName);
|
||||||
|
|
||||||
if(app == null)
|
|
||||||
{
|
|
||||||
LOG.info("Throwing a permission denied exception in response to a non-existent app name", logPair("appName", appName));
|
|
||||||
throw (new QPermissionDeniedException("Permission denied."));
|
|
||||||
}
|
|
||||||
|
|
||||||
commonCheckPermissionThrowing(getEffectivePermissionRules(app, QContext.getQInstance()), PrivatePermissionSubType.HAS_ACCESS, app.getName());
|
commonCheckPermissionThrowing(getEffectivePermissionRules(app, QContext.getQInstance()), PrivatePermissionSubType.HAS_ACCESS, app.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,13 +255,6 @@ public class PermissionsHelper
|
|||||||
public static void checkReportPermissionThrowing(AbstractActionInput actionInput, String reportName) throws QPermissionDeniedException
|
public static void checkReportPermissionThrowing(AbstractActionInput actionInput, String reportName) throws QPermissionDeniedException
|
||||||
{
|
{
|
||||||
QReportMetaData report = QContext.getQInstance().getReport(reportName);
|
QReportMetaData report = QContext.getQInstance().getReport(reportName);
|
||||||
|
|
||||||
if(report == null)
|
|
||||||
{
|
|
||||||
LOG.info("Throwing a permission denied exception in response to a non-existent process name", logPair("reportName", reportName));
|
|
||||||
throw (new QPermissionDeniedException("Permission denied."));
|
|
||||||
}
|
|
||||||
|
|
||||||
commonCheckPermissionThrowing(getEffectivePermissionRules(report, QContext.getQInstance()), PrivatePermissionSubType.HAS_ACCESS, report.getName());
|
commonCheckPermissionThrowing(getEffectivePermissionRules(report, QContext.getQInstance()), PrivatePermissionSubType.HAS_ACCESS, report.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,13 +284,6 @@ public class PermissionsHelper
|
|||||||
public static void checkWidgetPermissionThrowing(AbstractActionInput actionInput, String widgetName) throws QPermissionDeniedException
|
public static void checkWidgetPermissionThrowing(AbstractActionInput actionInput, String widgetName) throws QPermissionDeniedException
|
||||||
{
|
{
|
||||||
QWidgetMetaDataInterface widget = QContext.getQInstance().getWidget(widgetName);
|
QWidgetMetaDataInterface widget = QContext.getQInstance().getWidget(widgetName);
|
||||||
|
|
||||||
if(widget == null)
|
|
||||||
{
|
|
||||||
LOG.info("Throwing a permission denied exception in response to a non-existent widget name", logPair("widgetName", widgetName));
|
|
||||||
throw (new QPermissionDeniedException("Permission denied."));
|
|
||||||
}
|
|
||||||
|
|
||||||
commonCheckPermissionThrowing(getEffectivePermissionRules(widget, QContext.getQInstance()), PrivatePermissionSubType.HAS_ACCESS, widget.getName());
|
commonCheckPermissionThrowing(getEffectivePermissionRules(widget, QContext.getQInstance()), PrivatePermissionSubType.HAS_ACCESS, widget.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@ package com.kingsrook.qqq.backend.core.actions.processes;
|
|||||||
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;
|
||||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QBadRequestException;
|
import com.kingsrook.qqq.backend.core.exceptions.QBadRequestException;
|
||||||
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;
|
||||||
@ -55,7 +54,7 @@ public class CancelProcessAction extends RunProcessAction
|
|||||||
{
|
{
|
||||||
ActionHelper.validateSession(runProcessInput);
|
ActionHelper.validateSession(runProcessInput);
|
||||||
|
|
||||||
QProcessMetaData process = QContext.getQInstance().getProcess(runProcessInput.getProcessName());
|
QProcessMetaData process = runProcessInput.getInstance().getProcess(runProcessInput.getProcessName());
|
||||||
if(process == null)
|
if(process == null)
|
||||||
{
|
{
|
||||||
throw new QBadRequestException("Process [" + runProcessInput.getProcessName() + "] is not defined in this instance.");
|
throw new QBadRequestException("Process [" + runProcessInput.getProcessName() + "] is not defined in this instance.");
|
||||||
|
@ -30,7 +30,6 @@ import java.util.Objects;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||||
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.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.logging.QLogger;
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
@ -65,7 +64,7 @@ public class RunBackendStepAction
|
|||||||
{
|
{
|
||||||
ActionHelper.validateSession(runBackendStepInput);
|
ActionHelper.validateSession(runBackendStepInput);
|
||||||
|
|
||||||
QProcessMetaData process = QContext.getQInstance().getProcess(runBackendStepInput.getProcessName());
|
QProcessMetaData process = runBackendStepInput.getInstance().getProcess(runBackendStepInput.getProcessName());
|
||||||
if(process == null)
|
if(process == null)
|
||||||
{
|
{
|
||||||
throw new QException("Process [" + runBackendStepInput.getProcessName() + "] is not defined in this instance.");
|
throw new QException("Process [" + runBackendStepInput.getProcessName() + "] is not defined in this instance.");
|
||||||
|
@ -79,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";
|
||||||
@ -100,7 +99,7 @@ public class RunProcessAction
|
|||||||
{
|
{
|
||||||
ActionHelper.validateSession(runProcessInput);
|
ActionHelper.validateSession(runProcessInput);
|
||||||
|
|
||||||
QProcessMetaData process = QContext.getQInstance().getProcess(runProcessInput.getProcessName());
|
QProcessMetaData process = runProcessInput.getInstance().getProcess(runProcessInput.getProcessName());
|
||||||
if(process == null)
|
if(process == null)
|
||||||
{
|
{
|
||||||
throw new QException("Process [" + runProcessInput.getProcessName() + "] is not defined in this instance.");
|
throw new QException("Process [" + runProcessInput.getProcessName() + "] is not defined in this instance.");
|
||||||
@ -518,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 //
|
||||||
@ -556,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 //
|
||||||
@ -636,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 //
|
||||||
|
@ -1,202 +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;
|
|
||||||
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
|
||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.ExportInput;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportDestination;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportFormat;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
|
||||||
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.QTableMetaData;
|
|
||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
|
||||||
import com.kingsrook.qqq.backend.core.utils.Pair;
|
|
||||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
|
||||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Utility for verifying that the ExportAction works for all tables, and all
|
|
||||||
** exposed joins.
|
|
||||||
**
|
|
||||||
** Meant for use within a unit test, or maybe as part of an instance's boot-up/
|
|
||||||
** validation.
|
|
||||||
*******************************************************************************/
|
|
||||||
public class ExportsFullInstanceVerifier
|
|
||||||
{
|
|
||||||
private static final QLogger LOG = QLogger.getLogger(ExportsFullInstanceVerifier.class);
|
|
||||||
|
|
||||||
private boolean filterForAtMostOneRowPerExport = true;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
public void verify(Collection<QTableMetaData> tables) throws QException
|
|
||||||
{
|
|
||||||
Map<Pair<String, String>, Exception> caughtExceptions = new LinkedHashMap<>();
|
|
||||||
for(QTableMetaData table : tables)
|
|
||||||
{
|
|
||||||
if(table.isCapabilityEnabled(QContext.getQInstance().getBackendForTable(table.getName()), Capability.TABLE_QUERY))
|
|
||||||
{
|
|
||||||
LOG.info("Verifying Exports on table", logPair("tableName", table.getName()));
|
|
||||||
|
|
||||||
//////////////////////////////////////////////
|
|
||||||
// run the table by itself (no join fields) //
|
|
||||||
//////////////////////////////////////////////
|
|
||||||
runExport(table.getName(), Collections.emptyList(), "main-table-only", caughtExceptions);
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////
|
|
||||||
// run once w/ the fields from each exposed join //
|
|
||||||
///////////////////////////////////////////////////
|
|
||||||
for(ExposedJoin exposedJoin : CollectionUtils.nonNullList(table.getExposedJoins()))
|
|
||||||
{
|
|
||||||
runExport(table.getName(), List.of(exposedJoin), "join-" + exposedJoin.getLabel(), caughtExceptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
// run w/ all exposed joins (if there are any) //
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
if(CollectionUtils.nullSafeHasContents(table.getExposedJoins()))
|
|
||||||
{
|
|
||||||
runExport(table.getName(), table.getExposedJoins(), "all-joins", caughtExceptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////
|
|
||||||
// log out an exceptions caught //
|
|
||||||
//////////////////////////////////
|
|
||||||
if(!caughtExceptions.isEmpty())
|
|
||||||
{
|
|
||||||
for(Map.Entry<Pair<String, String>, Exception> entry : caughtExceptions.entrySet())
|
|
||||||
{
|
|
||||||
LOG.info("Caught an exception verifying reports", entry.getValue(), logPair("tableName", entry.getKey().getA()), logPair("fieldName", entry.getKey().getB()));
|
|
||||||
}
|
|
||||||
throw (new QException("Reports Verification failed with " + caughtExceptions.size() + " exception" + StringUtils.plural(caughtExceptions.size())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
private void runExport(String tableName, List<ExposedJoin> exposedJoinList, String description, Map<Pair<String, String>, Exception> caughtExceptions)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// build the list of fieldNames to export - starting with all fields in the table //
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
List<String> fieldNames = new ArrayList<>();
|
|
||||||
for(QFieldMetaData field : QContext.getQInstance().getTable(tableName).getFields().values())
|
|
||||||
{
|
|
||||||
fieldNames.add(field.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////
|
|
||||||
// add all fields from all exposed joins as well //
|
|
||||||
///////////////////////////////////////////////////
|
|
||||||
for(ExposedJoin exposedJoin : CollectionUtils.nonNullList(exposedJoinList))
|
|
||||||
{
|
|
||||||
QTableMetaData joinTable = QContext.getQInstance().getTable(exposedJoin.getJoinTable());
|
|
||||||
for(QFieldMetaData field : joinTable.getFields().values())
|
|
||||||
{
|
|
||||||
fieldNames.add(joinTable.getName() + "." + field.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG.info("Verifying export", logPair("description", description), logPair("fieldCount", fieldNames.size()));
|
|
||||||
|
|
||||||
QQueryFilter queryFilter = new QQueryFilter();
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// if caller is okay with a filter that should limit the report to a small number of rows (could be more than 1 for to-many joins), then do so //
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
if(filterForAtMostOneRowPerExport)
|
|
||||||
{
|
|
||||||
queryFilter.withCriteria(QContext.getQInstance().getTable(tableName).getPrimaryKeyField(), QCriteriaOperator.EQUALS, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
ExportInput exportInput = new ExportInput();
|
|
||||||
exportInput.setTableName(tableName);
|
|
||||||
exportInput.setFieldNames(fieldNames);
|
|
||||||
exportInput.setReportDestination(new ReportDestination()
|
|
||||||
.withReportOutputStream(new ByteArrayOutputStream())
|
|
||||||
.withReportFormat(ReportFormat.CSV));
|
|
||||||
exportInput.setQueryFilter(queryFilter);
|
|
||||||
new ExportAction().execute(exportInput);
|
|
||||||
}
|
|
||||||
catch(QException e)
|
|
||||||
{
|
|
||||||
caughtExceptions.put(Pair.of(tableName, description), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for filterForAtMostOneRowPerExport
|
|
||||||
*******************************************************************************/
|
|
||||||
public boolean getFilterForAtMostOneRowPerExport()
|
|
||||||
{
|
|
||||||
return (this.filterForAtMostOneRowPerExport);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for filterForAtMostOneRowPerExport
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setFilterForAtMostOneRowPerExport(boolean filterForAtMostOneRowPerExport)
|
|
||||||
{
|
|
||||||
this.filterForAtMostOneRowPerExport = filterForAtMostOneRowPerExport;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for filterForAtMostOneRowPerExport
|
|
||||||
*******************************************************************************/
|
|
||||||
public ExportsFullInstanceVerifier withFilterForAtMostOneRowPerExport(boolean filterForAtMostOneRowPerExport)
|
|
||||||
{
|
|
||||||
this.filterForAtMostOneRowPerExport = filterForAtMostOneRowPerExport;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -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,13 +62,10 @@ 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;
|
||||||
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.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.fields.QFieldType;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||||
@ -305,19 +301,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(exportInput.getInstance(), dataSource.getSourceTable(), dataSource.getQueryJoins(), dataSource.getQueryFilter());
|
||||||
joinsContext = new JoinsContext(QContext.getQInstance(), dataSource.getSourceTable(), dataSource.getQueryJoins(), queryFilter);
|
countDataSourceRecords(reportInput, dataSource, reportFormat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -341,7 +328,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 +345,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(dataSource.getQueryJoins());
|
||||||
|
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 + ")."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -393,26 +369,6 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
private static List<QueryJoin> cloneDataSourceQueryJoins(QReportDataSource dataSource)
|
|
||||||
{
|
|
||||||
if(dataSource == null || dataSource.getQueryJoins() == null)
|
|
||||||
{
|
|
||||||
return (null);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<QueryJoin> rs = new ArrayList<>();
|
|
||||||
for(QueryJoin queryJoin : dataSource.getQueryJoins())
|
|
||||||
{
|
|
||||||
rs.add(queryJoin.clone());
|
|
||||||
}
|
|
||||||
return (rs);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -446,19 +402,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);
|
||||||
@ -467,12 +417,12 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
|
|||||||
queryInput.setRecordPipe(recordPipe);
|
queryInput.setRecordPipe(recordPipe);
|
||||||
queryInput.setTableName(dataSource.getSourceTable());
|
queryInput.setTableName(dataSource.getSourceTable());
|
||||||
queryInput.setFilter(queryFilter);
|
queryInput.setFilter(queryFilter);
|
||||||
queryInput.setQueryJoins(cloneDataSourceQueryJoins(dataSource));
|
queryInput.setQueryJoins(dataSource.getQueryJoins());
|
||||||
queryInput.withQueryHint(QueryHint.POTENTIALLY_LARGE_NUMBER_OF_RESULTS);
|
queryInput.withQueryHint(QueryHint.POTENTIALLY_LARGE_NUMBER_OF_RESULTS);
|
||||||
queryInput.withQueryHint(QueryHint.MAY_USE_READ_ONLY_BACKEND);
|
queryInput.withQueryHint(QueryHint.MAY_USE_READ_ONLY_BACKEND);
|
||||||
|
|
||||||
queryInput.setShouldTranslatePossibleValues(true);
|
queryInput.setShouldTranslatePossibleValues(true);
|
||||||
queryInput.setFieldsToTranslatePossibleValues(setupFieldsToTranslatePossibleValues(reportInput, dataSource));
|
queryInput.setFieldsToTranslatePossibleValues(setupFieldsToTranslatePossibleValues(reportInput, dataSource, new JoinsContext(reportInput.getInstance(), dataSource.getSourceTable(), dataSource.getQueryJoins(), queryInput.getFilter())));
|
||||||
|
|
||||||
if(dataSource.getQueryInputCustomizer() != null)
|
if(dataSource.getQueryInputCustomizer() != null)
|
||||||
{
|
{
|
||||||
@ -524,7 +474,7 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
|
|||||||
}
|
}
|
||||||
consumedCount.getAndAdd(records.size());
|
consumedCount.getAndAdd(records.size());
|
||||||
|
|
||||||
return (consumeRecords(dataSource, records, tableView, summaryViews, variantViews));
|
return (consumeRecords(reportInput, dataSource, records, tableView, summaryViews, variantViews));
|
||||||
});
|
});
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
@ -543,7 +493,7 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private Set<String> setupFieldsToTranslatePossibleValues(ReportInput reportInput, QReportDataSource dataSource) throws QException
|
private Set<String> setupFieldsToTranslatePossibleValues(ReportInput reportInput, QReportDataSource dataSource, JoinsContext joinsContext) throws QException
|
||||||
{
|
{
|
||||||
Set<String> fieldsToTranslatePossibleValues = new HashSet<>();
|
Set<String> fieldsToTranslatePossibleValues = new HashSet<>();
|
||||||
|
|
||||||
@ -616,56 +566,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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -673,9 +574,9 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private Integer consumeRecords(QReportDataSource dataSource, List<QRecord> records, QReportView tableView, List<QReportView> summaryViews, List<QReportView> variantViews) throws QException
|
private Integer consumeRecords(ReportInput reportInput, QReportDataSource dataSource, List<QRecord> records, QReportView tableView, List<QReportView> summaryViews, List<QReportView> variantViews) throws QException
|
||||||
{
|
{
|
||||||
QTableMetaData table = QContext.getQInstance().getTable(dataSource.getSourceTable());
|
QTableMetaData table = reportInput.getInstance().getTable(dataSource.getSourceTable());
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
// if this record goes on a table view, add it to the report streamer now //
|
// if this record goes on a table view, add it to the report streamer now //
|
||||||
@ -786,7 +687,7 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private void addRecordToSummaryKeyAggregates(QTableMetaData table, QRecord record, Map<SummaryKey, Map<String, AggregatesInterface<?, ?>>> viewAggregates, SummaryKey key)
|
private void addRecordToSummaryKeyAggregates(QTableMetaData table, QRecord record, Map<SummaryKey, Map<String, AggregatesInterface<?, ?>>> viewAggregates, SummaryKey key) throws QException
|
||||||
{
|
{
|
||||||
Map<String, AggregatesInterface<?, ?>> keyAggregates = viewAggregates.computeIfAbsent(key, (name) -> new HashMap<>());
|
Map<String, AggregatesInterface<?, ?>> keyAggregates = viewAggregates.computeIfAbsent(key, (name) -> new HashMap<>());
|
||||||
addRecordToAggregatesMap(table, record, keyAggregates);
|
addRecordToAggregatesMap(table, record, keyAggregates);
|
||||||
@ -797,7 +698,7 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private void addRecordToAggregatesMap(QTableMetaData table, QRecord record, Map<String, AggregatesInterface<?, ?>> aggregatesMap)
|
private void addRecordToAggregatesMap(QTableMetaData table, QRecord record, Map<String, AggregatesInterface<?, ?>> aggregatesMap) throws QException
|
||||||
{
|
{
|
||||||
//////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
// todo - an optimization could be, to only compute aggregates that we'll need... //
|
// todo - an optimization could be, to only compute aggregates that we'll need... //
|
||||||
@ -805,7 +706,7 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
|
|||||||
//////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
for(String fieldName : record.getValues().keySet())
|
for(String fieldName : record.getValues().keySet())
|
||||||
{
|
{
|
||||||
QFieldMetaData field;
|
QFieldMetaData field = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////
|
||||||
@ -878,14 +779,9 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
|
|||||||
List<QReportView> reportViews = views.stream().filter(v -> v.getType().equals(ReportType.SUMMARY)).toList();
|
List<QReportView> reportViews = views.stream().filter(v -> v.getType().equals(ReportType.SUMMARY)).toList();
|
||||||
for(QReportView view : reportViews)
|
for(QReportView view : reportViews)
|
||||||
{
|
{
|
||||||
QReportDataSource dataSource = getDataSource(view.getDataSourceName());
|
QReportDataSource dataSource = getDataSource(view.getDataSourceName());
|
||||||
if(dataSource == null)
|
QTableMetaData table = reportInput.getInstance().getTable(dataSource.getSourceTable());
|
||||||
{
|
SummaryOutput summaryOutput = computeSummaryRowsForView(reportInput, view, table);
|
||||||
throw new QReportingException("Data source for summary view was not found (viewName=" + view.getName() + ", dataSourceName=" + view.getDataSourceName() + ").");
|
|
||||||
}
|
|
||||||
|
|
||||||
QTableMetaData table = QContext.getQInstance().getTable(dataSource.getSourceTable());
|
|
||||||
SummaryOutput summaryOutput = computeSummaryRowsForView(reportInput, view, table);
|
|
||||||
|
|
||||||
ExportInput exportInput = new ExportInput();
|
ExportInput exportInput = new ExportInput();
|
||||||
exportInput.setReportDestination(reportInput.getReportDestination());
|
exportInput.setReportDestination(reportInput.getReportDestination());
|
||||||
@ -971,8 +867,9 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private SummaryOutput computeSummaryRowsForView(ReportInput reportInput, QReportView view, QTableMetaData table) throws QFormulaException
|
private SummaryOutput computeSummaryRowsForView(ReportInput reportInput, QReportView view, QTableMetaData table) throws QReportingException, QFormulaException
|
||||||
{
|
{
|
||||||
|
QValueFormatter valueFormatter = new QValueFormatter();
|
||||||
QMetaDataVariableInterpreter variableInterpreter = new QMetaDataVariableInterpreter();
|
QMetaDataVariableInterpreter variableInterpreter = new QMetaDataVariableInterpreter();
|
||||||
variableInterpreter.addValueMap("input", reportInput.getInputValues());
|
variableInterpreter.addValueMap("input", reportInput.getInputValues());
|
||||||
variableInterpreter.addValueMap("total", getSummaryValuesForInterpreter(totalAggregates));
|
variableInterpreter.addValueMap("total", getSummaryValuesForInterpreter(totalAggregates));
|
||||||
@ -1044,7 +941,10 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
|
|||||||
//////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
if(CollectionUtils.nullSafeHasContents(view.getOrderByFields()))
|
if(CollectionUtils.nullSafeHasContents(view.getOrderByFields()))
|
||||||
{
|
{
|
||||||
summaryRows.sort((o1, o2) -> summaryRowComparator(view, o1, o2));
|
summaryRows.sort((o1, o2) ->
|
||||||
|
{
|
||||||
|
return summaryRowComparator(view, o1, o2);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////
|
////////////////
|
||||||
@ -1079,6 +979,8 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
|
|||||||
Serializable serializable = getValueForColumn(variableInterpreter, column);
|
Serializable serializable = getValueForColumn(variableInterpreter, column);
|
||||||
totalRow.setValue(column.getName(), serializable);
|
totalRow.setValue(column.getName(), serializable);
|
||||||
thisRowValues.put(column.getName(), serializable);
|
thisRowValues.put(column.getName(), serializable);
|
||||||
|
|
||||||
|
String formatted = valueFormatter.formatValue(column.getDisplayFormat(), serializable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1101,7 +1003,7 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
|
|||||||
titleValues.add(variableInterpreter.interpret(titleField));
|
titleValues.add(variableInterpreter.interpret(titleField));
|
||||||
}
|
}
|
||||||
|
|
||||||
title = QValueFormatter.formatStringWithValues(view.getTitleFormat(), titleValues);
|
title = new QValueFormatter().formatStringWithValues(view.getTitleFormat(), titleValues);
|
||||||
}
|
}
|
||||||
else if(StringUtils.hasContent(view.getTitleFormat()))
|
else if(StringUtils.hasContent(view.getTitleFormat()))
|
||||||
{
|
{
|
||||||
|
@ -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;
|
|
||||||
|
|
||||||
}
|
|
@ -124,7 +124,7 @@ public class ExcelFastexcelExportStreamer implements ExportStreamerInterface
|
|||||||
if(workbook == null)
|
if(workbook == null)
|
||||||
{
|
{
|
||||||
String appName = ObjectUtils.tryAndRequireNonNullElse(() -> QContext.getQInstance().getBranding().getAppName(), "QQQ");
|
String appName = ObjectUtils.tryAndRequireNonNullElse(() -> QContext.getQInstance().getBranding().getAppName(), "QQQ");
|
||||||
QInstance instance = QContext.getQInstance();
|
QInstance instance = exportInput.getInstance();
|
||||||
if(instance != null && instance.getBranding() != null && instance.getBranding().getCompanyName() != null)
|
if(instance != null && instance.getBranding() != null && instance.getBranding().getCompanyName() != null)
|
||||||
{
|
{
|
||||||
appName = instance.getBranding().getCompanyName();
|
appName = instance.getBranding().getCompanyName();
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -161,7 +161,7 @@ public class StreamedSheetWriter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Integer> m = new HashMap<>();
|
Map<String, Integer> m = new HashMap();
|
||||||
m.computeIfAbsent("s", (s) -> 3);
|
m.computeIfAbsent("s", (s) -> 3);
|
||||||
|
|
||||||
value = rs.toString();
|
value = rs.toString();
|
||||||
|
@ -23,7 +23,6 @@ package com.kingsrook.qqq.backend.core.actions.scripts;
|
|||||||
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.BuildScriptLogAndScriptLogLineExecutionLogger;
|
import com.kingsrook.qqq.backend.core.actions.scripts.logging.BuildScriptLogAndScriptLogLineExecutionLogger;
|
||||||
@ -98,7 +97,7 @@ public class RecordScriptTestInterface implements TestScriptActionInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
QueryOutput queryOutput = new QueryAction().execute(new QueryInput(tableName)
|
QueryOutput queryOutput = new QueryAction().execute(new QueryInput(tableName)
|
||||||
.withFilter(new QQueryFilter(new QFilterCriteria(table.getPrimaryKeyField(), QCriteriaOperator.IN, Arrays.stream(recordPrimaryKeyList.split(",")).toList())))
|
.withFilter(new QQueryFilter(new QFilterCriteria(table.getPrimaryKeyField(), QCriteriaOperator.IN, recordPrimaryKeyList.split(","))))
|
||||||
.withIncludeAssociations(true));
|
.withIncludeAssociations(true));
|
||||||
if(CollectionUtils.nullSafeIsEmpty(queryOutput.getRecords()))
|
if(CollectionUtils.nullSafeIsEmpty(queryOutput.getRecords()))
|
||||||
{
|
{
|
||||||
|
@ -154,9 +154,8 @@ public class RunAdHocRecordScriptAction
|
|||||||
Method qRecordListToApiRecordList = apiScriptUtilsClass.getMethod("qRecordListToApiRecordList", List.class, String.class, String.class, String.class);
|
Method qRecordListToApiRecordList = apiScriptUtilsClass.getMethod("qRecordListToApiRecordList", List.class, String.class, String.class, String.class);
|
||||||
Object apiRecordList = qRecordListToApiRecordList.invoke(null, input.getRecordList(), input.getTableName(), scriptRevision.getApiName(), scriptRevision.getApiVersion());
|
Object apiRecordList = qRecordListToApiRecordList.invoke(null, input.getRecordList(), input.getTableName(), scriptRevision.getApiName(), scriptRevision.getApiVersion());
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
// noinspection unchecked
|
||||||
ArrayList<? extends Serializable> rs = (ArrayList<? extends Serializable>) apiRecordList;
|
return (ArrayList<? extends Serializable>) apiRecordList;
|
||||||
return rs;
|
|
||||||
}
|
}
|
||||||
catch(ClassNotFoundException e)
|
catch(ClassNotFoundException e)
|
||||||
{
|
{
|
||||||
|
@ -352,7 +352,7 @@ public class GetAction
|
|||||||
{
|
{
|
||||||
if(qPossibleValueTranslator == null)
|
if(qPossibleValueTranslator == null)
|
||||||
{
|
{
|
||||||
qPossibleValueTranslator = new QPossibleValueTranslator(QContext.getQInstance(), QContext.getQSession());
|
qPossibleValueTranslator = new QPossibleValueTranslator(getInput.getInstance(), getInput.getSession());
|
||||||
}
|
}
|
||||||
qPossibleValueTranslator.translatePossibleValuesInRecords(getInput.getTable(), List.of(returnRecord));
|
qPossibleValueTranslator.translatePossibleValuesInRecords(getInput.getTable(), List.of(returnRecord));
|
||||||
}
|
}
|
||||||
|
@ -227,11 +227,6 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public void performValidations(InsertInput insertInput, boolean isPreview) throws QException
|
public void performValidations(InsertInput insertInput, boolean isPreview) throws QException
|
||||||
{
|
{
|
||||||
if(CollectionUtils.nullSafeIsEmpty(insertInput.getRecords()))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QTableMetaData table = insertInput.getTable();
|
QTableMetaData table = insertInput.getTable();
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////
|
||||||
@ -246,7 +241,7 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
|
|||||||
|
|
||||||
setDefaultValuesInRecords(table, insertInput.getRecords());
|
setDefaultValuesInRecords(table, insertInput.getRecords());
|
||||||
|
|
||||||
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.INSERT, QContext.getQInstance(), table, insertInput.getRecords(), null);
|
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.INSERT, insertInput.getInstance(), table, insertInput.getRecords(), null);
|
||||||
|
|
||||||
runPreInsertCustomizerIfItIsTime(insertInput, isPreview, preInsertCustomizer, AbstractPreInsertCustomizer.WhenToRun.BEFORE_UNIQUE_KEY_CHECKS);
|
runPreInsertCustomizerIfItIsTime(insertInput, isPreview, preInsertCustomizer, AbstractPreInsertCustomizer.WhenToRun.BEFORE_UNIQUE_KEY_CHECKS);
|
||||||
setErrorsIfUniqueKeyErrors(insertInput, table);
|
setErrorsIfUniqueKeyErrors(insertInput, table);
|
||||||
|
@ -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,7 +50,6 @@ 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.metadata.QBackendMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||||
@ -66,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;
|
||||||
|
|
||||||
|
|
||||||
@ -104,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;
|
||||||
@ -163,109 +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
|
** 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.
|
||||||
@ -405,7 +297,7 @@ public class QueryAction
|
|||||||
{
|
{
|
||||||
if(qPossibleValueTranslator == null)
|
if(qPossibleValueTranslator == null)
|
||||||
{
|
{
|
||||||
qPossibleValueTranslator = new QPossibleValueTranslator(QContext.getQInstance(), QContext.getQSession());
|
qPossibleValueTranslator = new QPossibleValueTranslator(queryInput.getInstance(), queryInput.getSession());
|
||||||
}
|
}
|
||||||
qPossibleValueTranslator.translatePossibleValuesInRecords(queryInput.getTable(), records, queryInput.getQueryJoins(), queryInput.getFieldsToTranslatePossibleValues());
|
qPossibleValueTranslator.translatePossibleValuesInRecords(queryInput.getTable(), records, queryInput.getQueryJoins(), queryInput.getFieldsToTranslatePossibleValues());
|
||||||
}
|
}
|
||||||
|
@ -252,7 +252,7 @@ public class UpdateAction
|
|||||||
behaviorsToOmit = Set.of(DynamicDefaultValueBehavior.MODIFY_DATE);
|
behaviorsToOmit = Set.of(DynamicDefaultValueBehavior.MODIFY_DATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.UPDATE, QContext.getQInstance(), table, updateInput.getRecords(), behaviorsToOmit);
|
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.UPDATE, updateInput.getInstance(), table, updateInput.getRecords(), behaviorsToOmit);
|
||||||
validatePrimaryKeysAreGiven(updateInput);
|
validatePrimaryKeysAreGiven(updateInput);
|
||||||
|
|
||||||
if(oldRecordList.isPresent())
|
if(oldRecordList.isPresent())
|
||||||
|
@ -68,7 +68,7 @@ public class QValueFormatter
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public static String formatValue(QFieldMetaData field, Serializable value)
|
public static String formatValue(QFieldMetaData field, Serializable value)
|
||||||
{
|
{
|
||||||
return (formatValue(field.getDisplayFormat(), field.getType(), field.getName(), value));
|
return (formatValue(field.getDisplayFormat(), field.getName(), value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ public class QValueFormatter
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public static String formatValue(String displayFormat, Serializable value)
|
public static String formatValue(String displayFormat, Serializable value)
|
||||||
{
|
{
|
||||||
return (formatValue(displayFormat, null, "", value));
|
return (formatValue(displayFormat, "", value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ public class QValueFormatter
|
|||||||
** For a display format string, an optional fieldName (only used for logging),
|
** For a display format string, an optional fieldName (only used for logging),
|
||||||
** and a value, apply the format.
|
** and a value, apply the format.
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private static String formatValue(String displayFormat, QFieldType fieldType, String fieldName, Serializable value)
|
private static String formatValue(String displayFormat, String fieldName, Serializable value)
|
||||||
{
|
{
|
||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
// null values get null results //
|
// null values get null results //
|
||||||
@ -107,11 +107,6 @@ public class QValueFormatter
|
|||||||
return formatBoolean(b);
|
return formatBoolean(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(QFieldType.BOOLEAN.equals(fieldType))
|
|
||||||
{
|
|
||||||
return formatBoolean(ValueUtils.getValueAsBoolean(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(value instanceof LocalTime lt)
|
if(value instanceof LocalTime lt)
|
||||||
{
|
{
|
||||||
return formatLocalTime(lt);
|
return formatLocalTime(lt);
|
||||||
@ -409,7 +404,6 @@ public class QValueFormatter
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** For a single record, set its display values - where caller (meant to stay private)
|
** For a single record, set its display values - where caller (meant to stay private)
|
||||||
** can specify if they've already done fieldBehaviors (to avoid re-doing).
|
** can specify if they've already done fieldBehaviors (to avoid re-doing).
|
||||||
@ -569,7 +563,6 @@ public class QValueFormatter
|
|||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// heavy fields that weren't fetched - they should have a backend-detail specifying their length (or null if null) //
|
// heavy fields that weren't fetched - they should have a backend-detail specifying their length (or null if null) //
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
Map<String, Serializable> heavyFieldLengths = (Map<String, Serializable>) record.getBackendDetail(QRecord.BACKEND_DETAILS_TYPE_HEAVY_FIELD_LENGTHS);
|
Map<String, Serializable> heavyFieldLengths = (Map<String, Serializable>) record.getBackendDetail(QRecord.BACKEND_DETAILS_TYPE_HEAVY_FIELD_LENGTHS);
|
||||||
if(heavyFieldLengths != null)
|
if(heavyFieldLengths != null)
|
||||||
{
|
{
|
||||||
|
@ -30,7 +30,6 @@ import java.util.List;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
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.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.actions.tables.query.QCriteriaOperator;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
||||||
@ -70,14 +69,14 @@ public class SearchPossibleValueSourceAction
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public SearchPossibleValueSourceOutput execute(SearchPossibleValueSourceInput input) throws QException
|
public SearchPossibleValueSourceOutput execute(SearchPossibleValueSourceInput input) throws QException
|
||||||
{
|
{
|
||||||
QInstance qInstance = QContext.getQInstance();
|
QInstance qInstance = input.getInstance();
|
||||||
QPossibleValueSource possibleValueSource = qInstance.getPossibleValueSource(input.getPossibleValueSourceName());
|
QPossibleValueSource possibleValueSource = qInstance.getPossibleValueSource(input.getPossibleValueSourceName());
|
||||||
if(possibleValueSource == null)
|
if(possibleValueSource == null)
|
||||||
{
|
{
|
||||||
throw new QException("Missing possible value source named [" + input.getPossibleValueSourceName() + "]");
|
throw new QException("Missing possible value source named [" + input.getPossibleValueSourceName() + "]");
|
||||||
}
|
}
|
||||||
|
|
||||||
possibleValueTranslator = new QPossibleValueTranslator(QContext.getQInstance(), QContext.getQSession());
|
possibleValueTranslator = new QPossibleValueTranslator(input.getInstance(), input.getSession());
|
||||||
SearchPossibleValueSourceOutput output = null;
|
SearchPossibleValueSourceOutput output = null;
|
||||||
if(possibleValueSource.getType().equals(QPossibleValueSourceType.ENUM))
|
if(possibleValueSource.getType().equals(QPossibleValueSourceType.ENUM))
|
||||||
{
|
{
|
||||||
@ -200,7 +199,7 @@ public class SearchPossibleValueSourceAction
|
|||||||
QueryInput queryInput = new QueryInput();
|
QueryInput queryInput = new QueryInput();
|
||||||
queryInput.setTableName(possibleValueSource.getTableName());
|
queryInput.setTableName(possibleValueSource.getTableName());
|
||||||
|
|
||||||
QTableMetaData table = QContext.getQInstance().getTable(possibleValueSource.getTableName());
|
QTableMetaData table = input.getInstance().getTable(possibleValueSource.getTableName());
|
||||||
|
|
||||||
QQueryFilter queryFilter = new QQueryFilter();
|
QQueryFilter queryFilter = new QQueryFilter();
|
||||||
queryFilter.setBooleanOperator(QQueryFilter.BooleanOperator.OR);
|
queryFilter.setBooleanOperator(QQueryFilter.BooleanOperator.OR);
|
||||||
@ -260,6 +259,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 //
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -269,9 +271,6 @@ public class SearchPossibleValueSourceAction
|
|||||||
queryFilter = input.getDefaultQueryFilter();
|
queryFilter = input.getDefaultQueryFilter();
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo - skip & limit as params
|
|
||||||
queryFilter.setLimit(250);
|
|
||||||
|
|
||||||
queryFilter.setOrderBys(possibleValueSource.getOrderByFields());
|
queryFilter.setOrderBys(possibleValueSource.getOrderByFields());
|
||||||
|
|
||||||
queryInput.setFilter(queryFilter);
|
queryInput.setFilter(queryFilter);
|
||||||
@ -300,7 +299,6 @@ public class SearchPossibleValueSourceAction
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
|
||||||
private SearchPossibleValueSourceOutput searchPossibleValueCustom(SearchPossibleValueSourceInput input, QPossibleValueSource possibleValueSource)
|
private SearchPossibleValueSourceOutput searchPossibleValueCustom(SearchPossibleValueSourceInput input, QPossibleValueSource possibleValueSource)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -22,8 +22,8 @@
|
|||||||
package com.kingsrook.qqq.backend.core.exceptions;
|
package com.kingsrook.qqq.backend.core.exceptions;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
|
||||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
|
|
||||||
|
|
||||||
@ -55,11 +55,12 @@ public class QInstanceValidationException extends QException
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QInstanceValidationException(List<String> reasons)
|
public QInstanceValidationException(List<String> reasons)
|
||||||
{
|
{
|
||||||
super((CollectionUtils.nullSafeHasContents(reasons))
|
super(
|
||||||
? "Instance validation failed for the following reasons:\n - " + StringUtils.join("\n - ", reasons) + "\n(" + reasons.size() + " Total reason" + StringUtils.plural(reasons) + ")"
|
(reasons != null && reasons.size() > 0)
|
||||||
: "Validation failed, but no reasons were provided");
|
? "Instance validation failed for the following reasons:\n - " + StringUtils.join("\n - ", reasons)
|
||||||
|
: "Validation failed, but no reasons were provided");
|
||||||
|
|
||||||
if(CollectionUtils.nullSafeHasContents(reasons))
|
if(reasons != null && reasons.size() > 0)
|
||||||
{
|
{
|
||||||
this.reasons = reasons;
|
this.reasons = reasons;
|
||||||
}
|
}
|
||||||
@ -67,6 +68,25 @@ public class QInstanceValidationException extends QException
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Constructor of an array/varargs of reasons. They feed into the core exception message.
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QInstanceValidationException(String... reasons)
|
||||||
|
{
|
||||||
|
super(
|
||||||
|
(reasons != null && reasons.length > 0)
|
||||||
|
? "Instance validation failed for the following reasons: " + StringUtils.joinWithCommasAndAnd(Arrays.stream(reasons).toList())
|
||||||
|
: "Validation failed, but no reasons were provided");
|
||||||
|
|
||||||
|
if(reasons != null && reasons.length > 0)
|
||||||
|
{
|
||||||
|
this.reasons = Arrays.stream(reasons).toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Constructor of message & cause - does not populate reasons!
|
** Constructor of message & cause - does not populate reasons!
|
||||||
**
|
**
|
||||||
|
@ -44,7 +44,6 @@ 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.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;
|
||||||
@ -923,7 +922,7 @@ public class QInstanceValidator
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private void validateTableField(QInstance qInstance, String tableName, String fieldName, QTableMetaData table, QFieldMetaData field)
|
private <T extends FieldBehavior<T>> void validateTableField(QInstance qInstance, String tableName, String fieldName, QTableMetaData table, QFieldMetaData field)
|
||||||
{
|
{
|
||||||
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() + ".");
|
||||||
@ -945,13 +944,12 @@ public class QInstanceValidator
|
|||||||
assertCondition(field.getMaxLength() != null, prefix + "specifies a ValueTooLongBehavior, but not a maxLength.");
|
assertCondition(field.getMaxLength() != null, prefix + "specifies a ValueTooLongBehavior, but not a maxLength.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<Class<FieldBehavior<?>>> usedFieldBehaviorTypes = new HashSet<>();
|
Set<Class<FieldBehavior<T>>> usedFieldBehaviorTypes = new HashSet<>();
|
||||||
if(field.getBehaviors() != null)
|
if(field.getBehaviors() != null)
|
||||||
{
|
{
|
||||||
for(FieldBehavior<?> fieldBehavior : field.getBehaviors())
|
for(FieldBehavior<?> fieldBehavior : field.getBehaviors())
|
||||||
{
|
{
|
||||||
@SuppressWarnings("unchecked")
|
Class<FieldBehavior<T>> behaviorClass = (Class<FieldBehavior<T>>) fieldBehavior.getClass();
|
||||||
Class<FieldBehavior<?>> behaviorClass = (Class<FieldBehavior<?>>) fieldBehavior.getClass();
|
|
||||||
|
|
||||||
errors.addAll(fieldBehavior.validateBehaviorConfiguration(table, field));
|
errors.addAll(fieldBehavior.validateBehaviorConfiguration(table, field));
|
||||||
|
|
||||||
@ -1661,12 +1659,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)
|
||||||
@ -1675,21 +1670,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.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +24,6 @@ package com.kingsrook.qqq.backend.core.instances;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import com.amazonaws.auth.AWSStaticCredentialsProvider;
|
import com.amazonaws.auth.AWSStaticCredentialsProvider;
|
||||||
@ -114,7 +113,7 @@ public class SecretsManagerUtils
|
|||||||
dotEnv.renameTo(new File(".env.backup-" + System.currentTimeMillis()));
|
dotEnv.renameTo(new File(".env.backup-" + System.currentTimeMillis()));
|
||||||
}
|
}
|
||||||
|
|
||||||
FileUtils.writeStringToFile(dotEnv, fullEnv.toString(), StandardCharsets.UTF_8);
|
FileUtils.writeStringToFile(dotEnv, fullEnv.toString());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -280,16 +280,6 @@ public class QLogger
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
public void debug(LogPair... logPairs)
|
|
||||||
{
|
|
||||||
logger.warn(() -> makeJsonString(null, null, logPairs));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -430,16 +420,6 @@ public class QLogger
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
public void warn(LogPair... logPairs)
|
|
||||||
{
|
|
||||||
logger.warn(() -> makeJsonString(null, null, logPairs));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -500,16 +480,6 @@ public class QLogger
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
public void error(LogPair... logPairs)
|
|
||||||
{
|
|
||||||
logger.warn(() -> makeJsonString(null, null, logPairs));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -32,6 +32,7 @@ import com.kingsrook.qqq.backend.core.exceptions.QInstanceValidationException;
|
|||||||
import com.kingsrook.qqq.backend.core.instances.QInstanceValidator;
|
import com.kingsrook.qqq.backend.core.instances.QInstanceValidator;
|
||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.authentication.QAuthenticationMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||||
|
|
||||||
|
|
||||||
@ -92,6 +93,17 @@ public class AbstractActionInput
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@JsonIgnore
|
||||||
|
public QAuthenticationMetaData getAuthenticationMetaData()
|
||||||
|
{
|
||||||
|
return (getInstance().getAuthentication());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for instance
|
** Getter for instance
|
||||||
**
|
**
|
||||||
|
@ -152,8 +152,5 @@ public class AuditDetailAccumulator implements Serializable
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
**
|
|
||||||
***************************************************************************/
|
|
||||||
private record TableNameAndPrimaryKey(String tableName, Serializable primaryKey) {}
|
private record TableNameAndPrimaryKey(String tableName, Serializable primaryKey) {}
|
||||||
}
|
}
|
||||||
|
@ -59,30 +59,6 @@ public class AggregateInput extends AbstractTableActionInput
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Constructor
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
public AggregateInput(String tableName)
|
|
||||||
{
|
|
||||||
this();
|
|
||||||
setTableName(tableName);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
@Override
|
|
||||||
public AggregateInput withTableName(String tableName)
|
|
||||||
{
|
|
||||||
super.withTableName(tableName);
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for filter
|
** Getter for filter
|
||||||
**
|
**
|
||||||
|
@ -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
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -378,7 +378,7 @@ public class JoinsContext
|
|||||||
{
|
{
|
||||||
securityFieldTableAlias = matchedQueryJoin.getJoinTableOrItsAlias();
|
securityFieldTableAlias = matchedQueryJoin.getJoinTableOrItsAlias();
|
||||||
}
|
}
|
||||||
tmpTable = instance.getTable(aliasToTableNameMap.getOrDefault(securityFieldTableAlias, securityFieldTableAlias));
|
tmpTable = instance.getTable(securityFieldTableAlias);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// set the baseTableOrAlias for the next iteration to be this join's joinTableOrAlias //
|
// set the baseTableOrAlias for the next iteration to be this join's joinTableOrAlias //
|
||||||
@ -466,8 +466,8 @@ public class JoinsContext
|
|||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// check if the key type has an all-access key, and if so, if it's set to true for the current user/session //
|
// 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());
|
QSecurityKeyType securityKeyType = instance.getSecurityKeyType(recordSecurityLock.getSecurityKeyType());
|
||||||
boolean haveAllAccessKey = false;
|
boolean haveAllAccessKey = false;
|
||||||
if(StringUtils.hasContent(securityKeyType.getAllAccessKeyName()))
|
if(StringUtils.hasContent(securityKeyType.getAllAccessKeyName()))
|
||||||
{
|
{
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -1118,7 +1118,7 @@ public class JoinsContext
|
|||||||
if(useExposedJoins)
|
if(useExposedJoins)
|
||||||
{
|
{
|
||||||
QTableMetaData mainTable = QContext.getQInstance().getTable(mainTableName);
|
QTableMetaData mainTable = QContext.getQInstance().getTable(mainTableName);
|
||||||
for(ExposedJoin exposedJoin : CollectionUtils.nonNullList(mainTable.getExposedJoins()))
|
for(ExposedJoin exposedJoin : mainTable.getExposedJoins())
|
||||||
{
|
{
|
||||||
if(exposedJoin.getJoinTable().equals(joinTableName))
|
if(exposedJoin.getJoinTable().equals(joinTableName))
|
||||||
{
|
{
|
||||||
@ -1159,7 +1159,6 @@ public class JoinsContext
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -25,7 +25,6 @@ package com.kingsrook.qqq.backend.core.model.actions.tables.query;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -238,28 +237,6 @@ public class QQueryFilter implements Serializable, Cloneable
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** fluent method to add a new criteria
|
|
||||||
*******************************************************************************/
|
|
||||||
public QQueryFilter withCriteria(String fieldName, QCriteriaOperator operator, Collection<? extends Serializable> values)
|
|
||||||
{
|
|
||||||
addCriteria(new QFilterCriteria(fieldName, operator, values));
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** fluent method to add a new criteria
|
|
||||||
*******************************************************************************/
|
|
||||||
public QQueryFilter withCriteria(String fieldName, QCriteriaOperator operator, Serializable... values)
|
|
||||||
{
|
|
||||||
addCriteria(new QFilterCriteria(fieldName, operator, values));
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -528,27 +505,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 +522,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 +530,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 +563,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 +655,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()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
|||||||
** JoinsContext is constructed before executing a query, and not meant to be set
|
** JoinsContext is constructed before executing a query, and not meant to be set
|
||||||
** by users.
|
** by users.
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class QueryJoin implements Cloneable
|
public class QueryJoin
|
||||||
{
|
{
|
||||||
private String baseTableOrAlias;
|
private String baseTableOrAlias;
|
||||||
private String joinTable;
|
private String joinTable;
|
||||||
@ -69,40 +69,6 @@ public class QueryJoin implements Cloneable
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
@Override
|
|
||||||
public QueryJoin clone()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
QueryJoin clone = (QueryJoin) super.clone();
|
|
||||||
|
|
||||||
if(joinMetaData != null)
|
|
||||||
{
|
|
||||||
clone.joinMetaData = joinMetaData.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(securityCriteria != null)
|
|
||||||
{
|
|
||||||
clone.securityCriteria = new ArrayList<>();
|
|
||||||
for(QFilterCriteria securityCriterion : securityCriteria)
|
|
||||||
{
|
|
||||||
clone.securityCriteria.add(securityCriterion.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
catch(CloneNotSupportedException e)
|
|
||||||
{
|
|
||||||
throw new AssertionError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** define the types of joins - INNER, LEFT, RIGHT, or FULL.
|
** define the types of joins - INNER, LEFT, RIGHT, or FULL.
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -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,19 +35,16 @@ public abstract class AbstractFilterExpression<T extends Serializable> implement
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public abstract T evaluate(QFieldMetaData field) throws QException;
|
public abstract T evaluate() throws QException;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Evaluate the expression, given a map of input values.
|
|
||||||
**
|
**
|
||||||
** By default, this will defer to the evaluate(void) method - but, a subclass
|
|
||||||
** (e.g., FilterVariableExpression) may react differently.
|
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public T evaluateInputValues(Map<String, Serializable> inputValues) throws QException
|
public T evaluateInputValues(Map<String, Serializable> inputValues) throws QException
|
||||||
{
|
{
|
||||||
return evaluate(null);
|
return (T) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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."));
|
||||||
}
|
}
|
||||||
|
@ -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 + "]"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
@ -47,9 +42,6 @@ public class NowWithOffset extends AbstractFilterExpression<Serializable>
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
**
|
|
||||||
***************************************************************************/
|
|
||||||
public enum Operator
|
public enum Operator
|
||||||
{PLUS, MINUS}
|
{PLUS, MINUS}
|
||||||
|
|
||||||
@ -128,30 +120,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 +144,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
|
||||||
**
|
**
|
||||||
|
@ -22,35 +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;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
**
|
|
||||||
***************************************************************************/
|
|
||||||
public enum Operator
|
public enum Operator
|
||||||
{THIS, LAST}
|
{THIS, LAST}
|
||||||
|
|
||||||
@ -93,7 +85,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 +96,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 +151,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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,11 +79,10 @@ public class QFilterCriteriaDeserializer extends StdDeserializer<QFilterCriteria
|
|||||||
/////////////////////////////////
|
/////////////////////////////////
|
||||||
// get values out of json node //
|
// get values out of json node //
|
||||||
/////////////////////////////////
|
/////////////////////////////////
|
||||||
@SuppressWarnings("unchecked")
|
List<Serializable> values = objectMapper.treeToValue(node.get("values"), List.class);
|
||||||
List<Serializable> values = objectMapper.treeToValue(node.get("values"), List.class);
|
String fieldName = objectMapper.treeToValue(node.get("fieldName"), String.class);
|
||||||
String fieldName = objectMapper.treeToValue(node.get("fieldName"), String.class);
|
QCriteriaOperator operator = objectMapper.treeToValue(node.get("operator"), QCriteriaOperator.class);
|
||||||
QCriteriaOperator operator = objectMapper.treeToValue(node.get("operator"), QCriteriaOperator.class);
|
String otherFieldName = objectMapper.treeToValue(node.get("otherFieldName"), String.class);
|
||||||
String otherFieldName = objectMapper.treeToValue(node.get("otherFieldName"), String.class);
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// look at all the values - if any of them are actually meant to be an Expression (instance of subclass of AbstractFilterExpression) //
|
// look at all the values - if any of them are actually meant to be an Expression (instance of subclass of AbstractFilterExpression) //
|
||||||
|
@ -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))
|
||||||
|
@ -28,9 +28,6 @@ package com.kingsrook.qqq.backend.core.model.dashboard.widgets;
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class AlertData extends QWidgetData
|
public class AlertData extends QWidgetData
|
||||||
{
|
{
|
||||||
/***************************************************************************
|
|
||||||
**
|
|
||||||
***************************************************************************/
|
|
||||||
public enum AlertType
|
public enum AlertType
|
||||||
{
|
{
|
||||||
ERROR,
|
ERROR,
|
||||||
|
@ -40,10 +40,9 @@ public class CompositeWidgetData extends AbstractBlockWidgetData<CompositeWidget
|
|||||||
{
|
{
|
||||||
private List<AbstractBlockWidgetData<?, ?, ?, ?>> blocks = new ArrayList<>();
|
private List<AbstractBlockWidgetData<?, ?, ?, ?>> blocks = new ArrayList<>();
|
||||||
|
|
||||||
private Layout layout;
|
private Map<String, Serializable> styleOverrides = new HashMap<>();
|
||||||
private Map<String, Serializable> styleOverrides = new HashMap<>();
|
|
||||||
private String overlayHtml;
|
private Layout layout;
|
||||||
private Map<String, Serializable> overlayStyleOverrides = new HashMap<>();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -219,91 +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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
|
||||||
@ -75,7 +74,6 @@ public abstract class AbstractBlockWidgetData<
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public T withTooltip(S key, String value)
|
public T withTooltip(S key, String value)
|
||||||
{
|
{
|
||||||
addTooltip(key, value);
|
addTooltip(key, value);
|
||||||
@ -101,7 +99,6 @@ public abstract class AbstractBlockWidgetData<
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public T withTooltip(S key, BlockTooltip value)
|
public T withTooltip(S key, BlockTooltip value)
|
||||||
{
|
{
|
||||||
addTooltip(key, value);
|
addTooltip(key, value);
|
||||||
@ -147,7 +144,6 @@ public abstract class AbstractBlockWidgetData<
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Fluent setter for tooltipMap
|
** Fluent setter for tooltipMap
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public T withTooltipMap(Map<S, BlockTooltip> tooltipMap)
|
public T withTooltipMap(Map<S, BlockTooltip> tooltipMap)
|
||||||
{
|
{
|
||||||
this.tooltipMap = tooltipMap;
|
this.tooltipMap = tooltipMap;
|
||||||
@ -182,7 +178,6 @@ public abstract class AbstractBlockWidgetData<
|
|||||||
** Fluent setter for tooltip
|
** Fluent setter for tooltip
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public T withTooltip(String tooltip)
|
public T withTooltip(String tooltip)
|
||||||
{
|
{
|
||||||
this.tooltip = new BlockTooltip(tooltip);
|
this.tooltip = new BlockTooltip(tooltip);
|
||||||
@ -195,7 +190,6 @@ public abstract class AbstractBlockWidgetData<
|
|||||||
** Fluent setter for tooltip
|
** Fluent setter for tooltip
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public T withTooltip(BlockTooltip tooltip)
|
public T withTooltip(BlockTooltip tooltip)
|
||||||
{
|
{
|
||||||
this.tooltip = tooltip;
|
this.tooltip = tooltip;
|
||||||
@ -205,22 +199,8 @@ public abstract class AbstractBlockWidgetData<
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Fluent setter for tooltip
|
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public T withTooltip(CompositeWidgetData data)
|
|
||||||
{
|
|
||||||
this.tooltip = new BlockTooltip(data);
|
|
||||||
return (T) (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public T withLink(S key, String value)
|
public T withLink(S key, String value)
|
||||||
{
|
{
|
||||||
addLink(key, value);
|
addLink(key, value);
|
||||||
@ -246,7 +226,6 @@ public abstract class AbstractBlockWidgetData<
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public T withLink(S key, BlockLink value)
|
public T withLink(S key, BlockLink value)
|
||||||
{
|
{
|
||||||
addLink(key, value);
|
addLink(key, value);
|
||||||
@ -292,7 +271,6 @@ public abstract class AbstractBlockWidgetData<
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Fluent setter for linkMap
|
** Fluent setter for linkMap
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public T withLinkMap(Map<S, BlockLink> linkMap)
|
public T withLinkMap(Map<S, BlockLink> linkMap)
|
||||||
{
|
{
|
||||||
this.linkMap = linkMap;
|
this.linkMap = linkMap;
|
||||||
@ -327,7 +305,6 @@ public abstract class AbstractBlockWidgetData<
|
|||||||
** Fluent setter for link
|
** Fluent setter for link
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public T withLink(String link)
|
public T withLink(String link)
|
||||||
{
|
{
|
||||||
this.link = new BlockLink(link);
|
this.link = new BlockLink(link);
|
||||||
@ -340,7 +317,6 @@ public abstract class AbstractBlockWidgetData<
|
|||||||
** Fluent setter for link
|
** Fluent setter for link
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public T withLink(BlockLink link)
|
public T withLink(BlockLink link)
|
||||||
{
|
{
|
||||||
this.link = link;
|
this.link = link;
|
||||||
@ -372,7 +348,6 @@ public abstract class AbstractBlockWidgetData<
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Fluent setter for values
|
** Fluent setter for values
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public T withValues(V values)
|
public T withValues(V values)
|
||||||
{
|
{
|
||||||
this.values = values;
|
this.values = values;
|
||||||
@ -404,7 +379,6 @@ public abstract class AbstractBlockWidgetData<
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Fluent setter for styles
|
** Fluent setter for styles
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public T withStyles(SX styles)
|
public T withStyles(SX styles)
|
||||||
{
|
{
|
||||||
this.styles = styles;
|
this.styles = styles;
|
||||||
@ -412,7 +386,6 @@ public abstract class AbstractBlockWidgetData<
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for blockId
|
** Getter for blockId
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -436,11 +409,11 @@ public abstract class AbstractBlockWidgetData<
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Fluent setter for blockId
|
** Fluent setter for blockId
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public T withBlockId(String blockId)
|
public T withBlockId(String blockId)
|
||||||
{
|
{
|
||||||
this.blockId = blockId;
|
this.blockId = blockId;
|
||||||
return (T) this;
|
return (T) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,24 +22,17 @@
|
|||||||
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;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
**
|
|
||||||
***************************************************************************/
|
|
||||||
public enum Placement
|
public enum Placement
|
||||||
{BOTTOM, LEFT, RIGHT, TOP}
|
{BOTTOM, LEFT, RIGHT, TOP}
|
||||||
|
|
||||||
@ -66,17 +59,6 @@ public class BlockTooltip
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Constructor
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
public BlockTooltip(CompositeWidgetData blockData)
|
|
||||||
{
|
|
||||||
this.blockData = blockData;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for title
|
** Getter for title
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -137,35 +119,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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -192,7 +192,6 @@ public abstract class QRecordEntity
|
|||||||
|
|
||||||
for(QRecordEntityAssociation qRecordEntityAssociation : getAssociationList(this.getClass()))
|
for(QRecordEntityAssociation qRecordEntityAssociation : getAssociationList(this.getClass()))
|
||||||
{
|
{
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
List<? extends QRecordEntity> associatedEntities = (List<? extends QRecordEntity>) qRecordEntityAssociation.getGetter().invoke(this);
|
List<? extends QRecordEntity> associatedEntities = (List<? extends QRecordEntity>) qRecordEntityAssociation.getGetter().invoke(this);
|
||||||
String associationName = qRecordEntityAssociation.getAssociationAnnotation().name();
|
String associationName = qRecordEntityAssociation.getAssociationAnnotation().name();
|
||||||
|
|
||||||
@ -246,7 +245,6 @@ public abstract class QRecordEntity
|
|||||||
|
|
||||||
for(QRecordEntityAssociation qRecordEntityAssociation : getAssociationList(this.getClass()))
|
for(QRecordEntityAssociation qRecordEntityAssociation : getAssociationList(this.getClass()))
|
||||||
{
|
{
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
List<? extends QRecordEntity> associatedEntities = (List<? extends QRecordEntity>) qRecordEntityAssociation.getGetter().invoke(this);
|
List<? extends QRecordEntity> associatedEntities = (List<? extends QRecordEntity>) qRecordEntityAssociation.getGetter().invoke(this);
|
||||||
String associationName = qRecordEntityAssociation.getAssociationAnnotation().name();
|
String associationName = qRecordEntityAssociation.getAssociationAnnotation().name();
|
||||||
|
|
||||||
@ -348,7 +346,6 @@ public abstract class QRecordEntity
|
|||||||
|
|
||||||
if(associationAnnotation.isPresent())
|
if(associationAnnotation.isPresent())
|
||||||
{
|
{
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
Class<? extends QRecordEntity> listTypeParam = (Class<? extends QRecordEntity>) getListTypeParam(possibleGetter.getReturnType(), possibleGetter.getAnnotatedReturnType());
|
Class<? extends QRecordEntity> listTypeParam = (Class<? extends QRecordEntity>) getListTypeParam(possibleGetter.getReturnType(), possibleGetter.getAnnotatedReturnType());
|
||||||
associationList.add(new QRecordEntityAssociation(fieldName, possibleGetter, setter.get(), listTypeParam, associationAnnotation.orElse(null)));
|
associationList.add(new QRecordEntityAssociation(fieldName, possibleGetter, setter.get(), listTypeParam, associationAnnotation.orElse(null)));
|
||||||
}
|
}
|
||||||
|
@ -39,9 +39,6 @@ public class ParentWidgetMetaData extends QWidgetMetaData
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
**
|
|
||||||
***************************************************************************/
|
|
||||||
public enum LayoutType
|
public enum LayoutType
|
||||||
{
|
{
|
||||||
GRID,
|
GRID,
|
||||||
|
@ -188,8 +188,7 @@ public class FieldAdornment
|
|||||||
** Fluent setter for values
|
** Fluent setter for values
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@SafeVarargs
|
public FieldAdornment withValues(Pair<String, Serializable>... values)
|
||||||
public final FieldAdornment withValues(Pair<String, Serializable>... values)
|
|
||||||
{
|
{
|
||||||
for(Pair<String, Serializable> value : values)
|
for(Pair<String, Serializable> value : values)
|
||||||
{
|
{
|
||||||
|
@ -168,11 +168,11 @@ public class QFrontendTableMetaData
|
|||||||
editPermission = PermissionsHelper.hasTablePermission(actionInput, tableMetaData.getName(), TablePermissionSubType.EDIT);
|
editPermission = PermissionsHelper.hasTablePermission(actionInput, tableMetaData.getName(), TablePermissionSubType.EDIT);
|
||||||
deletePermission = PermissionsHelper.hasTablePermission(actionInput, tableMetaData.getName(), TablePermissionSubType.DELETE);
|
deletePermission = PermissionsHelper.hasTablePermission(actionInput, tableMetaData.getName(), TablePermissionSubType.DELETE);
|
||||||
|
|
||||||
QBackendMetaData backend = QContext.getQInstance().getBackend(tableMetaData.getBackendName());
|
QBackendMetaData backend = actionInput.getInstance().getBackend(tableMetaData.getBackendName());
|
||||||
if(backend != null && backend.getUsesVariants())
|
if(backend != null && backend.getUsesVariants())
|
||||||
{
|
{
|
||||||
usesVariants = true;
|
usesVariants = true;
|
||||||
variantTableLabel = QContext.getQInstance().getTable(backend.getVariantOptionsTableName()).getLabel();
|
variantTableLabel = actionInput.getInstance().getTable(backend.getVariantOptionsTableName()).getLabel();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.helpContents = tableMetaData.getHelpContent();
|
this.helpContents = tableMetaData.getHelpContent();
|
||||||
|
@ -26,7 +26,7 @@ package com.kingsrook.qqq.backend.core.model.metadata.joins;
|
|||||||
** Specification for (at least part of) how two tables join together - e.g.,
|
** Specification for (at least part of) how two tables join together - e.g.,
|
||||||
** leftField = rightField. Used as part of a list in a QJoinMetaData.
|
** leftField = rightField. Used as part of a list in a QJoinMetaData.
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class JoinOn implements Cloneable
|
public class JoinOn
|
||||||
{
|
{
|
||||||
private String leftField;
|
private String leftField;
|
||||||
private String rightField;
|
private String rightField;
|
||||||
@ -131,22 +131,4 @@ public class JoinOn implements Cloneable
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
@Override
|
|
||||||
public JoinOn clone()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
JoinOn clone = (JoinOn) super.clone();
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
catch(CloneNotSupportedException e)
|
|
||||||
{
|
|
||||||
throw new AssertionError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Definition of how 2 tables join together within a QQQ Instance.
|
** Definition of how 2 tables join together within a QQQ Instance.
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class QJoinMetaData implements TopLevelMetaDataInterface, Cloneable
|
public class QJoinMetaData implements TopLevelMetaDataInterface
|
||||||
{
|
{
|
||||||
private String name;
|
private String name;
|
||||||
private JoinType type;
|
private JoinType type;
|
||||||
@ -62,44 +62,6 @@ public class QJoinMetaData implements TopLevelMetaDataInterface, Cloneable
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
@Override
|
|
||||||
public QJoinMetaData clone()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
QJoinMetaData clone = (QJoinMetaData) super.clone();
|
|
||||||
|
|
||||||
if(joinOns != null)
|
|
||||||
{
|
|
||||||
clone.joinOns = new ArrayList<>();
|
|
||||||
for(JoinOn joinOn : joinOns)
|
|
||||||
{
|
|
||||||
clone.joinOns.add(joinOn.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(orderBys != null)
|
|
||||||
{
|
|
||||||
clone.orderBys = new ArrayList<>();
|
|
||||||
for(QFilterOrderBy orderBy : orderBys)
|
|
||||||
{
|
|
||||||
clone.orderBys.add(orderBy.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
catch(CloneNotSupportedException e)
|
|
||||||
{
|
|
||||||
throw new AssertionError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for name
|
** Getter for name
|
||||||
**
|
**
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -804,7 +804,7 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
|||||||
{
|
{
|
||||||
if(this.associatedScripts == null)
|
if(this.associatedScripts == null)
|
||||||
{
|
{
|
||||||
this.associatedScripts = new ArrayList<>();
|
this.associatedScripts = new ArrayList();
|
||||||
}
|
}
|
||||||
this.associatedScripts.add(associatedScript);
|
this.associatedScripts.add(associatedScript);
|
||||||
return (this);
|
return (this);
|
||||||
|
@ -32,9 +32,6 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.UniqueKey;
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class CacheUseCase
|
public class CacheUseCase
|
||||||
{
|
{
|
||||||
/***************************************************************************
|
|
||||||
**
|
|
||||||
***************************************************************************/
|
|
||||||
public enum Type
|
public enum Type
|
||||||
{
|
{
|
||||||
PRIMARY_KEY_TO_PRIMARY_KEY, // e.g., the primary key in the cache table equals the primary key in the source table.
|
PRIMARY_KEY_TO_PRIMARY_KEY, // e.g., the primary key in the cache table equals the primary key in the source table.
|
||||||
|
@ -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
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -58,7 +58,6 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
|||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||||
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.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.QueryJoin;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||||
@ -171,8 +170,6 @@ public class MemoryRecordStore
|
|||||||
Collection<QRecord> tableData = getTableData(input.getTable()).values();
|
Collection<QRecord> tableData = getTableData(input.getTable()).values();
|
||||||
List<QRecord> records = new ArrayList<>();
|
List<QRecord> records = new ArrayList<>();
|
||||||
|
|
||||||
QQueryFilter filter = clonedOrNewFilter(input.getFilter());
|
|
||||||
JoinsContext joinsContext = new JoinsContext(QContext.getQInstance(), input.getTableName(), input.getQueryJoins(), filter);
|
|
||||||
if(CollectionUtils.nullSafeHasContents(input.getQueryJoins()))
|
if(CollectionUtils.nullSafeHasContents(input.getQueryJoins()))
|
||||||
{
|
{
|
||||||
tableData = buildJoinCrossProduct(input);
|
tableData = buildJoinCrossProduct(input);
|
||||||
@ -188,7 +185,7 @@ public class MemoryRecordStore
|
|||||||
qRecord.setTableName(input.getTableName());
|
qRecord.setTableName(input.getTableName());
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean recordMatches = BackendQueryFilterUtils.doesRecordMatch(input.getFilter(), joinsContext, qRecord);
|
boolean recordMatches = BackendQueryFilterUtils.doesRecordMatch(input.getFilter(), qRecord);
|
||||||
|
|
||||||
if(recordMatches)
|
if(recordMatches)
|
||||||
{
|
{
|
||||||
@ -227,7 +224,8 @@ public class MemoryRecordStore
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private Collection<QRecord> buildJoinCrossProduct(QueryInput input) throws QException
|
private Collection<QRecord> buildJoinCrossProduct(QueryInput input) throws QException
|
||||||
{
|
{
|
||||||
QInstance qInstance = QContext.getQInstance();
|
QInstance qInstance = QContext.getQInstance();
|
||||||
|
JoinsContext joinsContext = new JoinsContext(qInstance, input.getTableName(), input.getQueryJoins(), input.getFilter());
|
||||||
|
|
||||||
List<QRecord> crossProduct = new ArrayList<>();
|
List<QRecord> crossProduct = new ArrayList<>();
|
||||||
QTableMetaData leftTable = input.getTable();
|
QTableMetaData leftTable = input.getTable();
|
||||||
@ -375,14 +373,7 @@ public class MemoryRecordStore
|
|||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
if(recordToInsert.getValue(primaryKeyField.getName()) == null && (primaryKeyField.getType().equals(QFieldType.INTEGER) || primaryKeyField.getType().equals(QFieldType.LONG)))
|
if(recordToInsert.getValue(primaryKeyField.getName()) == null && (primaryKeyField.getType().equals(QFieldType.INTEGER) || primaryKeyField.getType().equals(QFieldType.LONG)))
|
||||||
{
|
{
|
||||||
if(primaryKeyField.getType().equals(QFieldType.LONG))
|
recordToInsert.setValue(primaryKeyField.getName(), nextSerial++);
|
||||||
{
|
|
||||||
recordToInsert.setValue(primaryKeyField.getName(), (nextSerial++).longValue());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
recordToInsert.setValue(primaryKeyField.getName(), nextSerial++);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -392,7 +383,7 @@ public class MemoryRecordStore
|
|||||||
{
|
{
|
||||||
nextSerial = recordToInsert.getValueInteger(primaryKeyField.getName()) + 1;
|
nextSerial = recordToInsert.getValueInteger(primaryKeyField.getName()) + 1;
|
||||||
}
|
}
|
||||||
else if(primaryKeyField.getType().equals(QFieldType.LONG) && recordToInsert.getValueInteger(primaryKeyField.getName()) > nextSerial)
|
else if(primaryKeyField.getType().equals(QFieldType.LONG) && recordToInsert.getValueLong(primaryKeyField.getName()) > nextSerial)
|
||||||
{
|
{
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
// todo - mmm, could overflow here? //
|
// todo - mmm, could overflow here? //
|
||||||
@ -671,11 +662,7 @@ public class MemoryRecordStore
|
|||||||
{
|
{
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
return ((Comparable) a).compareTo(b);
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
Comparable<Serializable> comparableSerializableA = (Comparable<Serializable>) a;
|
|
||||||
|
|
||||||
return comparableSerializableA.compareTo(b);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -782,7 +769,6 @@ public class MemoryRecordStore
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
|
||||||
private static Serializable computeAggregate(List<QRecord> records, Aggregate aggregate, QTableMetaData table)
|
private static Serializable computeAggregate(List<QRecord> records, Aggregate aggregate, QTableMetaData table)
|
||||||
{
|
{
|
||||||
String fieldName = aggregate.getFieldName();
|
String fieldName = aggregate.getFieldName();
|
||||||
@ -910,21 +896,4 @@ public class MemoryRecordStore
|
|||||||
|
|
||||||
return ValueUtils.getValueAsFieldType(fieldType, aggregateValue);
|
return ValueUtils.getValueAsFieldType(fieldType, aggregateValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Either clone the input filter (so we can change it safely), or return a new blank filter.
|
|
||||||
*******************************************************************************/
|
|
||||||
protected QQueryFilter clonedOrNewFilter(QQueryFilter filter)
|
|
||||||
{
|
|
||||||
if(filter == null)
|
|
||||||
{
|
|
||||||
return (new QQueryFilter());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return (filter.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -34,18 +34,14 @@ import java.util.Objects;
|
|||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
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.actions.tables.query.JoinsContext;
|
|
||||||
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.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;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.expressions.AbstractFilterExpression;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.expressions.AbstractFilterExpression;
|
||||||
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.QFieldType;
|
|
||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||||
import org.apache.commons.lang.NotImplementedException;
|
import org.apache.commons.lang.NotImplementedException;
|
||||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -62,22 +58,8 @@ public class BackendQueryFilterUtils
|
|||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Test if record matches filter.
|
** Test if record matches filter.
|
||||||
******************************************************************************/
|
|
||||||
public static boolean doesRecordMatch(QQueryFilter filter, QRecord qRecord)
|
|
||||||
{
|
|
||||||
return doesRecordMatch(filter, null, qRecord);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Test if record matches filter - where we are executing a QueryAction, and
|
|
||||||
** we have a JoinsContext. Note, if you don't have one of those, you can call
|
|
||||||
** the overload of this method that doesn't take one, and everything downstream
|
|
||||||
** /should/ be tolerant of that being absent... You just might not have the
|
|
||||||
** benefit of things like knowing field-meta-data associated with criteria...
|
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public static boolean doesRecordMatch(QQueryFilter filter, JoinsContext joinsContext, QRecord qRecord)
|
public static boolean doesRecordMatch(QQueryFilter filter, QRecord qRecord)
|
||||||
{
|
{
|
||||||
if(filter == null || !filter.hasAnyCriteria())
|
if(filter == null || !filter.hasAnyCriteria())
|
||||||
{
|
{
|
||||||
@ -115,36 +97,7 @@ public class BackendQueryFilterUtils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
boolean criterionMatches = doesCriteriaMatch(criterion, fieldName, value);
|
||||||
// Test if this criteria(on) matches the record. //
|
|
||||||
// As criteria have become more sophisticated over time, we would like to be able to know //
|
|
||||||
// what field they are for. In general, we'll try to get that from the query's JoinsContext. //
|
|
||||||
// But, in some scenarios, that isn't available - so - be safe and defer to simpler methods //
|
|
||||||
// that might not have the full field, when necessary. //
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
Boolean criterionMatches = null;
|
|
||||||
if(joinsContext != null)
|
|
||||||
{
|
|
||||||
JoinsContext.FieldAndTableNameOrAlias fieldAndTableNameOrAlias = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
fieldAndTableNameOrAlias = joinsContext.getFieldAndTableNameOrAlias(criterion.getFieldName());
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
LOG.debug("Exception getting field from joinsContext", e, logPair("fieldName", criterion.getFieldName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(fieldAndTableNameOrAlias != null)
|
|
||||||
{
|
|
||||||
criterionMatches = doesCriteriaMatch(criterion, fieldAndTableNameOrAlias.field(), value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(criterionMatches == null)
|
|
||||||
{
|
|
||||||
criterionMatches = doesCriteriaMatch(criterion, criterion.getFieldName(), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// add this new value to the existing recordMatches value - and if we can short circuit the remaining checks, do so. //
|
// add this new value to the existing recordMatches value - and if we can short circuit the remaining checks, do so. //
|
||||||
@ -178,24 +131,11 @@ public class BackendQueryFilterUtils
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
**
|
|
||||||
***************************************************************************/
|
|
||||||
public static boolean doesCriteriaMatch(QFilterCriteria criterion, String fieldName, Serializable value)
|
|
||||||
{
|
|
||||||
QFieldMetaData field = new QFieldMetaData(fieldName, ValueUtils.inferQFieldTypeFromValue(value, QFieldType.STRING));
|
|
||||||
return doesCriteriaMatch(criterion, field, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private static boolean doesCriteriaMatch(QFilterCriteria criterion, QFieldMetaData field, Serializable value)
|
public static boolean doesCriteriaMatch(QFilterCriteria criterion, String fieldName, Serializable value)
|
||||||
{
|
{
|
||||||
String fieldName = field == null ? "__unknownField" : field.getName();
|
|
||||||
|
|
||||||
ListIterator<Serializable> valueListIterator = criterion.getValues().listIterator();
|
ListIterator<Serializable> valueListIterator = criterion.getValues().listIterator();
|
||||||
while(valueListIterator.hasNext())
|
while(valueListIterator.hasNext())
|
||||||
{
|
{
|
||||||
@ -204,7 +144,7 @@ public class BackendQueryFilterUtils
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
valueListIterator.set(expression.evaluate(field));
|
valueListIterator.set(expression.evaluate());
|
||||||
}
|
}
|
||||||
catch(QException qe)
|
catch(QException qe)
|
||||||
{
|
{
|
||||||
|
@ -27,7 +27,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
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.model.actions.processes.ProcessSummaryLine;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLine;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLineInterface;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLineInterface;
|
||||||
@ -77,7 +76,7 @@ public class BulkDeleteLoadStep extends LoadViaDeleteStep implements ProcessSumm
|
|||||||
{
|
{
|
||||||
super.preRun(runBackendStepInput, runBackendStepOutput);
|
super.preRun(runBackendStepInput, runBackendStepOutput);
|
||||||
|
|
||||||
QTableMetaData table = QContext.getQInstance().getTable(runBackendStepInput.getTableName());
|
QTableMetaData table = runBackendStepInput.getInstance().getTable(runBackendStepInput.getTableName());
|
||||||
if(table != null)
|
if(table != null)
|
||||||
{
|
{
|
||||||
tableLabel = table.getLabel();
|
tableLabel = table.getLabel();
|
||||||
@ -120,7 +119,7 @@ public class BulkDeleteLoadStep extends LoadViaDeleteStep implements ProcessSumm
|
|||||||
////////////////////////////
|
////////////////////////////
|
||||||
super.runOnePage(runBackendStepInput, runBackendStepOutput);
|
super.runOnePage(runBackendStepInput, runBackendStepOutput);
|
||||||
|
|
||||||
QTableMetaData table = QContext.getQInstance().getTable(runBackendStepInput.getTableName());
|
QTableMetaData table = runBackendStepInput.getInstance().getTable(runBackendStepInput.getTableName());
|
||||||
String primaryKeyFieldName = table.getPrimaryKeyField();
|
String primaryKeyFieldName = table.getPrimaryKeyField();
|
||||||
Map<Serializable, QRecord> outputRecordMap = runBackendStepOutput.getRecords().stream().collect(Collectors.toMap(r -> r.getValue(primaryKeyFieldName), r -> r, (a, b) -> a));
|
Map<Serializable, QRecord> outputRecordMap = runBackendStepOutput.getRecords().stream().collect(Collectors.toMap(r -> r.getValue(primaryKeyFieldName), r -> r, (a, b) -> a));
|
||||||
|
|
||||||
|
@ -27,7 +27,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.DeleteAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.DeleteAction;
|
||||||
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.model.actions.processes.ProcessSummaryLine;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLine;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLineInterface;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLineInterface;
|
||||||
@ -66,7 +65,7 @@ public class BulkDeleteTransformStep extends AbstractTransformStep
|
|||||||
///////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////
|
||||||
// capture the table label - for the process summary //
|
// capture the table label - for the process summary //
|
||||||
///////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////
|
||||||
QTableMetaData table = QContext.getQInstance().getTable(runBackendStepInput.getTableName());
|
QTableMetaData table = runBackendStepInput.getInstance().getTable(runBackendStepInput.getTableName());
|
||||||
if(table != null)
|
if(table != null)
|
||||||
{
|
{
|
||||||
tableLabel = table.getLabel();
|
tableLabel = table.getLabel();
|
||||||
|
@ -25,7 +25,6 @@ package com.kingsrook.qqq.backend.core.processes.implementations.bulk.edit;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
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.model.actions.processes.ProcessSummaryLine;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLine;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLineInterface;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLineInterface;
|
||||||
@ -100,7 +99,7 @@ public class BulkEditLoadStep extends LoadViaUpdateStep implements ProcessSummar
|
|||||||
{
|
{
|
||||||
super.preRun(runBackendStepInput, runBackendStepOutput);
|
super.preRun(runBackendStepInput, runBackendStepOutput);
|
||||||
|
|
||||||
QTableMetaData table = QContext.getQInstance().getTable(runBackendStepInput.getTableName());
|
QTableMetaData table = runBackendStepInput.getInstance().getTable(runBackendStepInput.getTableName());
|
||||||
if(table != null)
|
if(table != null)
|
||||||
{
|
{
|
||||||
tableLabel = table.getLabel();
|
tableLabel = table.getLabel();
|
||||||
@ -125,7 +124,7 @@ public class BulkEditLoadStep extends LoadViaUpdateStep implements ProcessSummar
|
|||||||
////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////
|
||||||
// roll up results based on output from update action //
|
// roll up results based on output from update action //
|
||||||
////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////
|
||||||
QTableMetaData table = QContext.getQInstance().getTable(runBackendStepInput.getTableName());
|
QTableMetaData table = runBackendStepInput.getInstance().getTable(runBackendStepInput.getTableName());
|
||||||
for(QRecord record : runBackendStepOutput.getRecords())
|
for(QRecord record : runBackendStepOutput.getRecords())
|
||||||
{
|
{
|
||||||
Serializable recordPrimaryKey = record.getValue(table.getPrimaryKeyField());
|
Serializable recordPrimaryKey = record.getValue(table.getPrimaryKeyField());
|
||||||
|
@ -31,7 +31,6 @@ import java.util.Optional;
|
|||||||
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
||||||
import com.kingsrook.qqq.backend.core.actions.values.QPossibleValueTranslator;
|
import com.kingsrook.qqq.backend.core.actions.values.QPossibleValueTranslator;
|
||||||
import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter;
|
import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter;
|
||||||
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.model.actions.processes.ProcessSummaryLine;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLine;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLineInterface;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLineInterface;
|
||||||
@ -82,7 +81,7 @@ public class BulkEditTransformStep extends AbstractTransformStep
|
|||||||
///////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////
|
||||||
// capture the table label - for the process summary //
|
// capture the table label - for the process summary //
|
||||||
///////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////
|
||||||
table = QContext.getQInstance().getTable(runBackendStepInput.getTableName());
|
table = runBackendStepInput.getInstance().getTable(runBackendStepInput.getTableName());
|
||||||
if(table != null)
|
if(table != null)
|
||||||
{
|
{
|
||||||
tableLabel = table.getLabel();
|
tableLabel = table.getLabel();
|
||||||
@ -231,7 +230,7 @@ public class BulkEditTransformStep extends AbstractTransformStep
|
|||||||
|
|
||||||
if(field.getPossibleValueSourceName() != null)
|
if(field.getPossibleValueSourceName() != null)
|
||||||
{
|
{
|
||||||
QPossibleValueTranslator qPossibleValueTranslator = new QPossibleValueTranslator(QContext.getQInstance(), QContext.getQSession());
|
QPossibleValueTranslator qPossibleValueTranslator = new QPossibleValueTranslator(runBackendStepInput.getInstance(), runBackendStepInput.getSession());
|
||||||
String translatedValue = qPossibleValueTranslator.translatePossibleValue(field, value);
|
String translatedValue = qPossibleValueTranslator.translatePossibleValue(field, value);
|
||||||
if(StringUtils.hasContent(translatedValue))
|
if(StringUtils.hasContent(translatedValue))
|
||||||
{
|
{
|
||||||
|
@ -27,7 +27,6 @@ import java.util.Locale;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import com.kingsrook.qqq.backend.core.adapters.CsvToQRecordAdapter;
|
import com.kingsrook.qqq.backend.core.adapters.CsvToQRecordAdapter;
|
||||||
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.QUserFacingException;
|
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.QUploadedFile;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.QUploadedFile;
|
||||||
@ -84,7 +83,7 @@ public class BulkInsertExtractStep extends AbstractExtractStep
|
|||||||
.withLimit(getLimit())
|
.withLimit(getLimit())
|
||||||
.withCsv(new String(bytes))
|
.withCsv(new String(bytes))
|
||||||
.withDoCorrectValueTypes(true)
|
.withDoCorrectValueTypes(true)
|
||||||
.withTable(QContext.getQInstance().getTable(tableName))
|
.withTable(runBackendStepInput.getInstance().getTable(tableName))
|
||||||
.withMapping(mapping)
|
.withMapping(mapping)
|
||||||
.withRecordCustomizer((record) ->
|
.withRecordCustomizer((record) ->
|
||||||
{
|
{
|
||||||
|
@ -38,7 +38,6 @@ import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizerInterfa
|
|||||||
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.tables.InsertAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.helpers.UniqueKeyHelper;
|
import com.kingsrook.qqq.backend.core.actions.tables.helpers.UniqueKeyHelper;
|
||||||
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.model.actions.processes.ProcessSummaryLine;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLine;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLineInterface;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLineInterface;
|
||||||
@ -105,7 +104,7 @@ public class BulkInsertTransformStep extends AbstractTransformStep
|
|||||||
@Override
|
@Override
|
||||||
public void preRun(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
public void preRun(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
||||||
{
|
{
|
||||||
this.table = QContext.getQInstance().getTable(runBackendStepInput.getTableName());
|
this.table = runBackendStepInput.getInstance().getTable(runBackendStepInput.getTableName());
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// since we're doing a unique key check in this class, we can tell the loadViaInsert step that it (rather, the InsertAction) doesn't need to re-do one. //
|
// since we're doing a unique key check in this class, we can tell the loadViaInsert step that it (rather, the InsertAction) doesn't need to re-do one. //
|
||||||
@ -122,7 +121,7 @@ public class BulkInsertTransformStep extends AbstractTransformStep
|
|||||||
public void runOnePage(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
public void runOnePage(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
||||||
{
|
{
|
||||||
int rowsInThisPage = runBackendStepInput.getRecords().size();
|
int rowsInThisPage = runBackendStepInput.getRecords().size();
|
||||||
QTableMetaData table = QContext.getQInstance().getTable(runBackendStepInput.getTableName());
|
QTableMetaData table = runBackendStepInput.getInstance().getTable(runBackendStepInput.getTableName());
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// set up an insert-input, which will be used as input to the pre-customizer as well as for additional validations //
|
// set up an insert-input, which will be used as input to the pre-customizer as well as for additional validations //
|
||||||
|
@ -1,126 +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.processes.implementations.columnstats;
|
|
||||||
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
|
||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
|
||||||
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.QTableMetaData;
|
|
||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
|
||||||
import com.kingsrook.qqq.backend.core.utils.ExceptionUtils;
|
|
||||||
import com.kingsrook.qqq.backend.core.utils.Pair;
|
|
||||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
|
||||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Utility for verifying that the ColumnStats process works for all fields,
|
|
||||||
** on all tables, and all exposed joins.
|
|
||||||
**
|
|
||||||
** Meant for use within a unit test, or maybe as part of an instance's boot-up/
|
|
||||||
** validation.
|
|
||||||
*******************************************************************************/
|
|
||||||
public class ColumnStatsFullInstanceVerifier
|
|
||||||
{
|
|
||||||
private static final QLogger LOG = QLogger.getLogger(ColumnStatsFullInstanceVerifier.class);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
public void verify(Collection<QTableMetaData> tables) throws QException
|
|
||||||
{
|
|
||||||
Map<Pair<String, String>, Exception> caughtExceptions = new LinkedHashMap<>();
|
|
||||||
for(QTableMetaData table : tables)
|
|
||||||
{
|
|
||||||
if(table.isCapabilityEnabled(QContext.getQInstance().getBackendForTable(table.getName()), Capability.QUERY_STATS))
|
|
||||||
{
|
|
||||||
LOG.info("Verifying ColumnStats on table", logPair("tableName", table.getName()));
|
|
||||||
for(QFieldMetaData field : table.getFields().values())
|
|
||||||
{
|
|
||||||
runColumnStats(table.getName(), field.getName(), caughtExceptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(ExposedJoin exposedJoin : CollectionUtils.nonNullList(table.getExposedJoins()))
|
|
||||||
{
|
|
||||||
QTableMetaData joinTable = QContext.getQInstance().getTable(exposedJoin.getJoinTable());
|
|
||||||
for(QFieldMetaData field : joinTable.getFields().values())
|
|
||||||
{
|
|
||||||
runColumnStats(table.getName(), joinTable.getName() + "." + field.getName(), caughtExceptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// log out an exceptions caught
|
|
||||||
if(!caughtExceptions.isEmpty())
|
|
||||||
{
|
|
||||||
for(Map.Entry<Pair<String, String>, Exception> entry : caughtExceptions.entrySet())
|
|
||||||
{
|
|
||||||
LOG.info("Caught an exception verifying column stats", entry.getValue(), logPair("tableName", entry.getKey().getA()), logPair("fieldName", entry.getKey().getB()));
|
|
||||||
}
|
|
||||||
throw (new QException("Column Status Verification failed with " + caughtExceptions.size() + " exception" + StringUtils.plural(caughtExceptions.size())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
private void runColumnStats(String tableName, String fieldName, Map<Pair<String, String>, Exception> caughtExceptions) throws QException
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
RunBackendStepInput input = new RunBackendStepInput();
|
|
||||||
input.addValue("tableName", tableName);
|
|
||||||
input.addValue("fieldName", fieldName);
|
|
||||||
RunBackendStepOutput output = new RunBackendStepOutput();
|
|
||||||
new ColumnStatsStep().run(input, output);
|
|
||||||
}
|
|
||||||
catch(QException e)
|
|
||||||
{
|
|
||||||
Throwable rootException = ExceptionUtils.getRootException(e);
|
|
||||||
if(rootException instanceof QException && rootException.getMessage().contains("not supported for this field's data type"))
|
|
||||||
{
|
|
||||||
////////////////////////////////////////////////
|
|
||||||
// ignore this exception, it's kinda expected //
|
|
||||||
////////////////////////////////////////////////
|
|
||||||
LOG.debug("Caught an expected-exception in column stats", e, logPair("tableName", tableName), logPair("fieldName", fieldName));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
caughtExceptions.put(Pair.of(tableName, fieldName), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -136,11 +136,29 @@ public class ColumnStatsStep implements BackendStep
|
|||||||
filter = new QQueryFilter();
|
filter = new QQueryFilter();
|
||||||
}
|
}
|
||||||
|
|
||||||
QTableMetaData table = QContext.getQInstance().getTable(tableName);
|
QueryJoin queryJoin = null;
|
||||||
|
QTableMetaData table = QContext.getQInstance().getTable(tableName);
|
||||||
FieldAndQueryJoin fieldAndQueryJoin = getFieldAndQueryJoin(table, fieldName);
|
QFieldMetaData field = null;
|
||||||
QFieldMetaData field = fieldAndQueryJoin.field();
|
if(fieldName.contains("."))
|
||||||
QueryJoin queryJoin = fieldAndQueryJoin.queryJoin();
|
{
|
||||||
|
String[] parts = fieldName.split("\\.", 2);
|
||||||
|
for(ExposedJoin exposedJoin : CollectionUtils.nonNullList(table.getExposedJoins()))
|
||||||
|
{
|
||||||
|
if(exposedJoin.getJoinTable().equals(parts[0]))
|
||||||
|
{
|
||||||
|
field = QContext.getQInstance().getTable(exposedJoin.getJoinTable()).getField(parts[1]);
|
||||||
|
queryJoin = new QueryJoin()
|
||||||
|
.withJoinTable(exposedJoin.getJoinTable())
|
||||||
|
.withSelect(true)
|
||||||
|
.withType(QueryJoin.Type.INNER);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
field = table.getField(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
if(field == null)
|
if(field == null)
|
||||||
{
|
{
|
||||||
@ -195,7 +213,7 @@ public class ColumnStatsStep implements BackendStep
|
|||||||
filter.withOrderBy(new QFilterOrderByAggregate(aggregate, false));
|
filter.withOrderBy(new QFilterOrderByAggregate(aggregate, false));
|
||||||
filter.withOrderBy(new QFilterOrderByGroupBy(groupBy));
|
filter.withOrderBy(new QFilterOrderByGroupBy(groupBy));
|
||||||
|
|
||||||
Integer limit = 1000;
|
Integer limit = 1000; // too big?
|
||||||
AggregateInput aggregateInput = new AggregateInput();
|
AggregateInput aggregateInput = new AggregateInput();
|
||||||
aggregateInput.withAggregate(aggregate);
|
aggregateInput.withAggregate(aggregate);
|
||||||
aggregateInput.withGroupBy(groupBy);
|
aggregateInput.withGroupBy(groupBy);
|
||||||
@ -205,11 +223,7 @@ public class ColumnStatsStep implements BackendStep
|
|||||||
|
|
||||||
if(queryJoin != null)
|
if(queryJoin != null)
|
||||||
{
|
{
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
aggregateInput.withQueryJoin(queryJoin);
|
||||||
// re-construct this queryJoin object - just because, the JoinsContext edits the previous one, so we can make some failing-joins otherwise... //
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
fieldAndQueryJoin = getFieldAndQueryJoin(table, fieldName);
|
|
||||||
aggregateInput.withQueryJoin(fieldAndQueryJoin.queryJoin());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AggregateOutput aggregateOutput = new AggregateAction().execute(aggregateInput);
|
AggregateOutput aggregateOutput = new AggregateAction().execute(aggregateInput);
|
||||||
@ -224,7 +238,7 @@ public class ColumnStatsStep implements BackendStep
|
|||||||
value = Instant.parse(value + ":00:00Z");
|
value = Instant.parse(value + ":00:00Z");
|
||||||
}
|
}
|
||||||
|
|
||||||
Integer count = ValueUtils.getValueAsInteger(result.getAggregateValue(aggregate));
|
Integer count = ValueUtils.getValueAsInteger(result.getAggregateValue(aggregate));
|
||||||
valueCounts.add(new QRecord().withValue(fieldName, value).withValue("count", count));
|
valueCounts.add(new QRecord().withValue(fieldName, value).withValue("count", count));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -512,43 +526,4 @@ public class ColumnStatsStep implements BackendStep
|
|||||||
return (null);
|
return (null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
private FieldAndQueryJoin getFieldAndQueryJoin(QTableMetaData table, String fieldName)
|
|
||||||
{
|
|
||||||
QFieldMetaData field = null;
|
|
||||||
QueryJoin queryJoin = null;
|
|
||||||
if(fieldName.contains("."))
|
|
||||||
{
|
|
||||||
String[] parts = fieldName.split("\\.", 2);
|
|
||||||
for(ExposedJoin exposedJoin : CollectionUtils.nonNullList(table.getExposedJoins()))
|
|
||||||
{
|
|
||||||
if(exposedJoin.getJoinTable().equals(parts[0]))
|
|
||||||
{
|
|
||||||
field = QContext.getQInstance().getTable(exposedJoin.getJoinTable()).getField(parts[1]);
|
|
||||||
queryJoin = new QueryJoin()
|
|
||||||
.withJoinTable(exposedJoin.getJoinTable())
|
|
||||||
.withSelect(true)
|
|
||||||
.withType(QueryJoin.Type.INNER);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
field = table.getField(fieldName);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (new FieldAndQueryJoin(field, queryJoin));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
private record FieldAndQueryJoin(QFieldMetaData field, QueryJoin queryJoin) {}
|
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||||
import com.kingsrook.qqq.backend.core.adapters.JsonToQFieldMappingAdapter;
|
import com.kingsrook.qqq.backend.core.adapters.JsonToQFieldMappingAdapter;
|
||||||
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.logging.QLogger;
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||||
@ -86,7 +85,7 @@ public class BasicETLTransformFunction implements BackendStep
|
|||||||
throw (new QException("Mapping was not a Key-based mapping type. Was a : " + mapping.getClass().getName()));
|
throw (new QException("Mapping was not a Key-based mapping type. Was a : " + mapping.getClass().getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
QTableMetaData table = QContext.getQInstance().getTable(tableName);
|
QTableMetaData table = runBackendStepInput.getInstance().getTable(tableName);
|
||||||
List<QRecord> mappedRecords = applyMapping(runBackendStepInput.getRecords(), table, keyBasedFieldMapping);
|
List<QRecord> mappedRecords = applyMapping(runBackendStepInput.getRecords(), table, keyBasedFieldMapping);
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -27,7 +27,6 @@ import java.util.List;
|
|||||||
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.values.QPossibleValueTranslator;
|
import com.kingsrook.qqq.backend.core.actions.values.QPossibleValueTranslator;
|
||||||
import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter;
|
import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter;
|
||||||
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.processes.RunBackendStepInput;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||||
@ -86,14 +85,14 @@ public class BaseStreamedETLStep
|
|||||||
protected void updateRecordsWithDisplayValuesAndPossibleValues(RunBackendStepInput input, List<QRecord> list)
|
protected void updateRecordsWithDisplayValuesAndPossibleValues(RunBackendStepInput input, List<QRecord> list)
|
||||||
{
|
{
|
||||||
String destinationTable = input.getValueString(StreamedETLWithFrontendProcess.FIELD_DESTINATION_TABLE);
|
String destinationTable = input.getValueString(StreamedETLWithFrontendProcess.FIELD_DESTINATION_TABLE);
|
||||||
QTableMetaData table = QContext.getQInstance().getTable(destinationTable);
|
QTableMetaData table = input.getInstance().getTable(destinationTable);
|
||||||
|
|
||||||
if(table != null && list != null)
|
if(table != null && list != null)
|
||||||
{
|
{
|
||||||
QValueFormatter qValueFormatter = new QValueFormatter();
|
QValueFormatter qValueFormatter = new QValueFormatter();
|
||||||
qValueFormatter.setDisplayValuesInRecords(table, list);
|
qValueFormatter.setDisplayValuesInRecords(table, list);
|
||||||
|
|
||||||
QPossibleValueTranslator qPossibleValueTranslator = new QPossibleValueTranslator(QContext.getQInstance(), QContext.getQSession());
|
QPossibleValueTranslator qPossibleValueTranslator = new QPossibleValueTranslator(input.getInstance(), input.getSession());
|
||||||
qPossibleValueTranslator.translatePossibleValuesInRecords(table, list);
|
qPossibleValueTranslator.translatePossibleValuesInRecords(table, list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,6 @@ import com.kingsrook.qqq.backend.core.actions.reporting.DistinctFilteringRecordP
|
|||||||
import com.kingsrook.qqq.backend.core.actions.reporting.RecordPipe;
|
import com.kingsrook.qqq.backend.core.actions.reporting.RecordPipe;
|
||||||
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;
|
||||||
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.logging.QLogger;
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||||
@ -268,7 +267,7 @@ public class ExtractViaQueryStep extends AbstractExtractStep
|
|||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// else, check for recordIds from a frontend launching of a process //
|
// else, check for recordIds from a frontend launching of a process //
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
QTableMetaData table = QContext.getQInstance().getTable(runBackendStepInput.getValueString(FIELD_SOURCE_TABLE));
|
QTableMetaData table = runBackendStepInput.getInstance().getTable(runBackendStepInput.getValueString(FIELD_SOURCE_TABLE));
|
||||||
if(table == null)
|
if(table == null)
|
||||||
{
|
{
|
||||||
throw (new QException("source table name was not set - could not load records by id"));
|
throw (new QException("source table name was not set - could not load records by id"));
|
||||||
@ -320,7 +319,7 @@ public class ExtractViaQueryStep extends AbstractExtractStep
|
|||||||
if(needDistinctPipe)
|
if(needDistinctPipe)
|
||||||
{
|
{
|
||||||
String sourceTableName = runBackendStepInput.getValueString(StreamedETLWithFrontendProcess.FIELD_SOURCE_TABLE);
|
String sourceTableName = runBackendStepInput.getValueString(StreamedETLWithFrontendProcess.FIELD_SOURCE_TABLE);
|
||||||
QTableMetaData sourceTable = QContext.getQInstance().getTable(sourceTableName);
|
QTableMetaData sourceTable = runBackendStepInput.getInstance().getTable(sourceTableName);
|
||||||
return (new DistinctFilteringRecordPipe(new UniqueKey(sourceTable.getPrimaryKeyField()), overrideCapacity));
|
return (new DistinctFilteringRecordPipe(new UniqueKey(sourceTable.getPrimaryKeyField()), overrideCapacity));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -28,7 +28,6 @@ import java.util.Optional;
|
|||||||
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
||||||
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.model.actions.processes.RunBackendStepInput;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||||
@ -87,7 +86,7 @@ public class LoadViaInsertOrUpdateStep extends AbstractLoadStep
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public void insertAndUpdateRecords(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
public void insertAndUpdateRecords(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
||||||
{
|
{
|
||||||
QTableMetaData tableMetaData = QContext.getQInstance().getTable(runBackendStepInput.getValueString(FIELD_DESTINATION_TABLE));
|
QTableMetaData tableMetaData = runBackendStepInput.getInstance().getTable(runBackendStepInput.getValueString(FIELD_DESTINATION_TABLE));
|
||||||
|
|
||||||
if(CollectionUtils.nullSafeHasContents(recordsToInsert))
|
if(CollectionUtils.nullSafeHasContents(recordsToInsert))
|
||||||
{
|
{
|
||||||
@ -140,7 +139,7 @@ public class LoadViaInsertOrUpdateStep extends AbstractLoadStep
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
protected void evaluateRecords(RunBackendStepInput runBackendStepInput) throws QException
|
protected void evaluateRecords(RunBackendStepInput runBackendStepInput) throws QException
|
||||||
{
|
{
|
||||||
QTableMetaData tableMetaData = QContext.getQInstance().getTable(runBackendStepInput.getValueString(FIELD_DESTINATION_TABLE));
|
QTableMetaData tableMetaData = runBackendStepInput.getInstance().getTable(runBackendStepInput.getValueString(FIELD_DESTINATION_TABLE));
|
||||||
recordsToInsert = new ArrayList<>();
|
recordsToInsert = new ArrayList<>();
|
||||||
recordsToUpdate = new ArrayList<>();
|
recordsToUpdate = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -162,10 +162,10 @@ public class StreamedETLPreviewStep extends BaseStreamedETLStep implements Backe
|
|||||||
private void countRecords(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput, AbstractExtractStep extractStep) throws QException
|
private void countRecords(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput, AbstractExtractStep extractStep) throws QException
|
||||||
{
|
{
|
||||||
String sourceTableName = runBackendStepInput.getValueString(StreamedETLWithFrontendProcess.FIELD_SOURCE_TABLE);
|
String sourceTableName = runBackendStepInput.getValueString(StreamedETLWithFrontendProcess.FIELD_SOURCE_TABLE);
|
||||||
QTableMetaData sourceTable = QContext.getQInstance().getTable(sourceTableName);
|
QTableMetaData sourceTable = runBackendStepInput.getInstance().getTable(sourceTableName);
|
||||||
if(StringUtils.hasContent(sourceTableName))
|
if(StringUtils.hasContent(sourceTableName))
|
||||||
{
|
{
|
||||||
QBackendMetaData sourceTableBackend = QContext.getQInstance().getBackendForTable(sourceTableName);
|
QBackendMetaData sourceTableBackend = runBackendStepInput.getInstance().getBackendForTable(sourceTableName);
|
||||||
if(sourceTable.isCapabilityEnabled(sourceTableBackend, Capability.TABLE_COUNT))
|
if(sourceTable.isCapabilityEnabled(sourceTableBackend, Capability.TABLE_COUNT))
|
||||||
{
|
{
|
||||||
Integer recordCount = extractStep.doCount(runBackendStepInput);
|
Integer recordCount = extractStep.doCount(runBackendStepInput);
|
||||||
|
@ -65,14 +65,9 @@ public class MergeDuplicatesLoadStep extends LoadViaInsertOrUpdateStep
|
|||||||
{
|
{
|
||||||
super.runOnePage(runBackendStepInput, runBackendStepOutput);
|
super.runOnePage(runBackendStepInput, runBackendStepOutput);
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
ListingHash<String, Serializable> otherTableIdsToDelete = (ListingHash<String, Serializable>) runBackendStepInput.getValue("otherTableIdsToDelete");
|
||||||
ListingHash<String, Serializable> otherTableIdsToDelete = (ListingHash<String, Serializable>) runBackendStepInput.getValue("otherTableIdsToDelete");
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
ListingHash<String, QQueryFilter> otherTableFiltersToDelete = (ListingHash<String, QQueryFilter>) runBackendStepInput.getValue("otherTableFiltersToDelete");
|
ListingHash<String, QQueryFilter> otherTableFiltersToDelete = (ListingHash<String, QQueryFilter>) runBackendStepInput.getValue("otherTableFiltersToDelete");
|
||||||
|
ListingHash<String, QRecord> otherTableRecordsToStore = (ListingHash<String, QRecord>) runBackendStepInput.getValue("otherTableRecordsToStore");
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
ListingHash<String, QRecord> otherTableRecordsToStore = (ListingHash<String, QRecord>) runBackendStepInput.getValue("otherTableRecordsToStore");
|
|
||||||
|
|
||||||
if(otherTableIdsToDelete != null)
|
if(otherTableIdsToDelete != null)
|
||||||
{
|
{
|
||||||
|
@ -46,8 +46,7 @@ public class BasicRunReportProcess
|
|||||||
public static final String STEP_NAME_EXECUTE = "execute";
|
public static final String STEP_NAME_EXECUTE = "execute";
|
||||||
public static final String STEP_NAME_ACCESS = "accessReport";
|
public static final String STEP_NAME_ACCESS = "accessReport";
|
||||||
|
|
||||||
public static final String FIELD_REPORT_NAME = "reportName";
|
public static final String FIELD_REPORT_NAME = "reportName";
|
||||||
public static final String FIELD_REPORT_FORMAT = "reportFormat";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,9 +31,7 @@ import java.time.format.DateTimeFormatter;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
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.GenerateReportAction;
|
import com.kingsrook.qqq.backend.core.actions.reporting.GenerateReportAction;
|
||||||
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.QUserFacingException;
|
|
||||||
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.processes.RunBackendStepOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportDestination;
|
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportDestination;
|
||||||
@ -51,7 +49,6 @@ import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
|||||||
public class ExecuteReportStep implements BackendStep
|
public class ExecuteReportStep implements BackendStep
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -60,10 +57,9 @@ public class ExecuteReportStep implements BackendStep
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ReportFormat reportFormat = getReportFormat(runBackendStepInput);
|
String reportName = runBackendStepInput.getValueString("reportName");
|
||||||
String reportName = runBackendStepInput.getValueString("reportName");
|
QReportMetaData report = runBackendStepInput.getInstance().getReport(reportName);
|
||||||
QReportMetaData report = QContext.getQInstance().getReport(reportName);
|
File tmpFile = File.createTempFile(reportName, ".xlsx", new File("/tmp/"));
|
||||||
File tmpFile = File.createTempFile(reportName, "." + reportFormat.getExtension());
|
|
||||||
|
|
||||||
runBackendStepInput.getAsyncJobCallback().updateStatus("Generating Report");
|
runBackendStepInput.getAsyncJobCallback().updateStatus("Generating Report");
|
||||||
|
|
||||||
@ -72,7 +68,7 @@ public class ExecuteReportStep implements BackendStep
|
|||||||
ReportInput reportInput = new ReportInput();
|
ReportInput reportInput = new ReportInput();
|
||||||
reportInput.setReportName(reportName);
|
reportInput.setReportName(reportName);
|
||||||
reportInput.setReportDestination(new ReportDestination()
|
reportInput.setReportDestination(new ReportDestination()
|
||||||
.withReportFormat(reportFormat)
|
.withReportFormat(ReportFormat.XLSX) // todo - variable
|
||||||
.withReportOutputStream(reportOutputStream));
|
.withReportOutputStream(reportOutputStream));
|
||||||
|
|
||||||
Map<String, Serializable> values = runBackendStepInput.getValues();
|
Map<String, Serializable> values = runBackendStepInput.getValues();
|
||||||
@ -82,7 +78,7 @@ public class ExecuteReportStep implements BackendStep
|
|||||||
|
|
||||||
String downloadFileBaseName = getDownloadFileBaseName(runBackendStepInput, report);
|
String downloadFileBaseName = getDownloadFileBaseName(runBackendStepInput, report);
|
||||||
|
|
||||||
runBackendStepOutput.addValue("downloadFileName", downloadFileBaseName + "." + reportFormat.getExtension());
|
runBackendStepOutput.addValue("downloadFileName", downloadFileBaseName + ".xlsx");
|
||||||
runBackendStepOutput.addValue("serverFilePath", tmpFile.getCanonicalPath());
|
runBackendStepOutput.addValue("serverFilePath", tmpFile.getCanonicalPath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,22 +90,6 @@ public class ExecuteReportStep implements BackendStep
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
**
|
|
||||||
***************************************************************************/
|
|
||||||
private ReportFormat getReportFormat(RunBackendStepInput runBackendStepInput) throws QUserFacingException
|
|
||||||
{
|
|
||||||
String reportFormatInput = runBackendStepInput.getValueString(BasicRunReportProcess.FIELD_REPORT_FORMAT);
|
|
||||||
if(StringUtils.hasContent(reportFormatInput))
|
|
||||||
{
|
|
||||||
return (ReportFormat.fromString(reportFormatInput));
|
|
||||||
}
|
|
||||||
|
|
||||||
return (ReportFormat.XLSX);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -26,7 +26,6 @@ import java.io.Serializable;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||||
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.QUserFacingException;
|
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||||
@ -107,16 +106,15 @@ public class PrepareReportForRecordStep extends PrepareReportStep
|
|||||||
}
|
}
|
||||||
|
|
||||||
String reportName = runBackendStepInput.getValueString("reportName");
|
String reportName = runBackendStepInput.getValueString("reportName");
|
||||||
QReportMetaData report = QContext.getQInstance().getReport(reportName);
|
QReportMetaData report = runBackendStepInput.getInstance().getReport(reportName);
|
||||||
// runBackendStepOutput.addValue("downloadFileBaseName", runBackendStepInput.getTable().getLabel() + " " + record.getRecordLabel());
|
// runBackendStepOutput.addValue("downloadFileBaseName", runBackendStepInput.getTable().getLabel() + " " + record.getRecordLabel());
|
||||||
runBackendStepOutput.addValue("downloadFileBaseName", report.getLabel() + " - " + record.getRecordLabel());
|
runBackendStepOutput.addValue("downloadFileBaseName", report.getLabel() + " - " + record.getRecordLabel());
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
// if there are no more input fields, then remove the INPUT step from the process. //
|
// if there are no more input fields, then remove the INPUT step from the process. //
|
||||||
/////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
@SuppressWarnings("unchecked")
|
inputFieldList = (ArrayList<QFieldMetaData>) runBackendStepOutput.getValue("inputFieldList");
|
||||||
ArrayList<QFieldMetaData> updatedInputFieldList = (ArrayList<QFieldMetaData>) runBackendStepOutput.getValue("inputFieldList");
|
if(!CollectionUtils.nullSafeHasContents(inputFieldList))
|
||||||
if(!CollectionUtils.nullSafeHasContents(updatedInputFieldList))
|
|
||||||
{
|
{
|
||||||
removeInputStepFromProcess(runBackendStepOutput);
|
removeInputStepFromProcess(runBackendStepOutput);
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,6 @@ package com.kingsrook.qqq.backend.core.processes.implementations.reports;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||||
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.model.actions.processes.RunBackendStepInput;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||||
@ -57,7 +56,7 @@ public class PrepareReportStep implements BackendStep
|
|||||||
throw (new QException("Process value [reportName] was not given."));
|
throw (new QException("Process value [reportName] was not given."));
|
||||||
}
|
}
|
||||||
|
|
||||||
QReportMetaData report = QContext.getQInstance().getReport(reportName);
|
QReportMetaData report = runBackendStepInput.getInstance().getReport(reportName);
|
||||||
if(report == null)
|
if(report == null)
|
||||||
{
|
{
|
||||||
throw (new QException("Process named [" + reportName + "] was not found in this instance."));
|
throw (new QException("Process named [" + reportName + "] was not found in this instance."));
|
||||||
|
@ -129,7 +129,6 @@ public class RenderSavedReportExecuteStep implements BackendStep
|
|||||||
.withRenderedReportStatusId(RenderedReportStatus.RUNNING.getId())
|
.withRenderedReportStatusId(RenderedReportStatus.RUNNING.getId())
|
||||||
.withReportFormat(ReportFormatPossibleValueEnum.valueOf(reportFormat.name()).getPossibleValueId())
|
.withReportFormat(ReportFormatPossibleValueEnum.valueOf(reportFormat.name()).getPossibleValueId())
|
||||||
)).getRecords().get(0);
|
)).getRecords().get(0);
|
||||||
runBackendStepOutput.addValue("renderedReportId", renderedReportRecord.getValue("id"));
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// convert the report record to report meta-data, which the GenerateReportAction works on //
|
// convert the report record to report meta-data, which the GenerateReportAction works on //
|
||||||
|
@ -1,258 +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.processes.implementations.savedreports;
|
|
||||||
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.DeleteAction;
|
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
|
||||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
|
||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportFormat;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
|
||||||
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.metadata.fields.QFieldMetaData;
|
|
||||||
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.QTableMetaData;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.savedreports.RenderedReport;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.savedreports.ReportColumns;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.savedreports.SavedReport;
|
|
||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
|
||||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
|
||||||
import com.kingsrook.qqq.backend.core.utils.Pair;
|
|
||||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
|
||||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Utility for verifying that the RenderReports process works for all fields,
|
|
||||||
** on all tables, and all exposed joins.
|
|
||||||
**
|
|
||||||
** Meant for use within a unit test, or maybe as part of an instance's boot-up/
|
|
||||||
** validation.
|
|
||||||
*******************************************************************************/
|
|
||||||
public class ReportsFullInstanceVerifier
|
|
||||||
{
|
|
||||||
private static final QLogger LOG = QLogger.getLogger(ReportsFullInstanceVerifier.class);
|
|
||||||
|
|
||||||
private boolean removeRenderedReports = true;
|
|
||||||
private boolean filterForAtMostOneRowPerReport = true;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
public void verify(Collection<QTableMetaData> tables, String storageTableName) throws QException
|
|
||||||
{
|
|
||||||
Map<Pair<String, String>, Exception> caughtExceptions = new LinkedHashMap<>();
|
|
||||||
for(QTableMetaData table : tables)
|
|
||||||
{
|
|
||||||
if(table.isCapabilityEnabled(QContext.getQInstance().getBackendForTable(table.getName()), Capability.TABLE_QUERY))
|
|
||||||
{
|
|
||||||
LOG.info("Verifying Reports on table", logPair("tableName", table.getName()));
|
|
||||||
|
|
||||||
//////////////////////////////////////////////
|
|
||||||
// run the table by itself (no join fields) //
|
|
||||||
//////////////////////////////////////////////
|
|
||||||
runReport(table.getName(), Collections.emptyList(), "main-table-only", caughtExceptions, storageTableName);
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////
|
|
||||||
// run once w/ the fields from each exposed join //
|
|
||||||
///////////////////////////////////////////////////
|
|
||||||
for(ExposedJoin exposedJoin : CollectionUtils.nonNullList(table.getExposedJoins()))
|
|
||||||
{
|
|
||||||
runReport(table.getName(), List.of(exposedJoin), "join-" + exposedJoin.getLabel(), caughtExceptions, storageTableName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
// run w/ all exposed joins (if there are any) //
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
if(CollectionUtils.nullSafeHasContents(table.getExposedJoins()))
|
|
||||||
{
|
|
||||||
runReport(table.getName(), table.getExposedJoins(), "all-joins", caughtExceptions, storageTableName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////
|
|
||||||
// log out an exceptions caught //
|
|
||||||
//////////////////////////////////
|
|
||||||
if(!caughtExceptions.isEmpty())
|
|
||||||
{
|
|
||||||
for(Map.Entry<Pair<String, String>, Exception> entry : caughtExceptions.entrySet())
|
|
||||||
{
|
|
||||||
LOG.info("Caught an exception verifying reports", entry.getValue(), logPair("tableName", entry.getKey().getA()), logPair("fieldName", entry.getKey().getB()));
|
|
||||||
}
|
|
||||||
throw (new QException("Reports Verification failed with " + caughtExceptions.size() + " exception" + StringUtils.plural(caughtExceptions.size())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
private void runReport(String tableName, List<ExposedJoin> exposedJoinList, String description, Map<Pair<String, String>, Exception> caughtExceptions, String storageTableName)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// build the list of reports to include in the column - starting with all fields in the table //
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
ReportColumns reportColumns = new ReportColumns();
|
|
||||||
for(QFieldMetaData field : QContext.getQInstance().getTable(tableName).getFields().values())
|
|
||||||
{
|
|
||||||
reportColumns.withColumn(field.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////
|
|
||||||
// add all fields from all exposed joins as well //
|
|
||||||
///////////////////////////////////////////////////
|
|
||||||
for(ExposedJoin exposedJoin : CollectionUtils.nonNullList(exposedJoinList))
|
|
||||||
{
|
|
||||||
QTableMetaData joinTable = QContext.getQInstance().getTable(exposedJoin.getJoinTable());
|
|
||||||
for(QFieldMetaData field : joinTable.getFields().values())
|
|
||||||
{
|
|
||||||
reportColumns.withColumn(joinTable.getName() + "." + field.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QQueryFilter queryFilter = new QQueryFilter();
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// if caller is okay with a filter that should limit the report to a small number of rows (could be more than 1 for to-many joins), then do so //
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
if(filterForAtMostOneRowPerReport)
|
|
||||||
{
|
|
||||||
queryFilter.withCriteria(QContext.getQInstance().getTable(tableName).getPrimaryKeyField(), QCriteriaOperator.EQUALS, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////
|
|
||||||
// insert a saved report record //
|
|
||||||
//////////////////////////////////
|
|
||||||
SavedReport savedReport = new SavedReport();
|
|
||||||
savedReport.setTableName(tableName);
|
|
||||||
savedReport.setLabel("Test " + tableName + " " + description);
|
|
||||||
savedReport.setColumnsJson(JsonUtils.toJson(reportColumns));
|
|
||||||
savedReport.setQueryFilterJson(JsonUtils.toJson(queryFilter));
|
|
||||||
List<QRecord> reportRecordList = new InsertAction().execute(new InsertInput(SavedReport.TABLE_NAME).withRecordEntity(savedReport)).getRecords();
|
|
||||||
|
|
||||||
///////////////////////
|
|
||||||
// render the report //
|
|
||||||
///////////////////////
|
|
||||||
RunBackendStepInput input = new RunBackendStepInput();
|
|
||||||
RunBackendStepOutput output = new RunBackendStepOutput();
|
|
||||||
|
|
||||||
input.addValue(RenderSavedReportMetaDataProducer.FIELD_NAME_REPORT_FORMAT, ReportFormat.CSV.name());
|
|
||||||
input.addValue(RenderSavedReportMetaDataProducer.FIELD_NAME_STORAGE_TABLE_NAME, storageTableName);
|
|
||||||
input.setRecords(reportRecordList);
|
|
||||||
|
|
||||||
new RenderSavedReportExecuteStep().run(input, output);
|
|
||||||
|
|
||||||
//////////////////////////////////////////
|
|
||||||
// clean up the report, if so requested //
|
|
||||||
//////////////////////////////////////////
|
|
||||||
if(removeRenderedReports)
|
|
||||||
{
|
|
||||||
new DeleteAction().execute(new DeleteInput(RenderedReport.TABLE_NAME).withPrimaryKey(output.getValue("renderedReportId")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(QException e)
|
|
||||||
{
|
|
||||||
caughtExceptions.put(Pair.of(tableName, description), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for removeRenderedReports
|
|
||||||
*******************************************************************************/
|
|
||||||
public boolean getRemoveRenderedReports()
|
|
||||||
{
|
|
||||||
return (this.removeRenderedReports);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for removeRenderedReports
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setRemoveRenderedReports(boolean removeRenderedReports)
|
|
||||||
{
|
|
||||||
this.removeRenderedReports = removeRenderedReports;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for removeRenderedReports
|
|
||||||
*******************************************************************************/
|
|
||||||
public ReportsFullInstanceVerifier withRemoveRenderedReports(boolean removeRenderedReports)
|
|
||||||
{
|
|
||||||
this.removeRenderedReports = removeRenderedReports;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for filterForAtMostOneRowPerReport
|
|
||||||
*******************************************************************************/
|
|
||||||
public boolean getFilterForAtMostOneRowPerReport()
|
|
||||||
{
|
|
||||||
return (this.filterForAtMostOneRowPerReport);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for filterForAtMostOneRowPerReport
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setFilterForAtMostOneRowPerReport(boolean filterForAtMostOneRowPerReport)
|
|
||||||
{
|
|
||||||
this.filterForAtMostOneRowPerReport = filterForAtMostOneRowPerReport;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for filterForAtMostOneRowPerReport
|
|
||||||
*******************************************************************************/
|
|
||||||
public ReportsFullInstanceVerifier withFilterForAtMostOneRowPerReport(boolean filterForAtMostOneRowPerReport)
|
|
||||||
{
|
|
||||||
this.filterForAtMostOneRowPerReport = filterForAtMostOneRowPerReport;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,151 +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.processes.implementations.savedreports;
|
|
||||||
|
|
||||||
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.DeleteAction;
|
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
|
||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportFormat;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.savedreports.RenderedReport;
|
|
||||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
|
||||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Utility for verifying that the RenderReports process works for all report
|
|
||||||
** records stored in the saved reports table.
|
|
||||||
**
|
|
||||||
** Meant for use within a unit test, or maybe as part of an instance's boot-up/
|
|
||||||
** validation.
|
|
||||||
*******************************************************************************/
|
|
||||||
public class SavedReportsTableFullVerifier
|
|
||||||
{
|
|
||||||
private static final QLogger LOG = QLogger.getLogger(SavedReportsTableFullVerifier.class);
|
|
||||||
|
|
||||||
private boolean removeRenderedReports = true;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
public void verify(List<QRecord> savedReportRecordList, String storageTableName) throws QException
|
|
||||||
{
|
|
||||||
Map<Integer, Exception> caughtExceptions = new LinkedHashMap<>();
|
|
||||||
for(QRecord report : savedReportRecordList)
|
|
||||||
{
|
|
||||||
runReport(report, caughtExceptions, storageTableName);
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////
|
|
||||||
// log out an exceptions caught //
|
|
||||||
//////////////////////////////////
|
|
||||||
if(!caughtExceptions.isEmpty())
|
|
||||||
{
|
|
||||||
for(Map.Entry<Integer, Exception> entry : caughtExceptions.entrySet())
|
|
||||||
{
|
|
||||||
LOG.info("Caught an exception verifying saved reports", entry.getValue(), logPair("savdReportId", entry.getKey()));
|
|
||||||
}
|
|
||||||
throw (new QException("Saved Reports Verification failed with " + caughtExceptions.size() + " exception" + StringUtils.plural(caughtExceptions.size())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
private void runReport(QRecord savedReport, Map<Integer, Exception> caughtExceptions, String storageTableName)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
///////////////////////
|
|
||||||
// render the report //
|
|
||||||
///////////////////////
|
|
||||||
RunBackendStepInput input = new RunBackendStepInput();
|
|
||||||
RunBackendStepOutput output = new RunBackendStepOutput();
|
|
||||||
|
|
||||||
input.addValue(RenderSavedReportMetaDataProducer.FIELD_NAME_REPORT_FORMAT, ReportFormat.XLSX.name());
|
|
||||||
input.addValue(RenderSavedReportMetaDataProducer.FIELD_NAME_STORAGE_TABLE_NAME, storageTableName);
|
|
||||||
input.setRecords(List.of(savedReport));
|
|
||||||
|
|
||||||
new RenderSavedReportExecuteStep().run(input, output);
|
|
||||||
Exception exception = output.getException();
|
|
||||||
if(exception != null)
|
|
||||||
{
|
|
||||||
throw (exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////
|
|
||||||
// clean up the report, if so requested //
|
|
||||||
//////////////////////////////////////////
|
|
||||||
if(removeRenderedReports)
|
|
||||||
{
|
|
||||||
new DeleteAction().execute(new DeleteInput(RenderedReport.TABLE_NAME).withPrimaryKey(output.getValue("renderedReportId")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
caughtExceptions.put(savedReport.getValueInteger("id"), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for removeRenderedReports
|
|
||||||
*******************************************************************************/
|
|
||||||
public boolean getRemoveRenderedReports()
|
|
||||||
{
|
|
||||||
return (this.removeRenderedReports);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for removeRenderedReports
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setRemoveRenderedReports(boolean removeRenderedReports)
|
|
||||||
{
|
|
||||||
this.removeRenderedReports = removeRenderedReports;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for removeRenderedReports
|
|
||||||
*******************************************************************************/
|
|
||||||
public SavedReportsTableFullVerifier withRemoveRenderedReports(boolean removeRenderedReports)
|
|
||||||
{
|
|
||||||
this.removeRenderedReports = removeRenderedReports;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -29,7 +29,6 @@ import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
|||||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException;
|
|
||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
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.processes.RunBackendStepOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||||
@ -45,7 +44,6 @@ import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.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.savedviews.SavedView;
|
import com.kingsrook.qqq.backend.core.model.savedviews.SavedView;
|
||||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -80,10 +78,10 @@ public class QuerySavedViewProcess implements BackendStep
|
|||||||
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
||||||
{
|
{
|
||||||
ActionHelper.validateSession(runBackendStepInput);
|
ActionHelper.validateSession(runBackendStepInput);
|
||||||
Integer savedViewId = runBackendStepInput.getValueInteger("id");
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
Integer savedViewId = runBackendStepInput.getValueInteger("id");
|
||||||
if(savedViewId != null)
|
if(savedViewId != null)
|
||||||
{
|
{
|
||||||
GetInput input = new GetInput();
|
GetInput input = new GetInput();
|
||||||
@ -91,11 +89,6 @@ public class QuerySavedViewProcess implements BackendStep
|
|||||||
input.setPrimaryKey(savedViewId);
|
input.setPrimaryKey(savedViewId);
|
||||||
|
|
||||||
GetOutput output = new GetAction().execute(input);
|
GetOutput output = new GetAction().execute(input);
|
||||||
if(output.getRecord() == null)
|
|
||||||
{
|
|
||||||
throw (new QNotFoundException("The requested view was not found."));
|
|
||||||
}
|
|
||||||
|
|
||||||
runBackendStepOutput.addRecord(output.getRecord());
|
runBackendStepOutput.addRecord(output.getRecord());
|
||||||
runBackendStepOutput.addValue("savedView", output.getRecord());
|
runBackendStepOutput.addValue("savedView", output.getRecord());
|
||||||
runBackendStepOutput.addValue("savedViewList", (Serializable) List.of(output.getRecord()));
|
runBackendStepOutput.addValue("savedViewList", (Serializable) List.of(output.getRecord()));
|
||||||
@ -115,11 +108,6 @@ public class QuerySavedViewProcess implements BackendStep
|
|||||||
runBackendStepOutput.addValue("savedViewList", (Serializable) output.getRecords());
|
runBackendStepOutput.addValue("savedViewList", (Serializable) output.getRecords());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(QNotFoundException qnfe)
|
|
||||||
{
|
|
||||||
LOG.info("View not found", logPair("savedViewId", savedViewId));
|
|
||||||
throw (qnfe);
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
LOG.warn("Error querying for saved views", e);
|
LOG.warn("Error querying for saved views", e);
|
||||||
|
@ -145,7 +145,7 @@ public class StoreScriptRevisionProcessStep implements BackendStep
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
scriptRevision.setValue("author", QContext.getQSession().getUser().getFullName());
|
scriptRevision.setValue("author", input.getSession().getUser().getFullName());
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
|
@ -139,7 +139,7 @@ public class TestScriptProcessStep implements BackendStep
|
|||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
// send script outputs back out //
|
// send script outputs back out //
|
||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
output.addValue("scriptLogLines", CollectionUtils.useOrWrap(testScriptOutput.getScriptLogLines(), new TypeToken<ArrayList<QRecord>>() {}));
|
output.addValue("scriptLogLines", CollectionUtils.useOrWrap(testScriptOutput.getScriptLogLines(), TypeToken.get(ArrayList.class)));
|
||||||
output.addValue("outputObject", testScriptOutput.getOutputObject());
|
output.addValue("outputObject", testScriptOutput.getOutputObject());
|
||||||
|
|
||||||
if(testScriptOutput.getException() != null)
|
if(testScriptOutput.getException() != null)
|
||||||
|
@ -72,33 +72,33 @@ import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public abstract class AbstractTableSyncTransformStep extends AbstractTransformStep
|
public abstract class AbstractTableSyncTransformStep extends AbstractTransformStep
|
||||||
{
|
{
|
||||||
protected static final QLogger LOG = QLogger.getLogger(AbstractTableSyncTransformStep.class);
|
private static final QLogger LOG = QLogger.getLogger(AbstractTableSyncTransformStep.class);
|
||||||
|
|
||||||
protected ProcessSummaryLine okToInsert = StandardProcessSummaryLineProducer.getOkToInsertLine();
|
private ProcessSummaryLine okToInsert = StandardProcessSummaryLineProducer.getOkToInsertLine();
|
||||||
protected ProcessSummaryLine okToUpdate = StandardProcessSummaryLineProducer.getOkToUpdateLine();
|
private ProcessSummaryLine okToUpdate = StandardProcessSummaryLineProducer.getOkToUpdateLine();
|
||||||
|
|
||||||
protected ProcessSummaryLine willNotInsert = new ProcessSummaryLine(Status.INFO)
|
private ProcessSummaryLine willNotInsert = new ProcessSummaryLine(Status.INFO)
|
||||||
.withMessageSuffix("because this process is not configured to insert records.")
|
.withMessageSuffix("because this process is not configured to insert records.")
|
||||||
.withSingularFutureMessage("will not be inserted ")
|
.withSingularFutureMessage("will not be inserted ")
|
||||||
.withPluralFutureMessage("will not be inserted ")
|
.withPluralFutureMessage("will not be inserted ")
|
||||||
.withSingularPastMessage("was not inserted ")
|
.withSingularPastMessage("was not inserted ")
|
||||||
.withPluralPastMessage("were not inserted ");
|
.withPluralPastMessage("were not inserted ");
|
||||||
|
|
||||||
protected ProcessSummaryLine willNotUpdate = new ProcessSummaryLine(Status.INFO)
|
private ProcessSummaryLine willNotUpdate = new ProcessSummaryLine(Status.INFO)
|
||||||
.withMessageSuffix("because this process is not configured to update records.")
|
.withMessageSuffix("because this process is not configured to update records.")
|
||||||
.withSingularFutureMessage("will not be updated ")
|
.withSingularFutureMessage("will not be updated ")
|
||||||
.withPluralFutureMessage("will not be updated ")
|
.withPluralFutureMessage("will not be updated ")
|
||||||
.withSingularPastMessage("was not updated ")
|
.withSingularPastMessage("was not updated ")
|
||||||
.withPluralPastMessage("were not updated ");
|
.withPluralPastMessage("were not updated ");
|
||||||
|
|
||||||
protected ProcessSummaryLine errorMissingKeyField = new ProcessSummaryLine(Status.ERROR)
|
private ProcessSummaryLine errorMissingKeyField = new ProcessSummaryLine(Status.ERROR)
|
||||||
.withMessageSuffix("missing a value for the key field.")
|
.withMessageSuffix("missing a value for the key field.")
|
||||||
.withSingularFutureMessage("will not be synced, because it is ")
|
.withSingularFutureMessage("will not be synced, because it is ")
|
||||||
.withPluralFutureMessage("will not be synced, because they are ")
|
.withPluralFutureMessage("will not be synced, because they are ")
|
||||||
.withSingularPastMessage("was not synced, because it is ")
|
.withSingularPastMessage("was not synced, because it is ")
|
||||||
.withPluralPastMessage("were not synced, because they are ");
|
.withPluralPastMessage("were not synced, because they are ");
|
||||||
|
|
||||||
protected ProcessSummaryLine unspecifiedError = new ProcessSummaryLine(Status.ERROR)
|
private ProcessSummaryLine unspecifiedError = new ProcessSummaryLine(Status.ERROR)
|
||||||
.withMessageSuffix("of an unexpected error: ")
|
.withMessageSuffix("of an unexpected error: ")
|
||||||
.withSingularFutureMessage("will not be synced, ")
|
.withSingularFutureMessage("will not be synced, ")
|
||||||
.withPluralFutureMessage("will not be synced, ")
|
.withPluralFutureMessage("will not be synced, ")
|
||||||
@ -109,10 +109,7 @@ public abstract class AbstractTableSyncTransformStep extends AbstractTransformSt
|
|||||||
protected RunBackendStepOutput runBackendStepOutput = null;
|
protected RunBackendStepOutput runBackendStepOutput = null;
|
||||||
protected RecordLookupHelper recordLookupHelper = null;
|
protected RecordLookupHelper recordLookupHelper = null;
|
||||||
|
|
||||||
protected QPossibleValueTranslator possibleValueTranslator;
|
private QPossibleValueTranslator possibleValueTranslator;
|
||||||
|
|
||||||
protected static final String SYNC_TABLE_PERFORM_INSERTS_KEY = "syncTablePerformInsertsKey";
|
|
||||||
protected static final String SYNC_TABLE_PERFORM_UPDATES_KEY = "syncTablePerformUpdatesKey";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -196,26 +193,6 @@ public abstract class AbstractTableSyncTransformStep extends AbstractTransformSt
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
void setPerformInserts(boolean performInserts)
|
|
||||||
{
|
|
||||||
this.setPerformInserts(performInserts);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
void setPerformUpdates(boolean performUpdates)
|
|
||||||
{
|
|
||||||
this.setPerformUpdates(performUpdates);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** artificial method, here to make jacoco see that this class is indeed
|
** artificial method, here to make jacoco see that this class is indeed
|
||||||
** included in test coverage...
|
** included in test coverage...
|
||||||
@ -245,18 +222,6 @@ public abstract class AbstractTableSyncTransformStep extends AbstractTransformSt
|
|||||||
|
|
||||||
SyncProcessConfig config = getSyncProcessConfig();
|
SyncProcessConfig config = getSyncProcessConfig();
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
// see if these fields have been updated via input fields //
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
if(runBackendStepInput.getValueString(SYNC_TABLE_PERFORM_INSERTS_KEY) != null)
|
|
||||||
{
|
|
||||||
config.setPerformInserts(Boolean.parseBoolean(runBackendStepInput.getValueString(SYNC_TABLE_PERFORM_INSERTS_KEY)));
|
|
||||||
}
|
|
||||||
if(runBackendStepInput.getValueString(SYNC_TABLE_PERFORM_UPDATES_KEY) != null)
|
|
||||||
{
|
|
||||||
config.setPerformUpdates(Boolean.parseBoolean(runBackendStepInput.getValueString(SYNC_TABLE_PERFORM_UPDATES_KEY)));
|
|
||||||
}
|
|
||||||
|
|
||||||
String sourceTableKeyField = config.sourceTableKeyField;
|
String sourceTableKeyField = config.sourceTableKeyField;
|
||||||
String destinationTableForeignKeyField = config.destinationTableForeignKey;
|
String destinationTableForeignKeyField = config.destinationTableForeignKey;
|
||||||
String destinationTableName = config.destinationTable;
|
String destinationTableName = config.destinationTable;
|
||||||
@ -400,16 +365,15 @@ public abstract class AbstractTableSyncTransformStep extends AbstractTransformSt
|
|||||||
{
|
{
|
||||||
if(possibleValueTranslator == null)
|
if(possibleValueTranslator == null)
|
||||||
{
|
{
|
||||||
possibleValueTranslator = new QPossibleValueTranslator(QContext.getQInstance(), QContext.getQSession());
|
possibleValueTranslator = new QPossibleValueTranslator(runBackendStepInput.getInstance(), runBackendStepInput.getSession());
|
||||||
}
|
}
|
||||||
|
|
||||||
possibleValueTranslator.translatePossibleValuesInRecords(QContext.getQInstance().getTable(destinationTableName), runBackendStepOutput.getRecords());
|
possibleValueTranslator.translatePossibleValuesInRecords(runBackendStepInput.getInstance().getTable(destinationTableName), runBackendStepOutput.getRecords());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Given a source record, extract what we'll use as its key from it.
|
** Given a source record, extract what we'll use as its key from it.
|
||||||
**
|
**
|
||||||
|
@ -433,8 +433,6 @@ public class ProcessLockUtils
|
|||||||
{
|
{
|
||||||
throw (new QException("Error deleting processLock record: " + deleteOutput.getRecordsWithErrors().get(0).getErrorsAsString()));
|
throw (new QException("Error deleting processLock record: " + deleteOutput.getRecordsWithErrors().get(0).getErrorsAsString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG.info("Released process lock", logPair("id", processLock.getId()), logPair("key", processLock.getKey()), logPair("typeId", processLock.getProcessLockTypeId()), logPair("details", processLock.getDetails()));
|
|
||||||
}
|
}
|
||||||
catch(QException e)
|
catch(QException e)
|
||||||
{
|
{
|
||||||
|
@ -32,7 +32,6 @@ import java.util.function.Consumer;
|
|||||||
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.GetAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||||
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.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.actions.processes.RunBackendStepInput;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||||
@ -516,7 +515,7 @@ public class GeneralProcessUtils
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public static Integer validateSingleSelectedId(RunBackendStepInput runBackendStepInput, String tableName) throws QException
|
public static Integer validateSingleSelectedId(RunBackendStepInput runBackendStepInput, String tableName) throws QException
|
||||||
{
|
{
|
||||||
String tableLabel = QContext.getQInstance().getTable(tableName).getLabel();
|
String tableLabel = runBackendStepInput.getInstance().getTable(tableName).getLabel();
|
||||||
|
|
||||||
////////////////////////////////////////////////////
|
////////////////////////////////////////////////////
|
||||||
// Get the selected recordId and verify we only 1 //
|
// Get the selected recordId and verify we only 1 //
|
||||||
|
@ -70,9 +70,7 @@ public class QuartzJobRunner implements Job
|
|||||||
QContext.init(qInstance, quartzScheduler.getSessionSupplier().get());
|
QContext.init(qInstance, quartzScheduler.getSessionSupplier().get());
|
||||||
|
|
||||||
schedulableType = qInstance.getSchedulableType(context.getJobDetail().getJobDataMap().getString("type"));
|
schedulableType = qInstance.getSchedulableType(context.getJobDetail().getJobDataMap().getString("type"));
|
||||||
@SuppressWarnings("unchecked")
|
params = (Map<String, Object>) context.getJobDetail().getJobDataMap().get("params");
|
||||||
Map<String, Object> paramsFromJobDataMap = (Map<String, Object>) context.getJobDetail().getJobDataMap().get("params");
|
|
||||||
params = paramsFromJobDataMap;
|
|
||||||
|
|
||||||
SchedulableRunner schedulableRunner = QCodeLoader.getAdHoc(SchedulableRunner.class, schedulableType.getRunner());
|
SchedulableRunner schedulableRunner = QCodeLoader.getAdHoc(SchedulableRunner.class, schedulableType.getRunner());
|
||||||
|
|
||||||
|
@ -74,9 +74,7 @@ public class SchedulableProcessRunner implements SchedulableRunner
|
|||||||
Map<String, Serializable> backendVariantData = null;
|
Map<String, Serializable> backendVariantData = null;
|
||||||
if(params.containsKey("backendVariantData"))
|
if(params.containsKey("backendVariantData"))
|
||||||
{
|
{
|
||||||
@SuppressWarnings("unchecked")
|
backendVariantData = (Map<String, Serializable>) params.get("backendVariantData");
|
||||||
Map<String, Serializable> dataFromMap = (Map<String, Serializable>) params.get("backendVariantData");
|
|
||||||
backendVariantData = dataFromMap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Serializable> processInputValues = buildProcessInputValuesMap(params, process);
|
Map<String, Serializable> processInputValues = buildProcessInputValuesMap(params, process);
|
||||||
|
@ -26,7 +26,6 @@ import java.io.File;
|
|||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.NoSuchFileException;
|
import java.nio.file.NoSuchFileException;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@ -79,7 +78,7 @@ public class TempFileStateProvider implements StateProviderInterface
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
String json = JsonUtils.toJson(data);
|
String json = JsonUtils.toJson(data);
|
||||||
FileUtils.writeStringToFile(getFile(key), json, StandardCharsets.UTF_8);
|
FileUtils.writeStringToFile(getFile(key), json);
|
||||||
}
|
}
|
||||||
catch(IOException e)
|
catch(IOException e)
|
||||||
{
|
{
|
||||||
@ -98,7 +97,7 @@ public class TempFileStateProvider implements StateProviderInterface
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
String json = FileUtils.readFileToString(getFile(key), StandardCharsets.UTF_8);
|
String json = FileUtils.readFileToString(getFile(key));
|
||||||
return (Optional.of(JsonUtils.toObject(json, type)));
|
return (Optional.of(JsonUtils.toObject(json, type)));
|
||||||
}
|
}
|
||||||
catch(FileNotFoundException | NoSuchFileException fnfe)
|
catch(FileNotFoundException | NoSuchFileException fnfe)
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user