mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-21 14:38:43 +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="allowSamelineMultipleAnnotations" value="true"/>
|
||||
</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">
|
||||
<property name="allowMissingParamTags" value="true"/>
|
||||
<property name="allowMissingReturnTag" value="true"/>
|
||||
@ -221,14 +233,23 @@
|
||||
<module name="MissingJavadocMethod">
|
||||
<property name="scope" value="private"/>
|
||||
</module>
|
||||
<module name="MissingJavadocType">
|
||||
<property name="scope" value="private"/>
|
||||
</module>
|
||||
<module name="MethodName">
|
||||
<property name="format" value="^[a-z][a-zA-Z0-9_]*$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Method name ''{0}'' must match pattern ''{1}''."/>
|
||||
</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">
|
||||
<property name="exceptionVariableName" value="expected"/>
|
||||
</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.
|
||||
* `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.
|
||||
* `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.
|
||||
** 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.
|
||||
@ -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.
|
||||
* `queryJoins` - *List of <<QueryJoin>> objects* - Optional list of tables to be joined with the main table being queried.
|
||||
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
|
||||
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 specify a different *booleanOperator*.
|
||||
** 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]
|
||||
----
|
||||
|
6
pom.xml
6
pom.xml
@ -46,11 +46,12 @@
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<revision>0.23.0-SNAPSHOT</revision>
|
||||
<revision>0.21.0-SNAPSHOT</revision>
|
||||
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<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.showWarnings>true</maven.compiler.showWarnings>
|
||||
<coverage.haltOnFailure>true</coverage.haltOnFailure>
|
||||
@ -167,7 +168,6 @@
|
||||
<violationSeverity>warning</violationSeverity>
|
||||
<excludes>**/target/generated-sources/*.*</excludes>
|
||||
<!-- <linkXRef>false</linkXRef> -->
|
||||
<includeTestSourceDirectory>true</includeTestSourceDirectory>
|
||||
</configuration>
|
||||
<goals>
|
||||
<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...
|
||||
*******************************************************************************/
|
||||
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()))
|
||||
{
|
||||
///////////////////////////////////////////////////////
|
||||
// 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()));
|
||||
throw (new QException("Missing securityKeyValue [" + recordSecurityLock.getSecurityKeyType() + "] in audit request for table " + auditSingleInput.getAuditTableName()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -278,7 +272,7 @@ public class AuditAction extends AbstractQActionFunction<AuditInput, AuditOutput
|
||||
List<QRecord> auditDetailRecords = new ArrayList<>();
|
||||
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)
|
||||
{
|
||||
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) {}
|
||||
|
||||
}
|
||||
|
@ -55,10 +55,10 @@ public abstract class AbstractPreInsertCustomizer implements TableCustomizerInte
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** 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
|
||||
***************************************************************************/
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 //
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
public enum WhenToRun
|
||||
{
|
||||
BEFORE_ALL_VALIDATIONS,
|
||||
|
@ -28,7 +28,6 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
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.QRuntimeException;
|
||||
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 enum RelationshipType
|
||||
{
|
||||
PARENT_POINTS_AT_CHILD,
|
||||
@ -101,7 +97,7 @@ public abstract class ChildInserterPostInsertCustomizer extends AbstractPostInse
|
||||
List<QRecord> rs = records;
|
||||
List<QRecord> childrenToInsert = new ArrayList<>();
|
||||
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 //
|
||||
|
@ -24,12 +24,10 @@ package com.kingsrook.qqq.backend.core.actions.customizers;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
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.metadata.fields.QFieldMetaData;
|
||||
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 com.kingsrook.qqq.backend.core.actions.values.QValueFormatter;
|
||||
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.model.actions.tables.query.QCriteriaOperator;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||
@ -103,7 +102,7 @@ public abstract class AbstractWidgetRenderer
|
||||
String possibleValueSourceName = dropdownData.getPossibleValueSourceName();
|
||||
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, //
|
||||
|
@ -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.QueryAction;
|
||||
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.QNotFoundException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
@ -182,10 +181,10 @@ public class ChildRecordListRenderer extends AbstractWidgetRenderer
|
||||
{
|
||||
String widgetLabel = input.getQueryParams().get("widgetLabel");
|
||||
String joinName = input.getQueryParams().get("joinName");
|
||||
QJoinMetaData join = QContext.getQInstance().getJoin(joinName);
|
||||
QJoinMetaData join = input.getInstance().getJoin(joinName);
|
||||
String id = input.getQueryParams().get("id");
|
||||
QTableMetaData leftTable = QContext.getQInstance().getTable(join.getLeftTable());
|
||||
QTableMetaData rightTable = QContext.getQInstance().getTable(join.getRightTable());
|
||||
QTableMetaData leftTable = input.getInstance().getTable(join.getLeftTable());
|
||||
QTableMetaData rightTable = input.getInstance().getTable(join.getRightTable());
|
||||
|
||||
Integer maxRows = null;
|
||||
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()));
|
||||
|
||||
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();
|
||||
if(widgetValues.containsKey("disabledFieldsForNewChildRecords"))
|
||||
{
|
||||
@SuppressWarnings("unchecked")
|
||||
Set<String> disabledFieldsForNewChildRecords = (Set<String>) widgetValues.get("disabledFieldsForNewChildRecords");
|
||||
widgetData.setDisabledFieldsForNewChildRecords(disabledFieldsForNewChildRecords);
|
||||
widgetData.setDisabledFieldsForNewChildRecords((Set<String>) widgetValues.get("disabledFieldsForNewChildRecords"));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -24,7 +24,6 @@ package com.kingsrook.qqq.backend.core.actions.dashboard.widgets;
|
||||
|
||||
import java.util.HashMap;
|
||||
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.model.actions.widgets.RenderWidgetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetOutput;
|
||||
@ -58,7 +57,7 @@ public class ProcessWidgetRenderer extends AbstractWidgetRenderer
|
||||
setupDropdowns(input, widgetMetaData, data);
|
||||
|
||||
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.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.permissions.PermissionCheckResult;
|
||||
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.model.actions.metadata.MetaDataInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataOutput;
|
||||
@ -73,7 +72,7 @@ public class MetaDataAction
|
||||
// map tables to frontend metadata //
|
||||
/////////////////////////////////////
|
||||
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();
|
||||
QTableMetaData table = entry.getValue();
|
||||
@ -84,7 +83,7 @@ public class MetaDataAction
|
||||
continue;
|
||||
}
|
||||
|
||||
QBackendMetaData backendForTable = QContext.getQInstance().getBackendForTable(tableName);
|
||||
QBackendMetaData backendForTable = metaDataInput.getInstance().getBackendForTable(tableName);
|
||||
tables.put(tableName, new QFrontendTableMetaData(metaDataInput, backendForTable, table, false, false));
|
||||
treeNodes.put(tableName, new AppTreeNode(table));
|
||||
}
|
||||
@ -97,7 +96,7 @@ public class MetaDataAction
|
||||
// map processes to frontend metadata //
|
||||
////////////////////////////////////////
|
||||
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();
|
||||
QProcessMetaData process = entry.getValue();
|
||||
@ -117,7 +116,7 @@ public class MetaDataAction
|
||||
// map reports to frontend metadata //
|
||||
//////////////////////////////////////
|
||||
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();
|
||||
QReportMetaData report = entry.getValue();
|
||||
@ -137,7 +136,7 @@ public class MetaDataAction
|
||||
// map widgets to frontend metadata //
|
||||
//////////////////////////////////////
|
||||
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();
|
||||
QWidgetMetaDataInterface widget = entry.getValue();
|
||||
@ -155,7 +154,7 @@ public class MetaDataAction
|
||||
///////////////////////////////////////////////////////
|
||||
// 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())
|
||||
.thenComparing((QAppMetaData a) -> a.getLabel()))
|
||||
.toList();
|
||||
@ -212,14 +211,14 @@ public class MetaDataAction
|
||||
////////////////////////////////////
|
||||
// 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?
|
||||
|
||||
|
@ -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.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException;
|
||||
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?
|
||||
ProcessMetaDataOutput processMetaDataOutput = new ProcessMetaDataOutput();
|
||||
|
||||
QProcessMetaData process = QContext.getQInstance().getProcess(processMetaDataInput.getProcessName());
|
||||
QProcessMetaData process = processMetaDataInput.getInstance().getProcess(processMetaDataInput.getProcessName());
|
||||
if(process == null)
|
||||
{
|
||||
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.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException;
|
||||
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?
|
||||
TableMetaDataOutput tableMetaDataOutput = new TableMetaDataOutput();
|
||||
|
||||
QTableMetaData table = QContext.getQInstance().getTable(tableMetaDataInput.getTableName());
|
||||
QTableMetaData table = tableMetaDataInput.getInstance().getTable(tableMetaDataInput.getTableName());
|
||||
if(table == null)
|
||||
{
|
||||
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));
|
||||
|
||||
// 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.session.QSession;
|
||||
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);
|
||||
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());
|
||||
}
|
||||
|
||||
@ -191,14 +184,7 @@ public class PermissionsHelper
|
||||
*******************************************************************************/
|
||||
public static void checkProcessPermissionThrowing(AbstractActionInput actionInput, String processName, Map<String, Serializable> processValues) throws QPermissionDeniedException
|
||||
{
|
||||
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."));
|
||||
}
|
||||
|
||||
QProcessMetaData process = QContext.getQInstance().getProcess(processName);
|
||||
QPermissionRules effectivePermissionRules = getEffectivePermissionRules(process, QContext.getQInstance());
|
||||
|
||||
if(effectivePermissionRules.getCustomPermissionChecker() != null)
|
||||
@ -240,13 +226,6 @@ public class PermissionsHelper
|
||||
public static void checkAppPermissionThrowing(AbstractActionInput actionInput, String appName) throws QPermissionDeniedException
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
@ -276,13 +255,6 @@ public class PermissionsHelper
|
||||
public static void checkReportPermissionThrowing(AbstractActionInput actionInput, String reportName) throws QPermissionDeniedException
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
@ -312,13 +284,6 @@ public class PermissionsHelper
|
||||
public static void checkWidgetPermissionThrowing(AbstractActionInput actionInput, String widgetName) throws QPermissionDeniedException
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,6 @@ package com.kingsrook.qqq.backend.core.actions.processes;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
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.QException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
@ -55,7 +54,7 @@ public class CancelProcessAction extends RunProcessAction
|
||||
{
|
||||
ActionHelper.validateSession(runProcessInput);
|
||||
|
||||
QProcessMetaData process = QContext.getQInstance().getProcess(runProcessInput.getProcessName());
|
||||
QProcessMetaData process = runProcessInput.getInstance().getProcess(runProcessInput.getProcessName());
|
||||
if(process == null)
|
||||
{
|
||||
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 com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||
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.QUserFacingException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
@ -65,7 +64,7 @@ public class RunBackendStepAction
|
||||
{
|
||||
ActionHelper.validateSession(runBackendStepInput);
|
||||
|
||||
QProcessMetaData process = QContext.getQInstance().getProcess(runBackendStepInput.getProcessName());
|
||||
QProcessMetaData process = runBackendStepInput.getInstance().getProcess(runBackendStepInput.getProcessName());
|
||||
if(process == null)
|
||||
{
|
||||
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);
|
||||
|
||||
public static final String BASEPULL_KEY_VALUE = "basepullKeyValue";
|
||||
public static final String BASEPULL_THIS_RUNTIME_KEY = "basepullThisRuntimeKey";
|
||||
public static final String BASEPULL_LAST_RUNTIME_KEY = "basepullLastRuntimeKey";
|
||||
public static final String BASEPULL_TIMESTAMP_FIELD = "basepullTimestampField";
|
||||
@ -100,7 +99,7 @@ public class RunProcessAction
|
||||
{
|
||||
ActionHelper.validateSession(runProcessInput);
|
||||
|
||||
QProcessMetaData process = QContext.getQInstance().getProcess(runProcessInput.getProcessName());
|
||||
QProcessMetaData process = runProcessInput.getInstance().getProcess(runProcessInput.getProcessName());
|
||||
if(process == null)
|
||||
{
|
||||
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();
|
||||
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 //
|
||||
@ -556,7 +551,7 @@ public class RunProcessAction
|
||||
String basepullTableName = basepullConfiguration.getTableName();
|
||||
String basepullKeyFieldName = basepullConfiguration.getKeyField();
|
||||
String basepullLastRunTimeFieldName = basepullConfiguration.getLastRunTimeFieldName();
|
||||
String basepullKeyValue = determineBasepullKeyValue(process, runProcessInput, basepullConfiguration);
|
||||
String basepullKeyValue = determineBasepullKeyValue(process, basepullConfiguration);
|
||||
|
||||
///////////////////////////////////////
|
||||
// get the stored basepull timestamp //
|
||||
@ -636,7 +631,7 @@ public class RunProcessAction
|
||||
String basepullKeyFieldName = basepullConfiguration.getKeyField();
|
||||
String basepullLastRunTimeFieldName = basepullConfiguration.getLastRunTimeFieldName();
|
||||
Integer basepullHoursBackForInitialTimestamp = basepullConfiguration.getHoursBackForInitialTimestamp();
|
||||
String basepullKeyValue = determineBasepullKeyValue(process, runProcessInput, basepullConfiguration);
|
||||
String basepullKeyValue = determineBasepullKeyValue(process, basepullConfiguration);
|
||||
|
||||
///////////////////////////////////////
|
||||
// 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.customizers.QCodeLoader;
|
||||
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.tables.CountAction;
|
||||
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.count.CountInput;
|
||||
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.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.QueryJoin;
|
||||
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;
|
||||
@ -305,19 +301,10 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
|
||||
JoinsContext joinsContext = 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()))
|
||||
{
|
||||
QQueryFilter queryFilter = dataSource.getQueryFilter() == null ? new QQueryFilter() : dataSource.getQueryFilter().clone();
|
||||
joinsContext = new JoinsContext(QContext.getQInstance(), dataSource.getSourceTable(), dataSource.getQueryJoins(), queryFilter);
|
||||
joinsContext = new JoinsContext(exportInput.getInstance(), dataSource.getSourceTable(), dataSource.getQueryJoins(), dataSource.getQueryFilter());
|
||||
countDataSourceRecords(reportInput, dataSource, reportFormat);
|
||||
}
|
||||
}
|
||||
|
||||
@ -341,7 +328,6 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
|
||||
field.setName(column.getName());
|
||||
if(StringUtils.hasContent(column.getLabel()))
|
||||
{
|
||||
|
||||
field.setLabel(column.getLabel());
|
||||
}
|
||||
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
|
||||
{
|
||||
Integer count = null;
|
||||
if(dataSource.getCustomRecordSource() != null)
|
||||
QQueryFilter queryFilter = dataSource.getQueryFilter() == null ? new QQueryFilter() : dataSource.getQueryFilter().clone();
|
||||
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?
|
||||
}
|
||||
else if(StringUtils.hasContent(dataSource.getSourceTable()))
|
||||
{
|
||||
QQueryFilter queryFilter = dataSource.getQueryFilter() == null ? new QQueryFilter() : dataSource.getQueryFilter().clone();
|
||||
setInputValuesInQueryFilter(reportInput, queryFilter);
|
||||
countByDataSource.put(dataSource.getName(), countOutput.getCount());
|
||||
|
||||
CountInput countInput = new CountInput();
|
||||
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())
|
||||
if(reportFormat.getMaxRows() != null && countOutput.getCount() > reportFormat.getMaxRows())
|
||||
{
|
||||
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 + ")."));
|
||||
}
|
||||
}
|
||||
@ -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(), ""));
|
||||
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);
|
||||
new AsyncRecordPipeLoop().run("Report[" + reportInput.getReportName() + "]", null, recordPipe, (callback) ->
|
||||
{
|
||||
if(dataSource.getCustomRecordSource() != null)
|
||||
{
|
||||
ReportCustomRecordSourceInterface recordSource = QCodeLoader.getAdHoc(ReportCustomRecordSourceInterface.class, dataSource.getCustomRecordSource());
|
||||
recordSource.execute(reportInput, dataSource, recordPipe);
|
||||
return (true);
|
||||
}
|
||||
else if(dataSource.getSourceTable() != null)
|
||||
if(dataSource.getSourceTable() != null)
|
||||
{
|
||||
QQueryFilter queryFilter = dataSource.getQueryFilter() == null ? new QQueryFilter() : dataSource.getQueryFilter().clone();
|
||||
setInputValuesInQueryFilter(reportInput, queryFilter);
|
||||
@ -467,12 +417,12 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
|
||||
queryInput.setRecordPipe(recordPipe);
|
||||
queryInput.setTableName(dataSource.getSourceTable());
|
||||
queryInput.setFilter(queryFilter);
|
||||
queryInput.setQueryJoins(cloneDataSourceQueryJoins(dataSource));
|
||||
queryInput.setQueryJoins(dataSource.getQueryJoins());
|
||||
queryInput.withQueryHint(QueryHint.POTENTIALLY_LARGE_NUMBER_OF_RESULTS);
|
||||
queryInput.withQueryHint(QueryHint.MAY_USE_READ_ONLY_BACKEND);
|
||||
|
||||
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)
|
||||
{
|
||||
@ -524,7 +474,7 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
|
||||
}
|
||||
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<>();
|
||||
|
||||
@ -616,56 +566,7 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
|
||||
return;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// 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;
|
||||
}
|
||||
queryFilter.interpretValues(reportInput.getInputValues());
|
||||
}
|
||||
|
||||
|
||||
@ -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 //
|
||||
@ -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<>());
|
||||
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... //
|
||||
@ -805,7 +706,7 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
for(String fieldName : record.getValues().keySet())
|
||||
{
|
||||
QFieldMetaData field;
|
||||
QFieldMetaData field = null;
|
||||
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();
|
||||
for(QReportView view : reportViews)
|
||||
{
|
||||
QReportDataSource dataSource = getDataSource(view.getDataSourceName());
|
||||
if(dataSource == null)
|
||||
{
|
||||
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);
|
||||
QReportDataSource dataSource = getDataSource(view.getDataSourceName());
|
||||
QTableMetaData table = reportInput.getInstance().getTable(dataSource.getSourceTable());
|
||||
SummaryOutput summaryOutput = computeSummaryRowsForView(reportInput, view, table);
|
||||
|
||||
ExportInput exportInput = new ExportInput();
|
||||
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();
|
||||
variableInterpreter.addValueMap("input", reportInput.getInputValues());
|
||||
variableInterpreter.addValueMap("total", getSummaryValuesForInterpreter(totalAggregates));
|
||||
@ -1044,7 +941,10 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
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);
|
||||
totalRow.setValue(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));
|
||||
}
|
||||
|
||||
title = QValueFormatter.formatStringWithValues(view.getTitleFormat(), titleValues);
|
||||
title = new QValueFormatter().formatStringWithValues(view.getTitleFormat(), titleValues);
|
||||
}
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
appName = instance.getBranding().getCompanyName();
|
||||
|
@ -124,11 +124,10 @@ public class ExcelPoiBasedStreamingExportStreamer implements ExportStreamerInter
|
||||
private Writer activeSheetWriter = null;
|
||||
private StreamedSheetWriter sheetWriter = null;
|
||||
|
||||
private QReportView currentView = null;
|
||||
private Map<String, List<QFieldMetaData>> fieldsPerView = new HashMap<>();
|
||||
private Map<String, Integer> rowsPerView = new HashMap<>();
|
||||
private Map<String, String> labelViewsByName = new HashMap<>();
|
||||
private Map<String, String> sheetReferenceByViewName = new HashMap<>();
|
||||
private QReportView currentView = null;
|
||||
private Map<String, List<QFieldMetaData>> fieldsPerView = new HashMap<>();
|
||||
private Map<String, Integer> rowsPerView = new HashMap<>();
|
||||
private Map<String, String> labelViewsByName = new HashMap<>();
|
||||
|
||||
|
||||
|
||||
@ -181,7 +180,6 @@ public class ExcelPoiBasedStreamingExportStreamer implements ExportStreamerInter
|
||||
String sheetReference = sheet.getPackagePart().getPartName().getName().substring(1);
|
||||
sheetMapByExcelReference.put(sheetReference, sheet);
|
||||
sheetMapByViewName.put(view.getName(), sheet);
|
||||
sheetReferenceByViewName.put(view.getName(), sheetReference);
|
||||
sheetCounter++;
|
||||
}
|
||||
|
||||
@ -448,7 +446,7 @@ public class ExcelPoiBasedStreamingExportStreamer implements ExportStreamerInter
|
||||
// - with a new output stream writer //
|
||||
// - 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);
|
||||
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);
|
||||
|
||||
value = rs.toString();
|
||||
|
@ -23,7 +23,6 @@ package com.kingsrook.qqq.backend.core.actions.scripts;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
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)
|
||||
.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));
|
||||
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);
|
||||
Object apiRecordList = qRecordListToApiRecordList.invoke(null, input.getRecordList(), input.getTableName(), scriptRevision.getApiName(), scriptRevision.getApiVersion());
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
ArrayList<? extends Serializable> rs = (ArrayList<? extends Serializable>) apiRecordList;
|
||||
return rs;
|
||||
// noinspection unchecked
|
||||
return (ArrayList<? extends Serializable>) apiRecordList;
|
||||
}
|
||||
catch(ClassNotFoundException e)
|
||||
{
|
||||
|
@ -352,7 +352,7 @@ public class GetAction
|
||||
{
|
||||
if(qPossibleValueTranslator == null)
|
||||
{
|
||||
qPossibleValueTranslator = new QPossibleValueTranslator(QContext.getQInstance(), QContext.getQSession());
|
||||
qPossibleValueTranslator = new QPossibleValueTranslator(getInput.getInstance(), getInput.getSession());
|
||||
}
|
||||
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
|
||||
{
|
||||
if(CollectionUtils.nullSafeIsEmpty(insertInput.getRecords()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QTableMetaData table = insertInput.getTable();
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
@ -246,7 +241,7 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
|
||||
|
||||
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);
|
||||
setErrorsIfUniqueKeyErrors(insertInput, table);
|
||||
|
@ -26,7 +26,6 @@ import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
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.QQueryFilter;
|
||||
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.data.QRecord;
|
||||
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.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ListingHash;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
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"));
|
||||
}
|
||||
|
||||
validateFieldNamesToInclude(queryInput);
|
||||
|
||||
QBackendMetaData backend = queryInput.getBackend();
|
||||
postQueryRecordCustomizer = QCodeLoader.getTableCustomizer(table, TableCustomizers.POST_QUERY_RECORD.getRole());
|
||||
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
|
||||
** 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)
|
||||
{
|
||||
qPossibleValueTranslator = new QPossibleValueTranslator(QContext.getQInstance(), QContext.getQSession());
|
||||
qPossibleValueTranslator = new QPossibleValueTranslator(queryInput.getInstance(), queryInput.getSession());
|
||||
}
|
||||
qPossibleValueTranslator.translatePossibleValuesInRecords(queryInput.getTable(), records, queryInput.getQueryJoins(), queryInput.getFieldsToTranslatePossibleValues());
|
||||
}
|
||||
|
@ -252,7 +252,7 @@ public class UpdateAction
|
||||
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);
|
||||
|
||||
if(oldRecordList.isPresent())
|
||||
|
@ -68,7 +68,7 @@ public class QValueFormatter
|
||||
*******************************************************************************/
|
||||
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)
|
||||
{
|
||||
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),
|
||||
** 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 //
|
||||
@ -107,11 +107,6 @@ public class QValueFormatter
|
||||
return formatBoolean(b);
|
||||
}
|
||||
|
||||
if(QFieldType.BOOLEAN.equals(fieldType))
|
||||
{
|
||||
return formatBoolean(ValueUtils.getValueAsBoolean(value));
|
||||
}
|
||||
|
||||
if(value instanceof LocalTime 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)
|
||||
** 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) //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Serializable> heavyFieldLengths = (Map<String, Serializable>) record.getBackendDetail(QRecord.BACKEND_DETAILS_TYPE_HEAVY_FIELD_LENGTHS);
|
||||
if(heavyFieldLengths != null)
|
||||
{
|
||||
|
@ -30,7 +30,6 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
|
||||
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.logging.QLogger;
|
||||
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
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
QInstance qInstance = input.getInstance();
|
||||
QPossibleValueSource possibleValueSource = qInstance.getPossibleValueSource(input.getPossibleValueSourceName());
|
||||
if(possibleValueSource == null)
|
||||
{
|
||||
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;
|
||||
if(possibleValueSource.getType().equals(QPossibleValueSourceType.ENUM))
|
||||
{
|
||||
@ -200,7 +199,7 @@ public class SearchPossibleValueSourceAction
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(possibleValueSource.getTableName());
|
||||
|
||||
QTableMetaData table = QContext.getQInstance().getTable(possibleValueSource.getTableName());
|
||||
QTableMetaData table = input.getInstance().getTable(possibleValueSource.getTableName());
|
||||
|
||||
QQueryFilter queryFilter = new QQueryFilter();
|
||||
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 //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -269,9 +271,6 @@ public class SearchPossibleValueSourceAction
|
||||
queryFilter = input.getDefaultQueryFilter();
|
||||
}
|
||||
|
||||
// todo - skip & limit as params
|
||||
queryFilter.setLimit(250);
|
||||
|
||||
queryFilter.setOrderBys(possibleValueSource.getOrderByFields());
|
||||
|
||||
queryInput.setFilter(queryFilter);
|
||||
@ -300,7 +299,6 @@ public class SearchPossibleValueSourceAction
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private SearchPossibleValueSourceOutput searchPossibleValueCustom(SearchPossibleValueSourceInput input, QPossibleValueSource possibleValueSource)
|
||||
{
|
||||
try
|
||||
|
@ -22,8 +22,8 @@
|
||||
package com.kingsrook.qqq.backend.core.exceptions;
|
||||
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
|
||||
|
||||
@ -55,11 +55,12 @@ public class QInstanceValidationException extends QException
|
||||
*******************************************************************************/
|
||||
public QInstanceValidationException(List<String> reasons)
|
||||
{
|
||||
super((CollectionUtils.nullSafeHasContents(reasons))
|
||||
? "Instance validation failed for the following reasons:\n - " + StringUtils.join("\n - ", reasons) + "\n(" + reasons.size() + " Total reason" + StringUtils.plural(reasons) + ")"
|
||||
: "Validation failed, but no reasons were provided");
|
||||
super(
|
||||
(reasons != null && reasons.size() > 0)
|
||||
? "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;
|
||||
}
|
||||
@ -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!
|
||||
**
|
||||
|
@ -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.metadata.JoinGraph;
|
||||
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.values.QCustomPossibleValueProvider;
|
||||
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()),
|
||||
"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.");
|
||||
}
|
||||
|
||||
Set<Class<FieldBehavior<?>>> usedFieldBehaviorTypes = new HashSet<>();
|
||||
Set<Class<FieldBehavior<T>>> usedFieldBehaviorTypes = new HashSet<>();
|
||||
if(field.getBehaviors() != null)
|
||||
{
|
||||
for(FieldBehavior<?> fieldBehavior : field.getBehaviors())
|
||||
{
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<FieldBehavior<?>> behaviorClass = (Class<FieldBehavior<?>>) fieldBehavior.getClass();
|
||||
Class<FieldBehavior<T>> behaviorClass = (Class<FieldBehavior<T>>) fieldBehavior.getClass();
|
||||
|
||||
errors.addAll(fieldBehavior.validateBehaviorConfiguration(table, field));
|
||||
|
||||
@ -1661,12 +1659,9 @@ public class QInstanceValidator
|
||||
|
||||
String dataSourceErrorPrefix = "Report " + reportName + " data source " + dataSource.getName() + " ";
|
||||
|
||||
boolean hasASource = false;
|
||||
|
||||
if(StringUtils.hasContent(dataSource.getSourceTable()))
|
||||
{
|
||||
hasASource = true;
|
||||
assertCondition(dataSource.getStaticDataSupplier() == null, dataSourceErrorPrefix + "has both a sourceTable and a staticDataSupplier (not compatible together).");
|
||||
assertCondition(dataSource.getStaticDataSupplier() == null, dataSourceErrorPrefix + "has both a sourceTable and a staticDataSupplier (exactly 1 is required).");
|
||||
if(assertCondition(qInstance.getTable(dataSource.getSourceTable()) != null, dataSourceErrorPrefix + "source table " + dataSource.getSourceTable() + " is not a table in this instance."))
|
||||
{
|
||||
if(dataSource.getQueryFilter() != null)
|
||||
@ -1675,21 +1670,14 @@ public class QInstanceValidator
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(dataSource.getStaticDataSupplier() != null)
|
||||
else 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);
|
||||
}
|
||||
|
||||
if(dataSource.getCustomRecordSource() != null)
|
||||
else
|
||||
{
|
||||
hasASource = true;
|
||||
validateSimpleCodeReference(dataSourceErrorPrefix, dataSource.getCustomRecordSource(), ReportCustomRecordSourceInterface.class);
|
||||
errors.add(dataSourceErrorPrefix + "does not have a sourceTable or a staticDataSupplier (exactly 1 is required).");
|
||||
}
|
||||
|
||||
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.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import com.amazonaws.auth.AWSStaticCredentialsProvider;
|
||||
@ -114,7 +113,7 @@ public class SecretsManagerUtils
|
||||
dotEnv.renameTo(new File(".env.backup-" + System.currentTimeMillis()));
|
||||
}
|
||||
|
||||
FileUtils.writeStringToFile(dotEnv, fullEnv.toString(), StandardCharsets.UTF_8);
|
||||
FileUtils.writeStringToFile(dotEnv, fullEnv.toString());
|
||||
}
|
||||
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.logging.QLogger;
|
||||
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;
|
||||
|
||||
|
||||
@ -92,6 +93,17 @@ public class AbstractActionInput
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@JsonIgnore
|
||||
public QAuthenticationMetaData getAuthenticationMetaData()
|
||||
{
|
||||
return (getInstance().getAuthentication());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for instance
|
||||
**
|
||||
|
@ -152,8 +152,5 @@ public class AuditDetailAccumulator implements Serializable
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
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
|
||||
**
|
||||
|
@ -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();
|
||||
}
|
||||
tmpTable = instance.getTable(aliasToTableNameMap.getOrDefault(securityFieldTableAlias, securityFieldTableAlias));
|
||||
tmpTable = instance.getTable(securityFieldTableAlias);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
QSecurityKeyType securityKeyType = instance.getSecurityKeyType(recordSecurityLock.getSecurityKeyType());
|
||||
boolean haveAllAccessKey = false;
|
||||
QSecurityKeyType securityKeyType = instance.getSecurityKeyType(recordSecurityLock.getSecurityKeyType());
|
||||
boolean haveAllAccessKey = false;
|
||||
if(StringUtils.hasContent(securityKeyType.getAllAccessKeyName()))
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -1118,7 +1118,7 @@ public class JoinsContext
|
||||
if(useExposedJoins)
|
||||
{
|
||||
QTableMetaData mainTable = QContext.getQInstance().getTable(mainTableName);
|
||||
for(ExposedJoin exposedJoin : CollectionUtils.nonNullList(mainTable.getExposedJoins()))
|
||||
for(ExposedJoin exposedJoin : mainTable.getExposedJoins())
|
||||
{
|
||||
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.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
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
|
||||
** QQueryFilter - e.g., if it's one that defined in metaData, and that we don't
|
||||
** 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<>();
|
||||
|
||||
@ -564,9 +522,6 @@ public class QQueryFilter implements Serializable, Cloneable
|
||||
{
|
||||
try
|
||||
{
|
||||
Serializable interpretedValue = value;
|
||||
Exception caughtException = null;
|
||||
|
||||
if(value instanceof AbstractFilterExpression<?>)
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
@ -575,54 +530,17 @@ public class QQueryFilter implements Serializable, Cloneable
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
if(value instanceof FilterVariableExpression filterVariableExpression)
|
||||
{
|
||||
try
|
||||
{
|
||||
interpretedValue = filterVariableExpression.evaluateInputValues(inputValues);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
caughtException = e;
|
||||
interpretedValue = InputNotFound.instance;
|
||||
}
|
||||
newValues.add(filterVariableExpression.evaluateInputValues(inputValues));
|
||||
}
|
||||
}
|
||||
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)
|
||||
else
|
||||
{
|
||||
case REMOVE_FROM_FILTER -> criterion.setOperator(QCriteriaOperator.TRUE);
|
||||
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())));
|
||||
newValues.add(value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
String valueAsString = ValueUtils.getValueAsString(value);
|
||||
Serializable interpretedValue = variableInterpreter.interpretForObject(valueAsString);
|
||||
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
|
||||
*******************************************************************************/
|
||||
@ -775,28 +655,4 @@ public class QQueryFilter implements Serializable, Cloneable
|
||||
{
|
||||
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 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 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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** 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
|
||||
** by users.
|
||||
*******************************************************************************/
|
||||
public class QueryJoin implements Cloneable
|
||||
public class QueryJoin
|
||||
{
|
||||
private String baseTableOrAlias;
|
||||
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.
|
||||
*******************************************************************************/
|
||||
|
@ -25,7 +25,6 @@ package com.kingsrook.qqq.backend.core.model.actions.tables.query.expressions;
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
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
|
||||
{
|
||||
return evaluate(null);
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
|
||||
|
@ -26,7 +26,6 @@ import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
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;
|
||||
|
||||
|
||||
@ -46,7 +45,7 @@ public class FilterVariableExpression extends AbstractFilterExpression<Serializa
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public Serializable evaluate(QFieldMetaData field) throws QException
|
||||
public Serializable evaluate() throws QException
|
||||
{
|
||||
throw (new QUserFacingException("Missing variable value."));
|
||||
}
|
||||
|
@ -22,42 +22,23 @@
|
||||
package com.kingsrook.qqq.backend.core.model.actions.tables.query.expressions;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
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
|
||||
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 (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 + "]"));
|
||||
}
|
||||
return (Instant.now());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,24 +22,19 @@
|
||||
package com.kingsrook.qqq.backend.core.model.actions.tables.query.expressions;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
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 int amount;
|
||||
@ -47,9 +42,6 @@ public class NowWithOffset extends AbstractFilterExpression<Serializable>
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public enum Operator
|
||||
{PLUS, MINUS}
|
||||
|
||||
@ -128,30 +120,7 @@ public class NowWithOffset extends AbstractFilterExpression<Serializable>
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public Serializable evaluate(QFieldMetaData field) 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()
|
||||
public Instant evaluate() throws QException
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
**
|
||||
|
@ -22,35 +22,27 @@
|
||||
package com.kingsrook.qqq.backend.core.model.actions.tables.query.expressions;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.temporal.ChronoField;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
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;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ThisOrLastPeriod extends AbstractFilterExpression<Serializable>
|
||||
public class ThisOrLastPeriod extends AbstractFilterExpression<Instant>
|
||||
{
|
||||
private Operator operator;
|
||||
private ChronoUnit timeUnit;
|
||||
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public enum Operator
|
||||
{THIS, LAST}
|
||||
|
||||
@ -93,7 +85,7 @@ public class ThisOrLastPeriod extends AbstractFilterExpression<Serializable>
|
||||
** Factory
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static ThisOrLastPeriod last(ChronoUnit timeUnit)
|
||||
public static ThisOrLastPeriod last(int amount, ChronoUnit timeUnit)
|
||||
{
|
||||
return (new ThisOrLastPeriod(Operator.LAST, timeUnit));
|
||||
}
|
||||
@ -104,31 +96,7 @@ public class ThisOrLastPeriod extends AbstractFilterExpression<Serializable>
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public Serializable evaluate(QFieldMetaData field) 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()
|
||||
public Instant evaluate() throws QException
|
||||
{
|
||||
ZoneId zoneId = ValueUtils.getSessionOrInstanceZoneId();
|
||||
|
||||
@ -183,57 +151,7 @@ public class ThisOrLastPeriod extends AbstractFilterExpression<Serializable>
|
||||
|
||||
return operator.equals(Operator.THIS) ? startOfThisYear : startOfLastYear;
|
||||
}
|
||||
default -> throw (new QRuntimeException("Unsupported unit: " + 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));
|
||||
default -> throw (new QRuntimeException("Unsupported timeUnit: " + timeUnit));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,11 +79,10 @@ public class QFilterCriteriaDeserializer extends StdDeserializer<QFilterCriteria
|
||||
/////////////////////////////////
|
||||
// get values out of json node //
|
||||
/////////////////////////////////
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Serializable> values = objectMapper.treeToValue(node.get("values"), List.class);
|
||||
String fieldName = objectMapper.treeToValue(node.get("fieldName"), String.class);
|
||||
QCriteriaOperator operator = objectMapper.treeToValue(node.get("operator"), QCriteriaOperator.class);
|
||||
String otherFieldName = objectMapper.treeToValue(node.get("otherFieldName"), String.class);
|
||||
List<Serializable> values = objectMapper.treeToValue(node.get("values"), List.class);
|
||||
String fieldName = objectMapper.treeToValue(node.get("fieldName"), String.class);
|
||||
QCriteriaOperator operator = objectMapper.treeToValue(node.get("operator"), QCriteriaOperator.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) //
|
||||
|
@ -220,7 +220,7 @@ public class AuditsMetaDataProvider
|
||||
.withRecordLabelFormat("%s %s")
|
||||
.withRecordLabelFields("auditTableId", "recordId")
|
||||
.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("auditUserId", QFieldType.INTEGER).withPossibleValueSourceName(TABLE_NAME_AUDIT_USER))
|
||||
.withField(new QFieldMetaData("recordId", QFieldType.INTEGER))
|
||||
@ -243,8 +243,8 @@ public class AuditsMetaDataProvider
|
||||
.withRecordLabelFormat("%s")
|
||||
.withRecordLabelFields("id")
|
||||
.withPrimaryKeyField("id")
|
||||
.withField(new QFieldMetaData("id", QFieldType.LONG))
|
||||
.withField(new QFieldMetaData("auditId", QFieldType.LONG).withPossibleValueSourceName(TABLE_NAME_AUDIT))
|
||||
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||
.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("fieldName", QFieldType.STRING).withMaxLength(100).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 enum AlertType
|
||||
{
|
||||
ERROR,
|
||||
|
@ -40,10 +40,9 @@ public class CompositeWidgetData extends AbstractBlockWidgetData<CompositeWidget
|
||||
{
|
||||
private List<AbstractBlockWidgetData<?, ?, ?, ?>> blocks = new ArrayList<>();
|
||||
|
||||
private Layout layout;
|
||||
private Map<String, Serializable> styleOverrides = new HashMap<>();
|
||||
private String overlayHtml;
|
||||
private Map<String, Serializable> overlayStyleOverrides = new HashMap<>();
|
||||
private Map<String, Serializable> styleOverrides = new HashMap<>();
|
||||
|
||||
private Layout layout;
|
||||
|
||||
|
||||
|
||||
@ -219,91 +218,4 @@ public class CompositeWidgetData extends AbstractBlockWidgetData<CompositeWidget
|
||||
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.Map;
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.CompositeWidgetData;
|
||||
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)
|
||||
{
|
||||
addTooltip(key, value);
|
||||
@ -101,7 +99,6 @@ public abstract class AbstractBlockWidgetData<
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T withTooltip(S key, BlockTooltip value)
|
||||
{
|
||||
addTooltip(key, value);
|
||||
@ -147,7 +144,6 @@ public abstract class AbstractBlockWidgetData<
|
||||
/*******************************************************************************
|
||||
** Fluent setter for tooltipMap
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T withTooltipMap(Map<S, BlockTooltip> tooltipMap)
|
||||
{
|
||||
this.tooltipMap = tooltipMap;
|
||||
@ -182,7 +178,6 @@ public abstract class AbstractBlockWidgetData<
|
||||
** Fluent setter for tooltip
|
||||
**
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T withTooltip(String tooltip)
|
||||
{
|
||||
this.tooltip = new BlockTooltip(tooltip);
|
||||
@ -195,7 +190,6 @@ public abstract class AbstractBlockWidgetData<
|
||||
** Fluent setter for tooltip
|
||||
**
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T withTooltip(BlockTooltip 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)
|
||||
{
|
||||
addLink(key, value);
|
||||
@ -246,7 +226,6 @@ public abstract class AbstractBlockWidgetData<
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T withLink(S key, BlockLink value)
|
||||
{
|
||||
addLink(key, value);
|
||||
@ -292,7 +271,6 @@ public abstract class AbstractBlockWidgetData<
|
||||
/*******************************************************************************
|
||||
** Fluent setter for linkMap
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T withLinkMap(Map<S, BlockLink> linkMap)
|
||||
{
|
||||
this.linkMap = linkMap;
|
||||
@ -327,7 +305,6 @@ public abstract class AbstractBlockWidgetData<
|
||||
** Fluent setter for link
|
||||
**
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T withLink(String link)
|
||||
{
|
||||
this.link = new BlockLink(link);
|
||||
@ -340,7 +317,6 @@ public abstract class AbstractBlockWidgetData<
|
||||
** Fluent setter for link
|
||||
**
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T withLink(BlockLink link)
|
||||
{
|
||||
this.link = link;
|
||||
@ -372,7 +348,6 @@ public abstract class AbstractBlockWidgetData<
|
||||
/*******************************************************************************
|
||||
** Fluent setter for values
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T withValues(V values)
|
||||
{
|
||||
this.values = values;
|
||||
@ -404,7 +379,6 @@ public abstract class AbstractBlockWidgetData<
|
||||
/*******************************************************************************
|
||||
** Fluent setter for styles
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T withStyles(SX styles)
|
||||
{
|
||||
this.styles = styles;
|
||||
@ -412,7 +386,6 @@ public abstract class AbstractBlockWidgetData<
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for blockId
|
||||
*******************************************************************************/
|
||||
@ -436,11 +409,11 @@ public abstract class AbstractBlockWidgetData<
|
||||
/*******************************************************************************
|
||||
** Fluent setter for blockId
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T withBlockId(String blockId)
|
||||
{
|
||||
this.blockId = blockId;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -22,24 +22,17 @@
|
||||
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.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class BlockTooltip
|
||||
{
|
||||
private CompositeWidgetData blockData;
|
||||
private String title;
|
||||
private Placement placement = Placement.BOTTOM;
|
||||
private String title;
|
||||
private Placement placement = Placement.BOTTOM;
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public enum Placement
|
||||
{BOTTOM, LEFT, RIGHT, TOP}
|
||||
|
||||
@ -66,17 +59,6 @@ public class BlockTooltip
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public BlockTooltip(CompositeWidgetData blockData)
|
||||
{
|
||||
this.blockData = blockData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for title
|
||||
*******************************************************************************/
|
||||
@ -137,35 +119,4 @@ public class BlockTooltip
|
||||
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()))
|
||||
{
|
||||
@SuppressWarnings("unchecked")
|
||||
List<? extends QRecordEntity> associatedEntities = (List<? extends QRecordEntity>) qRecordEntityAssociation.getGetter().invoke(this);
|
||||
String associationName = qRecordEntityAssociation.getAssociationAnnotation().name();
|
||||
|
||||
@ -246,7 +245,6 @@ public abstract class QRecordEntity
|
||||
|
||||
for(QRecordEntityAssociation qRecordEntityAssociation : getAssociationList(this.getClass()))
|
||||
{
|
||||
@SuppressWarnings("unchecked")
|
||||
List<? extends QRecordEntity> associatedEntities = (List<? extends QRecordEntity>) qRecordEntityAssociation.getGetter().invoke(this);
|
||||
String associationName = qRecordEntityAssociation.getAssociationAnnotation().name();
|
||||
|
||||
@ -348,7 +346,6 @@ public abstract class QRecordEntity
|
||||
|
||||
if(associationAnnotation.isPresent())
|
||||
{
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends QRecordEntity> listTypeParam = (Class<? extends QRecordEntity>) getListTypeParam(possibleGetter.getReturnType(), possibleGetter.getAnnotatedReturnType());
|
||||
associationList.add(new QRecordEntityAssociation(fieldName, possibleGetter, setter.get(), listTypeParam, associationAnnotation.orElse(null)));
|
||||
}
|
||||
|
@ -39,9 +39,6 @@ public class ParentWidgetMetaData extends QWidgetMetaData
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public enum LayoutType
|
||||
{
|
||||
GRID,
|
||||
|
@ -188,8 +188,7 @@ public class FieldAdornment
|
||||
** Fluent setter for values
|
||||
**
|
||||
*******************************************************************************/
|
||||
@SafeVarargs
|
||||
public final FieldAdornment withValues(Pair<String, Serializable>... values)
|
||||
public FieldAdornment withValues(Pair<String, Serializable>... values)
|
||||
{
|
||||
for(Pair<String, Serializable> value : values)
|
||||
{
|
||||
|
@ -168,11 +168,11 @@ public class QFrontendTableMetaData
|
||||
editPermission = PermissionsHelper.hasTablePermission(actionInput, tableMetaData.getName(), TablePermissionSubType.EDIT);
|
||||
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())
|
||||
{
|
||||
usesVariants = true;
|
||||
variantTableLabel = QContext.getQInstance().getTable(backend.getVariantOptionsTableName()).getLabel();
|
||||
variantTableLabel = actionInput.getInstance().getTable(backend.getVariantOptionsTableName()).getLabel();
|
||||
}
|
||||
|
||||
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.,
|
||||
** leftField = rightField. Used as part of a list in a QJoinMetaData.
|
||||
*******************************************************************************/
|
||||
public class JoinOn implements Cloneable
|
||||
public class JoinOn
|
||||
{
|
||||
private String leftField;
|
||||
private String rightField;
|
||||
@ -131,22 +131,4 @@ public class JoinOn implements Cloneable
|
||||
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.
|
||||
*******************************************************************************/
|
||||
public class QJoinMetaData implements TopLevelMetaDataInterface, Cloneable
|
||||
public class QJoinMetaData implements TopLevelMetaDataInterface
|
||||
{
|
||||
private String name;
|
||||
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
|
||||
**
|
||||
|
@ -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
|
||||
** 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
|
||||
{
|
||||
@ -51,7 +44,6 @@ public class QReportDataSource
|
||||
|
||||
private QCodeReference queryInputCustomizer;
|
||||
private QCodeReference staticDataSupplier;
|
||||
private QCodeReference customRecordSource;
|
||||
|
||||
|
||||
|
||||
@ -273,35 +265,4 @@ public class QReportDataSource
|
||||
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)
|
||||
{
|
||||
this.associatedScripts = new ArrayList<>();
|
||||
this.associatedScripts = new ArrayList();
|
||||
}
|
||||
this.associatedScripts.add(associatedScript);
|
||||
return (this);
|
||||
|
@ -32,9 +32,6 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.UniqueKey;
|
||||
*******************************************************************************/
|
||||
public class CacheUseCase
|
||||
{
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
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.
|
||||
|
@ -247,7 +247,10 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
|
||||
//////////////////////////////////////////////////////////////
|
||||
// allow customizer to do custom things here, if so desired //
|
||||
//////////////////////////////////////////////////////////////
|
||||
finalCustomizeSession(qInstance, qSession);
|
||||
if(getCustomizer() != null)
|
||||
{
|
||||
getCustomizer().finalCustomizeSession(qInstance, qSession);
|
||||
}
|
||||
|
||||
return (qSession);
|
||||
}
|
||||
@ -308,7 +311,10 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
|
||||
//////////////////////////////////////////////////////////////
|
||||
// allow customizer to do custom things here, if so desired //
|
||||
//////////////////////////////////////////////////////////////
|
||||
finalCustomizeSession(qInstance, qSession);
|
||||
if(getCustomizer() != null)
|
||||
{
|
||||
getCustomizer().finalCustomizeSession(qInstance, 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
|
||||
*******************************************************************************/
|
||||
|
@ -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.query.JoinsContext;
|
||||
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.QueryJoin;
|
||||
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();
|
||||
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()))
|
||||
{
|
||||
tableData = buildJoinCrossProduct(input);
|
||||
@ -188,7 +185,7 @@ public class MemoryRecordStore
|
||||
qRecord.setTableName(input.getTableName());
|
||||
}
|
||||
|
||||
boolean recordMatches = BackendQueryFilterUtils.doesRecordMatch(input.getFilter(), joinsContext, qRecord);
|
||||
boolean recordMatches = BackendQueryFilterUtils.doesRecordMatch(input.getFilter(), qRecord);
|
||||
|
||||
if(recordMatches)
|
||||
{
|
||||
@ -227,7 +224,8 @@ public class MemoryRecordStore
|
||||
*******************************************************************************/
|
||||
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<>();
|
||||
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(primaryKeyField.getType().equals(QFieldType.LONG))
|
||||
{
|
||||
recordToInsert.setValue(primaryKeyField.getName(), (nextSerial++).longValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
recordToInsert.setValue(primaryKeyField.getName(), nextSerial++);
|
||||
}
|
||||
recordToInsert.setValue(primaryKeyField.getName(), nextSerial++);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -392,7 +383,7 @@ public class MemoryRecordStore
|
||||
{
|
||||
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? //
|
||||
@ -671,11 +662,7 @@ public class MemoryRecordStore
|
||||
{
|
||||
return (-1);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Comparable<Serializable> comparableSerializableA = (Comparable<Serializable>) a;
|
||||
|
||||
return comparableSerializableA.compareTo(b);
|
||||
return ((Comparable) a).compareTo(b);
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -782,7 +769,6 @@ public class MemoryRecordStore
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private static Serializable computeAggregate(List<QRecord> records, Aggregate aggregate, QTableMetaData table)
|
||||
{
|
||||
String fieldName = aggregate.getFieldName();
|
||||
@ -910,21 +896,4 @@ public class MemoryRecordStore
|
||||
|
||||
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 com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
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.QFilterOrderBy;
|
||||
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.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.ValueUtils;
|
||||
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.
|
||||
******************************************************************************/
|
||||
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())
|
||||
{
|
||||
@ -115,36 +97,7 @@ public class BackendQueryFilterUtils
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// 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);
|
||||
}
|
||||
boolean criterionMatches = doesCriteriaMatch(criterion, fieldName, value);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// 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();
|
||||
while(valueListIterator.hasNext())
|
||||
{
|
||||
@ -204,7 +144,7 @@ public class BackendQueryFilterUtils
|
||||
{
|
||||
try
|
||||
{
|
||||
valueListIterator.set(expression.evaluate(field));
|
||||
valueListIterator.set(expression.evaluate());
|
||||
}
|
||||
catch(QException qe)
|
||||
{
|
||||
|
@ -27,7 +27,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
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.model.actions.processes.ProcessSummaryLine;
|
||||
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);
|
||||
|
||||
QTableMetaData table = QContext.getQInstance().getTable(runBackendStepInput.getTableName());
|
||||
QTableMetaData table = runBackendStepInput.getInstance().getTable(runBackendStepInput.getTableName());
|
||||
if(table != null)
|
||||
{
|
||||
tableLabel = table.getLabel();
|
||||
@ -120,7 +119,7 @@ public class BulkDeleteLoadStep extends LoadViaDeleteStep implements ProcessSumm
|
||||
////////////////////////////
|
||||
super.runOnePage(runBackendStepInput, runBackendStepOutput);
|
||||
|
||||
QTableMetaData table = QContext.getQInstance().getTable(runBackendStepInput.getTableName());
|
||||
QTableMetaData table = runBackendStepInput.getInstance().getTable(runBackendStepInput.getTableName());
|
||||
String primaryKeyFieldName = table.getPrimaryKeyField();
|
||||
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.Optional;
|
||||
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.model.actions.processes.ProcessSummaryLine;
|
||||
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 //
|
||||
///////////////////////////////////////////////////////
|
||||
QTableMetaData table = QContext.getQInstance().getTable(runBackendStepInput.getTableName());
|
||||
QTableMetaData table = runBackendStepInput.getInstance().getTable(runBackendStepInput.getTableName());
|
||||
if(table != null)
|
||||
{
|
||||
tableLabel = table.getLabel();
|
||||
|
@ -25,7 +25,6 @@ package com.kingsrook.qqq.backend.core.processes.implementations.bulk.edit;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
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.model.actions.processes.ProcessSummaryLine;
|
||||
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);
|
||||
|
||||
QTableMetaData table = QContext.getQInstance().getTable(runBackendStepInput.getTableName());
|
||||
QTableMetaData table = runBackendStepInput.getInstance().getTable(runBackendStepInput.getTableName());
|
||||
if(table != null)
|
||||
{
|
||||
tableLabel = table.getLabel();
|
||||
@ -125,7 +124,7 @@ public class BulkEditLoadStep extends LoadViaUpdateStep implements ProcessSummar
|
||||
////////////////////////////////////////////////////////
|
||||
// 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())
|
||||
{
|
||||
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.values.QPossibleValueTranslator;
|
||||
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.model.actions.processes.ProcessSummaryLine;
|
||||
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 //
|
||||
///////////////////////////////////////////////////////
|
||||
table = QContext.getQInstance().getTable(runBackendStepInput.getTableName());
|
||||
table = runBackendStepInput.getInstance().getTable(runBackendStepInput.getTableName());
|
||||
if(table != null)
|
||||
{
|
||||
tableLabel = table.getLabel();
|
||||
@ -231,7 +230,7 @@ public class BulkEditTransformStep extends AbstractTransformStep
|
||||
|
||||
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);
|
||||
if(StringUtils.hasContent(translatedValue))
|
||||
{
|
||||
|
@ -27,7 +27,6 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
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.QUserFacingException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.QUploadedFile;
|
||||
@ -84,7 +83,7 @@ public class BulkInsertExtractStep extends AbstractExtractStep
|
||||
.withLimit(getLimit())
|
||||
.withCsv(new String(bytes))
|
||||
.withDoCorrectValueTypes(true)
|
||||
.withTable(QContext.getQInstance().getTable(tableName))
|
||||
.withTable(runBackendStepInput.getInstance().getTable(tableName))
|
||||
.withMapping(mapping)
|
||||
.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.tables.InsertAction;
|
||||
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.model.actions.processes.ProcessSummaryLine;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLineInterface;
|
||||
@ -105,7 +104,7 @@ public class BulkInsertTransformStep extends AbstractTransformStep
|
||||
@Override
|
||||
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. //
|
||||
@ -122,7 +121,7 @@ public class BulkInsertTransformStep extends AbstractTransformStep
|
||||
public void runOnePage(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
||||
{
|
||||
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 //
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
QTableMetaData table = QContext.getQInstance().getTable(tableName);
|
||||
|
||||
FieldAndQueryJoin fieldAndQueryJoin = getFieldAndQueryJoin(table, fieldName);
|
||||
QFieldMetaData field = fieldAndQueryJoin.field();
|
||||
QueryJoin queryJoin = fieldAndQueryJoin.queryJoin();
|
||||
QueryJoin queryJoin = null;
|
||||
QTableMetaData table = QContext.getQInstance().getTable(tableName);
|
||||
QFieldMetaData field = 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);
|
||||
}
|
||||
|
||||
if(field == null)
|
||||
{
|
||||
@ -195,7 +213,7 @@ public class ColumnStatsStep implements BackendStep
|
||||
filter.withOrderBy(new QFilterOrderByAggregate(aggregate, false));
|
||||
filter.withOrderBy(new QFilterOrderByGroupBy(groupBy));
|
||||
|
||||
Integer limit = 1000;
|
||||
Integer limit = 1000; // too big?
|
||||
AggregateInput aggregateInput = new AggregateInput();
|
||||
aggregateInput.withAggregate(aggregate);
|
||||
aggregateInput.withGroupBy(groupBy);
|
||||
@ -205,11 +223,7 @@ public class ColumnStatsStep implements BackendStep
|
||||
|
||||
if(queryJoin != null)
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// 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());
|
||||
aggregateInput.withQueryJoin(queryJoin);
|
||||
}
|
||||
|
||||
AggregateOutput aggregateOutput = new AggregateAction().execute(aggregateInput);
|
||||
@ -224,7 +238,7 @@ public class ColumnStatsStep implements BackendStep
|
||||
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));
|
||||
}
|
||||
|
||||
@ -512,43 +526,4 @@ public class ColumnStatsStep implements BackendStep
|
||||
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 com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||
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.logging.QLogger;
|
||||
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()));
|
||||
}
|
||||
|
||||
QTableMetaData table = QContext.getQInstance().getTable(tableName);
|
||||
QTableMetaData table = runBackendStepInput.getInstance().getTable(tableName);
|
||||
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.values.QPossibleValueTranslator;
|
||||
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.model.actions.processes.RunBackendStepInput;
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
QValueFormatter qValueFormatter = new QValueFormatter();
|
||||
qValueFormatter.setDisplayValuesInRecords(table, list);
|
||||
|
||||
QPossibleValueTranslator qPossibleValueTranslator = new QPossibleValueTranslator(QContext.getQInstance(), QContext.getQSession());
|
||||
QPossibleValueTranslator qPossibleValueTranslator = new QPossibleValueTranslator(input.getInstance(), input.getSession());
|
||||
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.tables.CountAction;
|
||||
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.logging.QLogger;
|
||||
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 //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
QTableMetaData table = QContext.getQInstance().getTable(runBackendStepInput.getValueString(FIELD_SOURCE_TABLE));
|
||||
QTableMetaData table = runBackendStepInput.getInstance().getTable(runBackendStepInput.getValueString(FIELD_SOURCE_TABLE));
|
||||
if(table == null)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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));
|
||||
}
|
||||
else
|
||||
|
@ -28,7 +28,6 @@ import java.util.Optional;
|
||||
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.UpdateAction;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||
@ -87,7 +86,7 @@ public class LoadViaInsertOrUpdateStep extends AbstractLoadStep
|
||||
*******************************************************************************/
|
||||
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))
|
||||
{
|
||||
@ -140,7 +139,7 @@ public class LoadViaInsertOrUpdateStep extends AbstractLoadStep
|
||||
*******************************************************************************/
|
||||
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<>();
|
||||
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
|
||||
{
|
||||
String sourceTableName = runBackendStepInput.getValueString(StreamedETLWithFrontendProcess.FIELD_SOURCE_TABLE);
|
||||
QTableMetaData sourceTable = QContext.getQInstance().getTable(sourceTableName);
|
||||
QTableMetaData sourceTable = runBackendStepInput.getInstance().getTable(sourceTableName);
|
||||
if(StringUtils.hasContent(sourceTableName))
|
||||
{
|
||||
QBackendMetaData sourceTableBackend = QContext.getQInstance().getBackendForTable(sourceTableName);
|
||||
QBackendMetaData sourceTableBackend = runBackendStepInput.getInstance().getBackendForTable(sourceTableName);
|
||||
if(sourceTable.isCapabilityEnabled(sourceTableBackend, Capability.TABLE_COUNT))
|
||||
{
|
||||
Integer recordCount = extractStep.doCount(runBackendStepInput);
|
||||
|
@ -65,14 +65,9 @@ public class MergeDuplicatesLoadStep extends LoadViaInsertOrUpdateStep
|
||||
{
|
||||
super.runOnePage(runBackendStepInput, runBackendStepOutput);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
ListingHash<String, Serializable> otherTableIdsToDelete = (ListingHash<String, Serializable>) runBackendStepInput.getValue("otherTableIdsToDelete");
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
ListingHash<String, Serializable> otherTableIdsToDelete = (ListingHash<String, Serializable>) runBackendStepInput.getValue("otherTableIdsToDelete");
|
||||
ListingHash<String, QQueryFilter> otherTableFiltersToDelete = (ListingHash<String, QQueryFilter>) runBackendStepInput.getValue("otherTableFiltersToDelete");
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
ListingHash<String, QRecord> otherTableRecordsToStore = (ListingHash<String, QRecord>) runBackendStepInput.getValue("otherTableRecordsToStore");
|
||||
ListingHash<String, QRecord> otherTableRecordsToStore = (ListingHash<String, QRecord>) runBackendStepInput.getValue("otherTableRecordsToStore");
|
||||
|
||||
if(otherTableIdsToDelete != null)
|
||||
{
|
||||
|
@ -46,8 +46,7 @@ public class BasicRunReportProcess
|
||||
public static final String STEP_NAME_EXECUTE = "execute";
|
||||
public static final String STEP_NAME_ACCESS = "accessReport";
|
||||
|
||||
public static final String FIELD_REPORT_NAME = "reportName";
|
||||
public static final String FIELD_REPORT_FORMAT = "reportFormat";
|
||||
public static final String FIELD_REPORT_NAME = "reportName";
|
||||
|
||||
|
||||
|
||||
|
@ -31,9 +31,7 @@ import java.time.format.DateTimeFormatter;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||
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.QUserFacingException;
|
||||
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.ReportDestination;
|
||||
@ -51,7 +49,6 @@ import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
public class ExecuteReportStep implements BackendStep
|
||||
{
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -60,10 +57,9 @@ public class ExecuteReportStep implements BackendStep
|
||||
{
|
||||
try
|
||||
{
|
||||
ReportFormat reportFormat = getReportFormat(runBackendStepInput);
|
||||
String reportName = runBackendStepInput.getValueString("reportName");
|
||||
QReportMetaData report = QContext.getQInstance().getReport(reportName);
|
||||
File tmpFile = File.createTempFile(reportName, "." + reportFormat.getExtension());
|
||||
String reportName = runBackendStepInput.getValueString("reportName");
|
||||
QReportMetaData report = runBackendStepInput.getInstance().getReport(reportName);
|
||||
File tmpFile = File.createTempFile(reportName, ".xlsx", new File("/tmp/"));
|
||||
|
||||
runBackendStepInput.getAsyncJobCallback().updateStatus("Generating Report");
|
||||
|
||||
@ -72,7 +68,7 @@ public class ExecuteReportStep implements BackendStep
|
||||
ReportInput reportInput = new ReportInput();
|
||||
reportInput.setReportName(reportName);
|
||||
reportInput.setReportDestination(new ReportDestination()
|
||||
.withReportFormat(reportFormat)
|
||||
.withReportFormat(ReportFormat.XLSX) // todo - variable
|
||||
.withReportOutputStream(reportOutputStream));
|
||||
|
||||
Map<String, Serializable> values = runBackendStepInput.getValues();
|
||||
@ -82,7 +78,7 @@ public class ExecuteReportStep implements BackendStep
|
||||
|
||||
String downloadFileBaseName = getDownloadFileBaseName(runBackendStepInput, report);
|
||||
|
||||
runBackendStepOutput.addValue("downloadFileName", downloadFileBaseName + "." + reportFormat.getExtension());
|
||||
runBackendStepOutput.addValue("downloadFileName", downloadFileBaseName + ".xlsx");
|
||||
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.Iterator;
|
||||
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.QUserFacingException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||
@ -107,16 +106,15 @@ public class PrepareReportForRecordStep extends PrepareReportStep
|
||||
}
|
||||
|
||||
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", report.getLabel() + " - " + record.getRecordLabel());
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// if there are no more input fields, then remove the INPUT step from the process. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
@SuppressWarnings("unchecked")
|
||||
ArrayList<QFieldMetaData> updatedInputFieldList = (ArrayList<QFieldMetaData>) runBackendStepOutput.getValue("inputFieldList");
|
||||
if(!CollectionUtils.nullSafeHasContents(updatedInputFieldList))
|
||||
inputFieldList = (ArrayList<QFieldMetaData>) runBackendStepOutput.getValue("inputFieldList");
|
||||
if(!CollectionUtils.nullSafeHasContents(inputFieldList))
|
||||
{
|
||||
removeInputStepFromProcess(runBackendStepOutput);
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ package com.kingsrook.qqq.backend.core.processes.implementations.reports;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
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.model.actions.processes.RunBackendStepInput;
|
||||
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."));
|
||||
}
|
||||
|
||||
QReportMetaData report = QContext.getQInstance().getReport(reportName);
|
||||
QReportMetaData report = runBackendStepInput.getInstance().getReport(reportName);
|
||||
if(report == null)
|
||||
{
|
||||
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())
|
||||
.withReportFormat(ReportFormatPossibleValueEnum.valueOf(reportFormat.name()).getPossibleValueId())
|
||||
)).getRecords().get(0);
|
||||
runBackendStepOutput.addValue("renderedReportId", renderedReportRecord.getValue("id"));
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// 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.QueryAction;
|
||||
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.model.actions.processes.RunBackendStepInput;
|
||||
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.QProcessMetaData;
|
||||
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
|
||||
{
|
||||
ActionHelper.validateSession(runBackendStepInput);
|
||||
Integer savedViewId = runBackendStepInput.getValueInteger("id");
|
||||
|
||||
try
|
||||
{
|
||||
Integer savedViewId = runBackendStepInput.getValueInteger("id");
|
||||
if(savedViewId != null)
|
||||
{
|
||||
GetInput input = new GetInput();
|
||||
@ -91,11 +89,6 @@ public class QuerySavedViewProcess implements BackendStep
|
||||
input.setPrimaryKey(savedViewId);
|
||||
|
||||
GetOutput output = new GetAction().execute(input);
|
||||
if(output.getRecord() == null)
|
||||
{
|
||||
throw (new QNotFoundException("The requested view was not found."));
|
||||
}
|
||||
|
||||
runBackendStepOutput.addRecord(output.getRecord());
|
||||
runBackendStepOutput.addValue("savedView", 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());
|
||||
}
|
||||
}
|
||||
catch(QNotFoundException qnfe)
|
||||
{
|
||||
LOG.info("View not found", logPair("savedViewId", savedViewId));
|
||||
throw (qnfe);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn("Error querying for saved views", e);
|
||||
|
@ -145,7 +145,7 @@ public class StoreScriptRevisionProcessStep implements BackendStep
|
||||
|
||||
try
|
||||
{
|
||||
scriptRevision.setValue("author", QContext.getQSession().getUser().getFullName());
|
||||
scriptRevision.setValue("author", input.getSession().getUser().getFullName());
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
|
@ -139,7 +139,7 @@ public class TestScriptProcessStep implements BackendStep
|
||||
//////////////////////////////////
|
||||
// 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());
|
||||
|
||||
if(testScriptOutput.getException() != null)
|
||||
|
@ -72,33 +72,33 @@ import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
*******************************************************************************/
|
||||
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();
|
||||
protected ProcessSummaryLine okToUpdate = StandardProcessSummaryLineProducer.getOkToUpdateLine();
|
||||
private ProcessSummaryLine okToInsert = StandardProcessSummaryLineProducer.getOkToInsertLine();
|
||||
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.")
|
||||
.withSingularFutureMessage("will not be inserted ")
|
||||
.withPluralFutureMessage("will not be inserted ")
|
||||
.withSingularPastMessage("was 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.")
|
||||
.withSingularFutureMessage("will not be updated ")
|
||||
.withPluralFutureMessage("will not be updated ")
|
||||
.withSingularPastMessage("was 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.")
|
||||
.withSingularFutureMessage("will not be synced, because it is ")
|
||||
.withPluralFutureMessage("will not be synced, because they are ")
|
||||
.withSingularPastMessage("was not synced, because it is ")
|
||||
.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: ")
|
||||
.withSingularFutureMessage("will not be synced, ")
|
||||
.withPluralFutureMessage("will not be synced, ")
|
||||
@ -109,10 +109,7 @@ public abstract class AbstractTableSyncTransformStep extends AbstractTransformSt
|
||||
protected RunBackendStepOutput runBackendStepOutput = null;
|
||||
protected RecordLookupHelper recordLookupHelper = null;
|
||||
|
||||
protected QPossibleValueTranslator possibleValueTranslator;
|
||||
|
||||
protected static final String SYNC_TABLE_PERFORM_INSERTS_KEY = "syncTablePerformInsertsKey";
|
||||
protected static final String SYNC_TABLE_PERFORM_UPDATES_KEY = "syncTablePerformUpdatesKey";
|
||||
private QPossibleValueTranslator possibleValueTranslator;
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
** included in test coverage...
|
||||
@ -245,18 +222,6 @@ public abstract class AbstractTableSyncTransformStep extends AbstractTransformSt
|
||||
|
||||
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 destinationTableForeignKeyField = config.destinationTableForeignKey;
|
||||
String destinationTableName = config.destinationTable;
|
||||
@ -400,16 +365,15 @@ public abstract class AbstractTableSyncTransformStep extends AbstractTransformSt
|
||||
{
|
||||
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.
|
||||
**
|
||||
|
@ -433,8 +433,6 @@ public class ProcessLockUtils
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
@ -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.GetAction;
|
||||
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.QUserFacingException;
|
||||
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
|
||||
{
|
||||
String tableLabel = QContext.getQInstance().getTable(tableName).getLabel();
|
||||
String tableLabel = runBackendStepInput.getInstance().getTable(tableName).getLabel();
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// Get the selected recordId and verify we only 1 //
|
||||
|
@ -70,9 +70,7 @@ public class QuartzJobRunner implements Job
|
||||
QContext.init(qInstance, quartzScheduler.getSessionSupplier().get());
|
||||
|
||||
schedulableType = qInstance.getSchedulableType(context.getJobDetail().getJobDataMap().getString("type"));
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> paramsFromJobDataMap = (Map<String, Object>) context.getJobDetail().getJobDataMap().get("params");
|
||||
params = paramsFromJobDataMap;
|
||||
params = (Map<String, Object>) context.getJobDetail().getJobDataMap().get("params");
|
||||
|
||||
SchedulableRunner schedulableRunner = QCodeLoader.getAdHoc(SchedulableRunner.class, schedulableType.getRunner());
|
||||
|
||||
|
@ -74,9 +74,7 @@ public class SchedulableProcessRunner implements SchedulableRunner
|
||||
Map<String, Serializable> backendVariantData = null;
|
||||
if(params.containsKey("backendVariantData"))
|
||||
{
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Serializable> dataFromMap = (Map<String, Serializable>) params.get("backendVariantData");
|
||||
backendVariantData = dataFromMap;
|
||||
backendVariantData = (Map<String, Serializable>) params.get("backendVariantData");
|
||||
}
|
||||
|
||||
Map<String, Serializable> processInputValues = buildProcessInputValuesMap(params, process);
|
||||
|
@ -26,7 +26,6 @@ import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
@ -79,7 +78,7 @@ public class TempFileStateProvider implements StateProviderInterface
|
||||
try
|
||||
{
|
||||
String json = JsonUtils.toJson(data);
|
||||
FileUtils.writeStringToFile(getFile(key), json, StandardCharsets.UTF_8);
|
||||
FileUtils.writeStringToFile(getFile(key), json);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
@ -98,7 +97,7 @@ public class TempFileStateProvider implements StateProviderInterface
|
||||
{
|
||||
try
|
||||
{
|
||||
String json = FileUtils.readFileToString(getFile(key), StandardCharsets.UTF_8);
|
||||
String json = FileUtils.readFileToString(getFile(key));
|
||||
return (Optional.of(JsonUtils.toObject(json, type)));
|
||||
}
|
||||
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