Compare commits

..

42 Commits

Author SHA1 Message Date
6702c06ed0 Merge branch 'rel/0.21.0' 2024-08-23 11:47:47 -05:00
9dfbd839c8 Update versions for release 2024-08-23 11:39:07 -05:00
724d5779cc Merge pull request #127 from Kingsrook/feature/CE-1405-zero-day-ledger-billing
Feature/ce 1405 zero day ledger billing
2024-08-23 11:19:46 -05:00
1fef376e65 Merge pull request #128 from Kingsrook/feature/CE-1556-ops-overview-enhanced-tooltips
Feature/ce 1556 ops overview enhanced tooltips
2024-08-23 11:02:05 -05:00
d3417a0652 CE-1405 Remove usage of SparseQRecord... not clear if we want it or not at this time 2024-08-21 20:09:36 -05:00
053d5f1058 CE-1405 Add getOldRecordMap 2024-08-21 17:01:55 -05:00
47e27d5ffc CE-1554: updates to allow widget block overlays 2024-08-20 18:06:01 -05:00
59a70a4cb7 CE-1405 fix bug with fieldNamesToInclude for tables w/ no selected fields 2024-08-20 09:38:54 -05:00
fea757c46d Merged dev into feature/CE-1405-zero-day-ledger-billing 2024-08-16 16:57:26 -05:00
9a65ea81b2 CE-1405 / CE-1479 - add queryInput.fieldNamesToInclude 2024-08-15 08:53:19 -05:00
494ec00b84 CE-1556: updated to try to use composite block data within tooltips 2024-08-13 17:23:30 -05:00
9b4b61af38 Merged feature/CE-1472-add-extensivewms-orders into dev 2024-08-13 10:14:46 -05:00
f237b5e82d Merged feature/fix-formParam-exceptions-for-plaintext-body-with-percent into dev 2024-08-05 13:36:43 -05:00
207311eb0b Merged feature/qol-improvements-20240801 into dev 2024-08-05 13:36:24 -05:00
ab5af234af Merged feature/checkstyle-updates into dev 2024-08-05 13:35:21 -05:00
9baa7c32bf Add safety around most calls to formParam and/or queryParam, as they can throw if the request isn't formatted as expected, in ways that we may not want it to. 2024-08-02 12:32:36 -05:00
3eae3a5758 re-set queryStat startTimestamp to just before executeQuery, to avoid including time spent aquiring db connection 2024-08-01 15:12:59 -05:00
a11d584c8a Fix formatting of booleans when value is string (e.g., format based on QFieldMetaData type, not value object class) 2024-08-01 15:11:20 -05:00
ba3cf53c30 Update to throw QNotFoundException if view isn't found by id (rather than NPE) 2024-08-01 15:08:53 -05:00
d44790545d Add total # failures to message; remove unused c'tor 2024-08-01 15:04:03 -05:00
5aed59b9b1 Add implements AutoCloseable, so we could use in a try-with-resources 2024-08-01 15:02:38 -05:00
3bcc0a17bc Add a log info re: releasing lock 2024-08-01 15:02:20 -05:00
09c4d99612 Avoid NPE and return w/ noop in performValidations if null (or empty) input records 2024-08-01 15:02:06 -05:00
26fc4fb4e0 Initial checkin 2024-08-01 15:01:22 -05:00
d92be4e69b don't duplicate apikey=value in re-tries; mask api key in outboundApiLog urls 2024-08-01 15:00:36 -05:00
2de3306f95 Add c'tor that takes table name, and override withTableName 2024-08-01 14:41:55 -05:00
58b0936c50 Add details to Incorrect number of values given exception 2024-08-01 14:41:40 -05:00
51eb7d89be Take report format as input 2024-08-01 14:40:27 -05:00
0b5e97d596 Bugfix, where sheet contents could get out-of-sync with their labels (e.g., see use-case with some summary views before their corresponding table views) 2024-07-22 14:26:45 -05:00
2609bc801c CE-1405 Add dataSource as argument to ReportCustomRecordSourceInterface.execute 2024-07-22 14:25:49 -05:00
583d702355 Re-add getInstance and getSession (until qqq consumer apps stop using them) 2024-07-19 17:02:37 -05:00
06a69279a8 CE-1472 - Fix doUpdate to set URL 2024-07-19 16:38:06 -05:00
9a2276edf2 CE-1472 - Refactored to do variants a little more generically per different auth-types; made createOAuth2TokenRequest its own overrideable method 2024-07-19 16:38:06 -05:00
36307dba24 CE-1405 Updates to qqq-reports: support for ReportCustomRecordSourceInterface 2024-07-19 16:37:22 -05:00
fa2b1c0b8e Fix merge conflicts 2024-07-19 16:25:15 -05:00
840e1aada3 Applying checkstyle updates to test sources 2024-07-19 16:16:51 -05:00
22d5bc547c Add includeTestSourceDirectory=true to checkstyle config 2024-07-19 16:16:51 -05:00
b7cfea157d Checkstyle updates
- remove MagicNumber
- add MissingJavadocType
- remove rules about contents of javadocs
2024-07-19 16:16:51 -05:00
028751e23a more test coverage for javalin (for new anonymous inner TypeReference) 2024-07-19 16:16:27 -05:00
be0e1f9c0b add some test coverage (updates to eliminate warnings put us just under threshold) 2024-07-19 16:16:27 -05:00
912e40fe0b Eliminated all warnings. 2024-07-19 16:16:27 -05:00
f9af2ba983 Remove all calls to actionInput.getInstance and getSesssion, in favor of the equivallent methods from QContext 2024-07-19 16:16:16 -05:00
200 changed files with 3035 additions and 809 deletions

View File

@ -213,18 +213,6 @@
<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"/>
@ -233,23 +221,14 @@
<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>

View File

@ -33,9 +33,6 @@ 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.
@ -55,6 +52,14 @@ 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`).
@ -68,6 +73,9 @@ 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]
----

View File

@ -46,12 +46,11 @@
</modules>
<properties>
<revision>0.21.0-SNAPSHOT</revision>
<revision>0.21.0</revision>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.release>17</maven.compiler.release>
<maven.compiler.showDeprecation>true</maven.compiler.showDeprecation>
<maven.compiler.showWarnings>true</maven.compiler.showWarnings>
<coverage.haltOnFailure>true</coverage.haltOnFailure>
@ -168,6 +167,7 @@
<violationSeverity>warning</violationSeverity>
<excludes>**/target/generated-sources/*.*</excludes>
<!-- <linkXRef>false</linkXRef> -->
<includeTestSourceDirectory>true</includeTestSourceDirectory>
</configuration>
<goals>
<goal>check</goal>

View File

@ -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
public class QBackendTransaction implements AutoCloseable
{
/*******************************************************************************

View File

@ -344,6 +344,9 @@ public class RecordAutomationStatusUpdater
/***************************************************************************
**
***************************************************************************/
private record Key(QTableMetaData table, TriggerEvent triggerEvent) {}
}

View File

@ -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,

View File

@ -28,6 +28,7 @@ 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;
@ -49,6 +50,9 @@ import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
*******************************************************************************/
public abstract class ChildInserterPostInsertCustomizer extends AbstractPostInsertCustomizer
{
/***************************************************************************
**
***************************************************************************/
public enum RelationshipType
{
PARENT_POINTS_AT_CHILD,
@ -97,7 +101,7 @@ public abstract class ChildInserterPostInsertCustomizer extends AbstractPostInse
List<QRecord> rs = records;
List<QRecord> childrenToInsert = new ArrayList<>();
QTableMetaData table = getInsertInput().getTable();
QTableMetaData childTable = getInsertInput().getInstance().getTable(getChildTableName());
QTableMetaData childTable = QContext.getQInstance().getTable(getChildTableName());
////////////////////////////////////////////////////////////////////////////////
// iterate over the inserted records, building a list child records to insert //

View File

@ -24,10 +24,12 @@ 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;
@ -143,4 +145,19 @@ public interface RecordCustomizerUtilityInterface
}
}
/*******************************************************************************
**
*******************************************************************************/
default Map<Serializable, QRecord> getOldRecordMap(List<QRecord> oldRecordList, UpdateInput updateInput)
{
Map<Serializable, QRecord> oldRecordMap = new HashMap<>();
for(QRecord qRecord : oldRecordList)
{
oldRecordMap.put(qRecord.getValue(updateInput.getTable().getPrimaryKeyField()), qRecord);
}
return (oldRecordMap);
}
}

View File

@ -31,6 +31,7 @@ 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;
@ -102,7 +103,7 @@ public abstract class AbstractWidgetRenderer
String possibleValueSourceName = dropdownData.getPossibleValueSourceName();
if(possibleValueSourceName != null)
{
QPossibleValueSource possibleValueSource = input.getInstance().getPossibleValueSource(possibleValueSourceName);
QPossibleValueSource possibleValueSource = QContext.getQInstance().getPossibleValueSource(possibleValueSourceName);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// this looks complicated, but is just look for a label in the dropdown data and if found use it, //

View File

@ -34,6 +34,7 @@ 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;
@ -181,10 +182,10 @@ public class ChildRecordListRenderer extends AbstractWidgetRenderer
{
String widgetLabel = input.getQueryParams().get("widgetLabel");
String joinName = input.getQueryParams().get("joinName");
QJoinMetaData join = input.getInstance().getJoin(joinName);
QJoinMetaData join = QContext.getQInstance().getJoin(joinName);
String id = input.getQueryParams().get("id");
QTableMetaData leftTable = input.getInstance().getTable(join.getLeftTable());
QTableMetaData rightTable = input.getInstance().getTable(join.getRightTable());
QTableMetaData leftTable = QContext.getQInstance().getTable(join.getLeftTable());
QTableMetaData rightTable = QContext.getQInstance().getTable(join.getRightTable());
Integer maxRows = null;
if(StringUtils.hasContent(input.getQueryParams().get("maxRows")))
@ -252,7 +253,7 @@ public class ChildRecordListRenderer extends AbstractWidgetRenderer
}
}
String tablePath = input.getInstance().getTablePath(rightTable.getName());
String tablePath = QContext.getQInstance().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);
@ -278,7 +279,9 @@ public class ChildRecordListRenderer extends AbstractWidgetRenderer
Map<String, Serializable> widgetValues = input.getWidgetMetaData().getDefaultValues();
if(widgetValues.containsKey("disabledFieldsForNewChildRecords"))
{
widgetData.setDisabledFieldsForNewChildRecords((Set<String>) widgetValues.get("disabledFieldsForNewChildRecords"));
@SuppressWarnings("unchecked")
Set<String> disabledFieldsForNewChildRecords = (Set<String>) widgetValues.get("disabledFieldsForNewChildRecords");
widgetData.setDisabledFieldsForNewChildRecords(disabledFieldsForNewChildRecords);
}
else
{

View File

@ -24,6 +24,7 @@ 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;
@ -57,7 +58,7 @@ public class ProcessWidgetRenderer extends AbstractWidgetRenderer
setupDropdowns(input, widgetMetaData, data);
String processName = (String) widgetMetaData.getDefaultValues().get(WIDGET_PROCESS_NAME);
QProcessMetaData processMetaData = input.getInstance().getProcess(processName);
QProcessMetaData processMetaData = QContext.getQInstance().getProcess(processName);
data.setProcessMetaData(processMetaData);
data.setDefaultValues(new HashMap<>(input.getQueryParams()));

View File

@ -30,6 +30,7 @@ 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;
@ -72,7 +73,7 @@ public class MetaDataAction
// map tables to frontend metadata //
/////////////////////////////////////
Map<String, QFrontendTableMetaData> tables = new LinkedHashMap<>();
for(Map.Entry<String, QTableMetaData> entry : metaDataInput.getInstance().getTables().entrySet())
for(Map.Entry<String, QTableMetaData> entry : QContext.getQInstance().getTables().entrySet())
{
String tableName = entry.getKey();
QTableMetaData table = entry.getValue();
@ -83,7 +84,7 @@ public class MetaDataAction
continue;
}
QBackendMetaData backendForTable = metaDataInput.getInstance().getBackendForTable(tableName);
QBackendMetaData backendForTable = QContext.getQInstance().getBackendForTable(tableName);
tables.put(tableName, new QFrontendTableMetaData(metaDataInput, backendForTable, table, false, false));
treeNodes.put(tableName, new AppTreeNode(table));
}
@ -96,7 +97,7 @@ public class MetaDataAction
// map processes to frontend metadata //
////////////////////////////////////////
Map<String, QFrontendProcessMetaData> processes = new LinkedHashMap<>();
for(Map.Entry<String, QProcessMetaData> entry : metaDataInput.getInstance().getProcesses().entrySet())
for(Map.Entry<String, QProcessMetaData> entry : QContext.getQInstance().getProcesses().entrySet())
{
String processName = entry.getKey();
QProcessMetaData process = entry.getValue();
@ -116,7 +117,7 @@ public class MetaDataAction
// map reports to frontend metadata //
//////////////////////////////////////
Map<String, QFrontendReportMetaData> reports = new LinkedHashMap<>();
for(Map.Entry<String, QReportMetaData> entry : metaDataInput.getInstance().getReports().entrySet())
for(Map.Entry<String, QReportMetaData> entry : QContext.getQInstance().getReports().entrySet())
{
String reportName = entry.getKey();
QReportMetaData report = entry.getValue();
@ -136,7 +137,7 @@ public class MetaDataAction
// map widgets to frontend metadata //
//////////////////////////////////////
Map<String, QFrontendWidgetMetaData> widgets = new LinkedHashMap<>();
for(Map.Entry<String, QWidgetMetaDataInterface> entry : metaDataInput.getInstance().getWidgets().entrySet())
for(Map.Entry<String, QWidgetMetaDataInterface> entry : QContext.getQInstance().getWidgets().entrySet())
{
String widgetName = entry.getKey();
QWidgetMetaDataInterface widget = entry.getValue();
@ -154,7 +155,7 @@ public class MetaDataAction
///////////////////////////////////////////////////////
// sort apps - by sortOrder (integer), then by label //
///////////////////////////////////////////////////////
List<QAppMetaData> sortedApps = metaDataInput.getInstance().getApps().values().stream()
List<QAppMetaData> sortedApps = QContext.getQInstance().getApps().values().stream()
.sorted(Comparator.comparing((QAppMetaData a) -> a.getSortOrder())
.thenComparing((QAppMetaData a) -> a.getLabel()))
.toList();
@ -211,14 +212,14 @@ public class MetaDataAction
////////////////////////////////////
// add branding metadata if found //
////////////////////////////////////
if(metaDataInput.getInstance().getBranding() != null)
if(QContext.getQInstance().getBranding() != null)
{
metaDataOutput.setBranding(metaDataInput.getInstance().getBranding());
metaDataOutput.setBranding(QContext.getQInstance().getBranding());
}
metaDataOutput.setEnvironmentValues(metaDataInput.getInstance().getEnvironmentValues());
metaDataOutput.setEnvironmentValues(QContext.getQInstance().getEnvironmentValues());
metaDataOutput.setHelpContents(metaDataInput.getInstance().getHelpContent());
metaDataOutput.setHelpContents(QContext.getQInstance().getHelpContent());
// todo post-customization - can do whatever w/ the result if you want?

View File

@ -23,6 +23,7 @@ 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;
@ -47,7 +48,7 @@ public class ProcessMetaDataAction
// todo pre-customization - just get to modify the request?
ProcessMetaDataOutput processMetaDataOutput = new ProcessMetaDataOutput();
QProcessMetaData process = processMetaDataInput.getInstance().getProcess(processMetaDataInput.getProcessName());
QProcessMetaData process = QContext.getQInstance().getProcess(processMetaDataInput.getProcessName());
if(process == null)
{
throw (new QNotFoundException("Process [" + processMetaDataInput.getProcessName() + "] was not found."));

View File

@ -23,6 +23,7 @@ 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;
@ -48,12 +49,12 @@ public class TableMetaDataAction
// todo pre-customization - just get to modify the request?
TableMetaDataOutput tableMetaDataOutput = new TableMetaDataOutput();
QTableMetaData table = tableMetaDataInput.getInstance().getTable(tableMetaDataInput.getTableName());
QTableMetaData table = QContext.getQInstance().getTable(tableMetaDataInput.getTableName());
if(table == null)
{
throw (new QNotFoundException("Table [" + tableMetaDataInput.getTableName() + "] was not found."));
}
QBackendMetaData backendForTable = tableMetaDataInput.getInstance().getBackendForTable(table.getName());
QBackendMetaData backendForTable = QContext.getQInstance().getBackendForTable(table.getName());
tableMetaDataOutput.setTable(new QFrontendTableMetaData(tableMetaDataInput, backendForTable, table, true, true));
// todo post-customization - can do whatever w/ the result if you want

View File

@ -25,6 +25,7 @@ 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;
@ -54,7 +55,7 @@ public class CancelProcessAction extends RunProcessAction
{
ActionHelper.validateSession(runProcessInput);
QProcessMetaData process = runProcessInput.getInstance().getProcess(runProcessInput.getProcessName());
QProcessMetaData process = QContext.getQInstance().getProcess(runProcessInput.getProcessName());
if(process == null)
{
throw new QBadRequestException("Process [" + runProcessInput.getProcessName() + "] is not defined in this instance.");

View File

@ -30,6 +30,7 @@ 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;
@ -64,7 +65,7 @@ public class RunBackendStepAction
{
ActionHelper.validateSession(runBackendStepInput);
QProcessMetaData process = runBackendStepInput.getInstance().getProcess(runBackendStepInput.getProcessName());
QProcessMetaData process = QContext.getQInstance().getProcess(runBackendStepInput.getProcessName());
if(process == null)
{
throw new QException("Process [" + runBackendStepInput.getProcessName() + "] is not defined in this instance.");

View File

@ -99,7 +99,7 @@ public class RunProcessAction
{
ActionHelper.validateSession(runProcessInput);
QProcessMetaData process = runProcessInput.getInstance().getProcess(runProcessInput.getProcessName());
QProcessMetaData process = QContext.getQInstance().getProcess(runProcessInput.getProcessName());
if(process == null)
{
throw new QException("Process [" + runProcessInput.getProcessName() + "] is not defined in this instance.");

View File

@ -42,6 +42,7 @@ 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;
@ -302,10 +303,19 @@ 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()))
{
joinsContext = new JoinsContext(QContext.getQInstance(), dataSource.getSourceTable(), cloneDataSourceQueryJoins(dataSource), dataSource.getQueryFilter() == null ? null : dataSource.getQueryFilter().clone());
countDataSourceRecords(reportInput, dataSource, reportFormat);
QQueryFilter queryFilter = dataSource.getQueryFilter() == null ? new QQueryFilter() : dataSource.getQueryFilter().clone();
joinsContext = new JoinsContext(QContext.getQInstance(), dataSource.getSourceTable(), dataSource.getQueryJoins(), queryFilter);
}
}
@ -329,6 +339,7 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
field.setName(column.getName());
if(StringUtils.hasContent(column.getLabel()))
{
field.setLabel(column.getLabel());
}
fields.add(field);
@ -346,23 +357,33 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
*******************************************************************************/
private void countDataSourceRecords(ReportInput reportInput, QReportDataSource dataSource, ReportFormat reportFormat) throws QException
{
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(cloneDataSourceQueryJoins(dataSource));
CountOutput countOutput = new CountAction().execute(countInput);
if(countOutput.getCount() != null)
Integer count = null;
if(dataSource.getCustomRecordSource() != null)
{
countByDataSource.put(dataSource.getName(), countOutput.getCount());
// 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);
if(reportFormat.getMaxRows() != null && countOutput.getCount() > reportFormat.getMaxRows())
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())
{
throw (new QUserFacingException("The requested report would include more rows ("
+ String.format("%,d", countOutput.getCount()) + ") than the maximum allowed ("
+ String.format("%,d", count) + ") than the maximum allowed ("
+ String.format("%,d", reportFormat.getMaxRows()) + ") for the selected file format (" + reportFormat + ")."));
}
}
@ -423,13 +444,19 @@ 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 for this data source //
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////
// run a record pipe loop, over the query (or other data-supplier/source) for this data source //
/////////////////////////////////////////////////////////////////////////////////////////////////
RecordPipe recordPipe = new BufferedRecordPipe(1000);
new AsyncRecordPipeLoop().run("Report[" + reportInput.getReportName() + "]", null, recordPipe, (callback) ->
{
if(dataSource.getSourceTable() != null)
if(dataSource.getCustomRecordSource() != null)
{
ReportCustomRecordSourceInterface recordSource = QCodeLoader.getAdHoc(ReportCustomRecordSourceInterface.class, dataSource.getCustomRecordSource());
recordSource.execute(reportInput, dataSource, recordPipe);
return (true);
}
else if(dataSource.getSourceTable() != null)
{
QQueryFilter queryFilter = dataSource.getQueryFilter() == null ? new QQueryFilter() : dataSource.getQueryFilter().clone();
setInputValuesInQueryFilter(reportInput, queryFilter);

View File

@ -0,0 +1,43 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2024. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.actions.reporting.customizers;
import com.kingsrook.qqq.backend.core.actions.reporting.RecordPipe;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportInput;
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportDataSource;
/*******************************************************************************
** Interface to be implemented to do a custom source of data for a report
** (instead of just a query against a table).
*******************************************************************************/
public interface ReportCustomRecordSourceInterface
{
/***************************************************************************
** Given the report input, put records into the pipe, for the report.
***************************************************************************/
void execute(ReportInput reportInput, QReportDataSource reportDataSource, RecordPipe recordPipe) throws QException;
}

View File

@ -124,7 +124,7 @@ public class ExcelFastexcelExportStreamer implements ExportStreamerInterface
if(workbook == null)
{
String appName = ObjectUtils.tryAndRequireNonNullElse(() -> QContext.getQInstance().getBranding().getAppName(), "QQQ");
QInstance instance = exportInput.getInstance();
QInstance instance = QContext.getQInstance();
if(instance != null && instance.getBranding() != null && instance.getBranding().getCompanyName() != null)
{
appName = instance.getBranding().getCompanyName();

View File

@ -124,10 +124,11 @@ 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 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<>();
@ -180,6 +181,7 @@ 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++;
}
@ -446,7 +448,7 @@ public class ExcelPoiBasedStreamingExportStreamer implements ExportStreamerInter
// - with a new output stream writer //
// - and with a SpreadsheetWriter //
//////////////////////////////////////////
zipOutputStream.putNextEntry(new ZipEntry("xl/worksheets/sheet" + this.sheetIndex++ + ".xml"));
zipOutputStream.putNextEntry(new ZipEntry(sheetReferenceByViewName.get(view.getName())));
activeSheetWriter = new OutputStreamWriter(zipOutputStream);
sheetWriter = new StreamedSheetWriter(activeSheetWriter);

View File

@ -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();

View File

@ -23,6 +23,7 @@ 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;
@ -97,7 +98,7 @@ public class RecordScriptTestInterface implements TestScriptActionInterface
}
QueryOutput queryOutput = new QueryAction().execute(new QueryInput(tableName)
.withFilter(new QQueryFilter(new QFilterCriteria(table.getPrimaryKeyField(), QCriteriaOperator.IN, recordPrimaryKeyList.split(","))))
.withFilter(new QQueryFilter(new QFilterCriteria(table.getPrimaryKeyField(), QCriteriaOperator.IN, Arrays.stream(recordPrimaryKeyList.split(",")).toList())))
.withIncludeAssociations(true));
if(CollectionUtils.nullSafeIsEmpty(queryOutput.getRecords()))
{

View File

@ -154,8 +154,9 @@ 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());
// noinspection unchecked
return (ArrayList<? extends Serializable>) apiRecordList;
@SuppressWarnings("unchecked")
ArrayList<? extends Serializable> rs = (ArrayList<? extends Serializable>) apiRecordList;
return rs;
}
catch(ClassNotFoundException e)
{

View File

@ -352,7 +352,7 @@ public class GetAction
{
if(qPossibleValueTranslator == null)
{
qPossibleValueTranslator = new QPossibleValueTranslator(getInput.getInstance(), getInput.getSession());
qPossibleValueTranslator = new QPossibleValueTranslator(QContext.getQInstance(), QContext.getQSession());
}
qPossibleValueTranslator.translatePossibleValuesInRecords(getInput.getTable(), List.of(returnRecord));
}

View File

@ -227,6 +227,11 @@ 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();
///////////////////////////////////////////////////////////////////
@ -241,7 +246,7 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
setDefaultValuesInRecords(table, insertInput.getRecords());
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.INSERT, insertInput.getInstance(), table, insertInput.getRecords(), null);
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.INSERT, QContext.getQInstance(), table, insertInput.getRecords(), null);
runPreInsertCustomizerIfItIsTime(insertInput, isPreview, preInsertCustomizer, AbstractPreInsertCustomizer.WhenToRun.BEFORE_UNIQUE_KEY_CHECKS);
setErrorsIfUniqueKeyErrors(insertInput, table);

View File

@ -26,6 +26,7 @@ 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;
@ -50,6 +51,7 @@ 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;
@ -64,6 +66,7 @@ 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;
@ -101,6 +104,8 @@ 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;
@ -158,6 +163,109 @@ 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.
@ -297,7 +405,7 @@ public class QueryAction
{
if(qPossibleValueTranslator == null)
{
qPossibleValueTranslator = new QPossibleValueTranslator(queryInput.getInstance(), queryInput.getSession());
qPossibleValueTranslator = new QPossibleValueTranslator(QContext.getQInstance(), QContext.getQSession());
}
qPossibleValueTranslator.translatePossibleValuesInRecords(queryInput.getTable(), records, queryInput.getQueryJoins(), queryInput.getFieldsToTranslatePossibleValues());
}

View File

@ -252,7 +252,7 @@ public class UpdateAction
behaviorsToOmit = Set.of(DynamicDefaultValueBehavior.MODIFY_DATE);
}
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.UPDATE, updateInput.getInstance(), table, updateInput.getRecords(), behaviorsToOmit);
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.UPDATE, QContext.getQInstance(), table, updateInput.getRecords(), behaviorsToOmit);
validatePrimaryKeysAreGiven(updateInput);
if(oldRecordList.isPresent())

View File

@ -68,7 +68,7 @@ public class QValueFormatter
*******************************************************************************/
public static String formatValue(QFieldMetaData field, Serializable value)
{
return (formatValue(field.getDisplayFormat(), field.getName(), value));
return (formatValue(field.getDisplayFormat(), field.getType(), field.getName(), value));
}
@ -78,7 +78,7 @@ public class QValueFormatter
*******************************************************************************/
public static String formatValue(String displayFormat, Serializable value)
{
return (formatValue(displayFormat, "", value));
return (formatValue(displayFormat, null, "", 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, String fieldName, Serializable value)
private static String formatValue(String displayFormat, QFieldType fieldType, String fieldName, Serializable value)
{
//////////////////////////////////
// null values get null results //
@ -107,6 +107,11 @@ public class QValueFormatter
return formatBoolean(b);
}
if(QFieldType.BOOLEAN.equals(fieldType))
{
return formatBoolean(ValueUtils.getValueAsBoolean(value));
}
if(value instanceof LocalTime lt)
{
return formatLocalTime(lt);
@ -404,6 +409,7 @@ 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).
@ -563,6 +569,7 @@ 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)
{

View File

@ -30,6 +30,7 @@ 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;
@ -69,14 +70,14 @@ public class SearchPossibleValueSourceAction
*******************************************************************************/
public SearchPossibleValueSourceOutput execute(SearchPossibleValueSourceInput input) throws QException
{
QInstance qInstance = input.getInstance();
QInstance qInstance = QContext.getQInstance();
QPossibleValueSource possibleValueSource = qInstance.getPossibleValueSource(input.getPossibleValueSourceName());
if(possibleValueSource == null)
{
throw new QException("Missing possible value source named [" + input.getPossibleValueSourceName() + "]");
}
possibleValueTranslator = new QPossibleValueTranslator(input.getInstance(), input.getSession());
possibleValueTranslator = new QPossibleValueTranslator(QContext.getQInstance(), QContext.getQSession());
SearchPossibleValueSourceOutput output = null;
if(possibleValueSource.getType().equals(QPossibleValueSourceType.ENUM))
{
@ -199,7 +200,7 @@ public class SearchPossibleValueSourceAction
QueryInput queryInput = new QueryInput();
queryInput.setTableName(possibleValueSource.getTableName());
QTableMetaData table = input.getInstance().getTable(possibleValueSource.getTableName());
QTableMetaData table = QContext.getQInstance().getTable(possibleValueSource.getTableName());
QQueryFilter queryFilter = new QQueryFilter();
queryFilter.setBooleanOperator(QQueryFilter.BooleanOperator.OR);
@ -299,6 +300,7 @@ public class SearchPossibleValueSourceAction
/*******************************************************************************
**
*******************************************************************************/
@SuppressWarnings({ "rawtypes", "unchecked" })
private SearchPossibleValueSourceOutput searchPossibleValueCustom(SearchPossibleValueSourceInput input, QPossibleValueSource possibleValueSource)
{
try

View File

@ -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,12 +55,11 @@ public class QInstanceValidationException extends QException
*******************************************************************************/
public QInstanceValidationException(List<String> reasons)
{
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");
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");
if(reasons != null && reasons.size() > 0)
if(CollectionUtils.nullSafeHasContents(reasons))
{
this.reasons = reasons;
}
@ -68,25 +67,6 @@ 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!
**

View File

@ -44,6 +44,7 @@ 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;
@ -922,7 +923,7 @@ public class QInstanceValidator
/*******************************************************************************
**
*******************************************************************************/
private <T extends FieldBehavior<T>> void validateTableField(QInstance qInstance, String tableName, String fieldName, QTableMetaData table, QFieldMetaData field)
private 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() + ".");
@ -944,12 +945,13 @@ public class QInstanceValidator
assertCondition(field.getMaxLength() != null, prefix + "specifies a ValueTooLongBehavior, but not a maxLength.");
}
Set<Class<FieldBehavior<T>>> usedFieldBehaviorTypes = new HashSet<>();
Set<Class<FieldBehavior<?>>> usedFieldBehaviorTypes = new HashSet<>();
if(field.getBehaviors() != null)
{
for(FieldBehavior<?> fieldBehavior : field.getBehaviors())
{
Class<FieldBehavior<T>> behaviorClass = (Class<FieldBehavior<T>>) fieldBehavior.getClass();
@SuppressWarnings("unchecked")
Class<FieldBehavior<?>> behaviorClass = (Class<FieldBehavior<?>>) fieldBehavior.getClass();
errors.addAll(fieldBehavior.validateBehaviorConfiguration(table, field));
@ -1659,9 +1661,12 @@ public class QInstanceValidator
String dataSourceErrorPrefix = "Report " + reportName + " data source " + dataSource.getName() + " ";
boolean hasASource = false;
if(StringUtils.hasContent(dataSource.getSourceTable()))
{
assertCondition(dataSource.getStaticDataSupplier() == null, dataSourceErrorPrefix + "has both a sourceTable and a staticDataSupplier (exactly 1 is required).");
hasASource = true;
assertCondition(dataSource.getStaticDataSupplier() == null, dataSourceErrorPrefix + "has both a sourceTable and a staticDataSupplier (not compatible together).");
if(assertCondition(qInstance.getTable(dataSource.getSourceTable()) != null, dataSourceErrorPrefix + "source table " + dataSource.getSourceTable() + " is not a table in this instance."))
{
if(dataSource.getQueryFilter() != null)
@ -1670,14 +1675,21 @@ public class QInstanceValidator
}
}
}
else if(dataSource.getStaticDataSupplier() != null)
if(dataSource.getStaticDataSupplier() != null)
{
assertCondition(dataSource.getCustomRecordSource() == null, dataSourceErrorPrefix + "has both a staticDataSupplier and a customRecordSource (not compatible together).");
hasASource = true;
validateSimpleCodeReference(dataSourceErrorPrefix, dataSource.getStaticDataSupplier(), Supplier.class);
}
else
if(dataSource.getCustomRecordSource() != null)
{
errors.add(dataSourceErrorPrefix + "does not have a sourceTable or a staticDataSupplier (exactly 1 is required).");
hasASource = true;
validateSimpleCodeReference(dataSourceErrorPrefix, dataSource.getCustomRecordSource(), ReportCustomRecordSourceInterface.class);
}
assertCondition(hasASource, dataSourceErrorPrefix + "does not have a sourceTable, customRecordSource, or a staticDataSupplier.");
}
}

View File

@ -24,6 +24,7 @@ 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;
@ -113,7 +114,7 @@ public class SecretsManagerUtils
dotEnv.renameTo(new File(".env.backup-" + System.currentTimeMillis()));
}
FileUtils.writeStringToFile(dotEnv, fullEnv.toString());
FileUtils.writeStringToFile(dotEnv, fullEnv.toString(), StandardCharsets.UTF_8);
}
else
{

View File

@ -32,7 +32,6 @@ 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;
@ -93,17 +92,6 @@ public class AbstractActionInput
/*******************************************************************************
**
*******************************************************************************/
@JsonIgnore
public QAuthenticationMetaData getAuthenticationMetaData()
{
return (getInstance().getAuthentication());
}
/*******************************************************************************
** Getter for instance
**

View File

@ -152,5 +152,8 @@ public class AuditDetailAccumulator implements Serializable
}
/***************************************************************************
**
***************************************************************************/
private record TableNameAndPrimaryKey(String tableName, Serializable primaryKey) {}
}

View File

@ -59,6 +59,30 @@ 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
**

View File

@ -66,6 +66,14 @@ 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. //
@ -686,4 +694,35 @@ 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);
}
}

View File

@ -40,11 +40,14 @@ public abstract class AbstractFilterExpression<T extends Serializable> implement
/*******************************************************************************
** 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 (T) this;
return evaluate();
}

View File

@ -42,6 +42,9 @@ public class NowWithOffset extends AbstractFilterExpression<Instant>
/***************************************************************************
**
***************************************************************************/
public enum Operator
{PLUS, MINUS}

View File

@ -43,6 +43,9 @@ public class ThisOrLastPeriod extends AbstractFilterExpression<Instant>
/***************************************************************************
**
***************************************************************************/
public enum Operator
{THIS, LAST}

View File

@ -79,10 +79,11 @@ public class QFilterCriteriaDeserializer extends StdDeserializer<QFilterCriteria
/////////////////////////////////
// get values out of json node //
/////////////////////////////////
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);
@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);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// look at all the values - if any of them are actually meant to be an Expression (instance of subclass of AbstractFilterExpression) //

View File

@ -28,6 +28,9 @@ package com.kingsrook.qqq.backend.core.model.dashboard.widgets;
*******************************************************************************/
public class AlertData extends QWidgetData
{
/***************************************************************************
**
***************************************************************************/
public enum AlertType
{
ERROR,

View File

@ -40,9 +40,10 @@ public class CompositeWidgetData extends AbstractBlockWidgetData<CompositeWidget
{
private List<AbstractBlockWidgetData<?, ?, ?, ?>> blocks = new ArrayList<>();
private Map<String, Serializable> styleOverrides = new HashMap<>();
private Layout layout;
private Layout layout;
private Map<String, Serializable> styleOverrides = new HashMap<>();
private String overlayHtml;
private Map<String, Serializable> overlayStyleOverrides = new HashMap<>();
@ -218,4 +219,91 @@ 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);
}
}

View File

@ -24,6 +24,7 @@ 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;
@ -74,6 +75,7 @@ public abstract class AbstractBlockWidgetData<
/*******************************************************************************
**
*******************************************************************************/
@SuppressWarnings("unchecked")
public T withTooltip(S key, String value)
{
addTooltip(key, value);
@ -99,6 +101,7 @@ public abstract class AbstractBlockWidgetData<
/*******************************************************************************
**
*******************************************************************************/
@SuppressWarnings("unchecked")
public T withTooltip(S key, BlockTooltip value)
{
addTooltip(key, value);
@ -144,6 +147,7 @@ public abstract class AbstractBlockWidgetData<
/*******************************************************************************
** Fluent setter for tooltipMap
*******************************************************************************/
@SuppressWarnings("unchecked")
public T withTooltipMap(Map<S, BlockTooltip> tooltipMap)
{
this.tooltipMap = tooltipMap;
@ -178,6 +182,7 @@ public abstract class AbstractBlockWidgetData<
** Fluent setter for tooltip
**
*******************************************************************************/
@SuppressWarnings("unchecked")
public T withTooltip(String tooltip)
{
this.tooltip = new BlockTooltip(tooltip);
@ -190,6 +195,7 @@ public abstract class AbstractBlockWidgetData<
** Fluent setter for tooltip
**
*******************************************************************************/
@SuppressWarnings("unchecked")
public T withTooltip(BlockTooltip tooltip)
{
this.tooltip = tooltip;
@ -199,8 +205,22 @@ 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);
@ -226,6 +246,7 @@ public abstract class AbstractBlockWidgetData<
/*******************************************************************************
**
*******************************************************************************/
@SuppressWarnings("unchecked")
public T withLink(S key, BlockLink value)
{
addLink(key, value);
@ -271,6 +292,7 @@ public abstract class AbstractBlockWidgetData<
/*******************************************************************************
** Fluent setter for linkMap
*******************************************************************************/
@SuppressWarnings("unchecked")
public T withLinkMap(Map<S, BlockLink> linkMap)
{
this.linkMap = linkMap;
@ -305,6 +327,7 @@ public abstract class AbstractBlockWidgetData<
** Fluent setter for link
**
*******************************************************************************/
@SuppressWarnings("unchecked")
public T withLink(String link)
{
this.link = new BlockLink(link);
@ -317,6 +340,7 @@ public abstract class AbstractBlockWidgetData<
** Fluent setter for link
**
*******************************************************************************/
@SuppressWarnings("unchecked")
public T withLink(BlockLink link)
{
this.link = link;
@ -348,6 +372,7 @@ public abstract class AbstractBlockWidgetData<
/*******************************************************************************
** Fluent setter for values
*******************************************************************************/
@SuppressWarnings("unchecked")
public T withValues(V values)
{
this.values = values;
@ -379,6 +404,7 @@ public abstract class AbstractBlockWidgetData<
/*******************************************************************************
** Fluent setter for styles
*******************************************************************************/
@SuppressWarnings("unchecked")
public T withStyles(SX styles)
{
this.styles = styles;
@ -386,6 +412,7 @@ public abstract class AbstractBlockWidgetData<
}
/*******************************************************************************
** Getter for blockId
*******************************************************************************/
@ -409,11 +436,11 @@ public abstract class AbstractBlockWidgetData<
/*******************************************************************************
** Fluent setter for blockId
*******************************************************************************/
@SuppressWarnings("unchecked")
public T withBlockId(String blockId)
{
this.blockId = blockId;
return (T) this;
}
}

View File

@ -22,17 +22,24 @@
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 String title;
private Placement placement = Placement.BOTTOM;
private CompositeWidgetData blockData;
private String title;
private Placement placement = Placement.BOTTOM;
/***************************************************************************
**
***************************************************************************/
public enum Placement
{BOTTOM, LEFT, RIGHT, TOP}
@ -59,6 +66,17 @@ public class BlockTooltip
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public BlockTooltip(CompositeWidgetData blockData)
{
this.blockData = blockData;
}
/*******************************************************************************
** Getter for title
*******************************************************************************/
@ -119,4 +137,35 @@ 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);
}
}

View File

@ -192,6 +192,7 @@ 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();
@ -245,6 +246,7 @@ 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();
@ -346,6 +348,7 @@ 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)));
}

View File

@ -53,6 +53,8 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
private String variantOptionsTableUsernameField;
private String variantOptionsTablePasswordField;
private String variantOptionsTableApiKeyField;
private String variantOptionsTableClientIdField;
private String variantOptionsTableClientSecretField;
private String variantOptionsTableName;
// todo - at some point, we may want to apply this to secret properties on subclasses?
@ -648,4 +650,66 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
{
qInstance.addBackend(this);
}
/*******************************************************************************
** Getter for variantOptionsTableClientIdField
*******************************************************************************/
public String getVariantOptionsTableClientIdField()
{
return (this.variantOptionsTableClientIdField);
}
/*******************************************************************************
** Setter for variantOptionsTableClientIdField
*******************************************************************************/
public void setVariantOptionsTableClientIdField(String variantOptionsTableClientIdField)
{
this.variantOptionsTableClientIdField = variantOptionsTableClientIdField;
}
/*******************************************************************************
** Fluent setter for variantOptionsTableClientIdField
*******************************************************************************/
public QBackendMetaData withVariantOptionsTableClientIdField(String variantOptionsTableClientIdField)
{
this.variantOptionsTableClientIdField = variantOptionsTableClientIdField;
return (this);
}
/*******************************************************************************
** Getter for variantOptionsTableClientSecretField
*******************************************************************************/
public String getVariantOptionsTableClientSecretField()
{
return (this.variantOptionsTableClientSecretField);
}
/*******************************************************************************
** Setter for variantOptionsTableClientSecretField
*******************************************************************************/
public void setVariantOptionsTableClientSecretField(String variantOptionsTableClientSecretField)
{
this.variantOptionsTableClientSecretField = variantOptionsTableClientSecretField;
}
/*******************************************************************************
** Fluent setter for variantOptionsTableClientSecretField
*******************************************************************************/
public QBackendMetaData withVariantOptionsTableClientSecretField(String variantOptionsTableClientSecretField)
{
this.variantOptionsTableClientSecretField = variantOptionsTableClientSecretField;
return (this);
}
}

View File

@ -39,6 +39,9 @@ public class ParentWidgetMetaData extends QWidgetMetaData
/***************************************************************************
**
***************************************************************************/
public enum LayoutType
{
GRID,

View File

@ -188,7 +188,8 @@ public class FieldAdornment
** Fluent setter for values
**
*******************************************************************************/
public FieldAdornment withValues(Pair<String, Serializable>... values)
@SafeVarargs
public final FieldAdornment withValues(Pair<String, Serializable>... values)
{
for(Pair<String, Serializable> value : values)
{

View File

@ -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 = actionInput.getInstance().getBackend(tableMetaData.getBackendName());
QBackendMetaData backend = QContext.getQInstance().getBackend(tableMetaData.getBackendName());
if(backend != null && backend.getUsesVariants())
{
usesVariants = true;
variantTableLabel = actionInput.getInstance().getTable(backend.getVariantOptionsTableName()).getLabel();
variantTableLabel = QContext.getQInstance().getTable(backend.getVariantOptionsTableName()).getLabel();
}
this.helpContents = tableMetaData.getHelpContent();

View File

@ -32,6 +32,13 @@ 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
{
@ -44,6 +51,7 @@ public class QReportDataSource
private QCodeReference queryInputCustomizer;
private QCodeReference staticDataSupplier;
private QCodeReference customRecordSource;
@ -265,4 +273,35 @@ 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);
}
}

View File

@ -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);

View File

@ -32,6 +32,9 @@ 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.

View File

@ -662,7 +662,11 @@ public class MemoryRecordStore
{
return (-1);
}
return ((Comparable) a).compareTo(b);
@SuppressWarnings("unchecked")
Comparable<Serializable> comparableSerializableA = (Comparable<Serializable>) a;
return comparableSerializableA.compareTo(b);
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -769,6 +773,7 @@ public class MemoryRecordStore
/*******************************************************************************
**
*******************************************************************************/
@SuppressWarnings({ "rawtypes", "unchecked" })
private static Serializable computeAggregate(List<QRecord> records, Aggregate aggregate, QTableMetaData table)
{
String fieldName = aggregate.getFieldName();

View File

@ -27,6 +27,7 @@ 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;
@ -76,7 +77,7 @@ public class BulkDeleteLoadStep extends LoadViaDeleteStep implements ProcessSumm
{
super.preRun(runBackendStepInput, runBackendStepOutput);
QTableMetaData table = runBackendStepInput.getInstance().getTable(runBackendStepInput.getTableName());
QTableMetaData table = QContext.getQInstance().getTable(runBackendStepInput.getTableName());
if(table != null)
{
tableLabel = table.getLabel();
@ -119,7 +120,7 @@ public class BulkDeleteLoadStep extends LoadViaDeleteStep implements ProcessSumm
////////////////////////////
super.runOnePage(runBackendStepInput, runBackendStepOutput);
QTableMetaData table = runBackendStepInput.getInstance().getTable(runBackendStepInput.getTableName());
QTableMetaData table = QContext.getQInstance().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));

View File

@ -27,6 +27,7 @@ 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;
@ -65,7 +66,7 @@ public class BulkDeleteTransformStep extends AbstractTransformStep
///////////////////////////////////////////////////////
// capture the table label - for the process summary //
///////////////////////////////////////////////////////
QTableMetaData table = runBackendStepInput.getInstance().getTable(runBackendStepInput.getTableName());
QTableMetaData table = QContext.getQInstance().getTable(runBackendStepInput.getTableName());
if(table != null)
{
tableLabel = table.getLabel();

View File

@ -25,6 +25,7 @@ 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;
@ -99,7 +100,7 @@ public class BulkEditLoadStep extends LoadViaUpdateStep implements ProcessSummar
{
super.preRun(runBackendStepInput, runBackendStepOutput);
QTableMetaData table = runBackendStepInput.getInstance().getTable(runBackendStepInput.getTableName());
QTableMetaData table = QContext.getQInstance().getTable(runBackendStepInput.getTableName());
if(table != null)
{
tableLabel = table.getLabel();
@ -124,7 +125,7 @@ public class BulkEditLoadStep extends LoadViaUpdateStep implements ProcessSummar
////////////////////////////////////////////////////////
// roll up results based on output from update action //
////////////////////////////////////////////////////////
QTableMetaData table = runBackendStepInput.getInstance().getTable(runBackendStepInput.getTableName());
QTableMetaData table = QContext.getQInstance().getTable(runBackendStepInput.getTableName());
for(QRecord record : runBackendStepOutput.getRecords())
{
Serializable recordPrimaryKey = record.getValue(table.getPrimaryKeyField());

View File

@ -31,6 +31,7 @@ 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;
@ -81,7 +82,7 @@ public class BulkEditTransformStep extends AbstractTransformStep
///////////////////////////////////////////////////////
// capture the table label - for the process summary //
///////////////////////////////////////////////////////
table = runBackendStepInput.getInstance().getTable(runBackendStepInput.getTableName());
table = QContext.getQInstance().getTable(runBackendStepInput.getTableName());
if(table != null)
{
tableLabel = table.getLabel();
@ -230,7 +231,7 @@ public class BulkEditTransformStep extends AbstractTransformStep
if(field.getPossibleValueSourceName() != null)
{
QPossibleValueTranslator qPossibleValueTranslator = new QPossibleValueTranslator(runBackendStepInput.getInstance(), runBackendStepInput.getSession());
QPossibleValueTranslator qPossibleValueTranslator = new QPossibleValueTranslator(QContext.getQInstance(), QContext.getQSession());
String translatedValue = qPossibleValueTranslator.translatePossibleValue(field, value);
if(StringUtils.hasContent(translatedValue))
{

View File

@ -27,6 +27,7 @@ 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;
@ -83,7 +84,7 @@ public class BulkInsertExtractStep extends AbstractExtractStep
.withLimit(getLimit())
.withCsv(new String(bytes))
.withDoCorrectValueTypes(true)
.withTable(runBackendStepInput.getInstance().getTable(tableName))
.withTable(QContext.getQInstance().getTable(tableName))
.withMapping(mapping)
.withRecordCustomizer((record) ->
{

View File

@ -38,6 +38,7 @@ 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;
@ -104,7 +105,7 @@ public class BulkInsertTransformStep extends AbstractTransformStep
@Override
public void preRun(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
{
this.table = runBackendStepInput.getInstance().getTable(runBackendStepInput.getTableName());
this.table = QContext.getQInstance().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. //
@ -121,7 +122,7 @@ public class BulkInsertTransformStep extends AbstractTransformStep
public void runOnePage(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
{
int rowsInThisPage = runBackendStepInput.getRecords().size();
QTableMetaData table = runBackendStepInput.getInstance().getTable(runBackendStepInput.getTableName());
QTableMetaData table = QContext.getQInstance().getTable(runBackendStepInput.getTableName());
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// set up an insert-input, which will be used as input to the pre-customizer as well as for additional validations //

View File

@ -28,6 +28,7 @@ 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;
@ -85,7 +86,7 @@ public class BasicETLTransformFunction implements BackendStep
throw (new QException("Mapping was not a Key-based mapping type. Was a : " + mapping.getClass().getName()));
}
QTableMetaData table = runBackendStepInput.getInstance().getTable(tableName);
QTableMetaData table = QContext.getQInstance().getTable(tableName);
List<QRecord> mappedRecords = applyMapping(runBackendStepInput.getRecords(), table, keyBasedFieldMapping);
//////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -27,6 +27,7 @@ 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;
@ -85,14 +86,14 @@ public class BaseStreamedETLStep
protected void updateRecordsWithDisplayValuesAndPossibleValues(RunBackendStepInput input, List<QRecord> list)
{
String destinationTable = input.getValueString(StreamedETLWithFrontendProcess.FIELD_DESTINATION_TABLE);
QTableMetaData table = input.getInstance().getTable(destinationTable);
QTableMetaData table = QContext.getQInstance().getTable(destinationTable);
if(table != null && list != null)
{
QValueFormatter qValueFormatter = new QValueFormatter();
qValueFormatter.setDisplayValuesInRecords(table, list);
QPossibleValueTranslator qPossibleValueTranslator = new QPossibleValueTranslator(input.getInstance(), input.getSession());
QPossibleValueTranslator qPossibleValueTranslator = new QPossibleValueTranslator(QContext.getQInstance(), QContext.getQSession());
qPossibleValueTranslator.translatePossibleValuesInRecords(table, list);
}
}

View File

@ -34,6 +34,7 @@ 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;
@ -267,7 +268,7 @@ public class ExtractViaQueryStep extends AbstractExtractStep
//////////////////////////////////////////////////////////////////////
// else, check for recordIds from a frontend launching of a process //
//////////////////////////////////////////////////////////////////////
QTableMetaData table = runBackendStepInput.getInstance().getTable(runBackendStepInput.getValueString(FIELD_SOURCE_TABLE));
QTableMetaData table = QContext.getQInstance().getTable(runBackendStepInput.getValueString(FIELD_SOURCE_TABLE));
if(table == null)
{
throw (new QException("source table name was not set - could not load records by id"));
@ -319,7 +320,7 @@ public class ExtractViaQueryStep extends AbstractExtractStep
if(needDistinctPipe)
{
String sourceTableName = runBackendStepInput.getValueString(StreamedETLWithFrontendProcess.FIELD_SOURCE_TABLE);
QTableMetaData sourceTable = runBackendStepInput.getInstance().getTable(sourceTableName);
QTableMetaData sourceTable = QContext.getQInstance().getTable(sourceTableName);
return (new DistinctFilteringRecordPipe(new UniqueKey(sourceTable.getPrimaryKeyField()), overrideCapacity));
}
else

View File

@ -28,6 +28,7 @@ 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;
@ -86,7 +87,7 @@ public class LoadViaInsertOrUpdateStep extends AbstractLoadStep
*******************************************************************************/
public void insertAndUpdateRecords(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
{
QTableMetaData tableMetaData = runBackendStepInput.getInstance().getTable(runBackendStepInput.getValueString(FIELD_DESTINATION_TABLE));
QTableMetaData tableMetaData = QContext.getQInstance().getTable(runBackendStepInput.getValueString(FIELD_DESTINATION_TABLE));
if(CollectionUtils.nullSafeHasContents(recordsToInsert))
{
@ -139,7 +140,7 @@ public class LoadViaInsertOrUpdateStep extends AbstractLoadStep
*******************************************************************************/
protected void evaluateRecords(RunBackendStepInput runBackendStepInput) throws QException
{
QTableMetaData tableMetaData = runBackendStepInput.getInstance().getTable(runBackendStepInput.getValueString(FIELD_DESTINATION_TABLE));
QTableMetaData tableMetaData = QContext.getQInstance().getTable(runBackendStepInput.getValueString(FIELD_DESTINATION_TABLE));
recordsToInsert = new ArrayList<>();
recordsToUpdate = new ArrayList<>();

View File

@ -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 = runBackendStepInput.getInstance().getTable(sourceTableName);
QTableMetaData sourceTable = QContext.getQInstance().getTable(sourceTableName);
if(StringUtils.hasContent(sourceTableName))
{
QBackendMetaData sourceTableBackend = runBackendStepInput.getInstance().getBackendForTable(sourceTableName);
QBackendMetaData sourceTableBackend = QContext.getQInstance().getBackendForTable(sourceTableName);
if(sourceTable.isCapabilityEnabled(sourceTableBackend, Capability.TABLE_COUNT))
{
Integer recordCount = extractStep.doCount(runBackendStepInput);

View File

@ -65,9 +65,14 @@ public class MergeDuplicatesLoadStep extends LoadViaInsertOrUpdateStep
{
super.runOnePage(runBackendStepInput, runBackendStepOutput);
ListingHash<String, Serializable> otherTableIdsToDelete = (ListingHash<String, Serializable>) runBackendStepInput.getValue("otherTableIdsToDelete");
@SuppressWarnings("unchecked")
ListingHash<String, Serializable> otherTableIdsToDelete = (ListingHash<String, Serializable>) runBackendStepInput.getValue("otherTableIdsToDelete");
@SuppressWarnings("unchecked")
ListingHash<String, QQueryFilter> otherTableFiltersToDelete = (ListingHash<String, QQueryFilter>) runBackendStepInput.getValue("otherTableFiltersToDelete");
ListingHash<String, QRecord> otherTableRecordsToStore = (ListingHash<String, QRecord>) runBackendStepInput.getValue("otherTableRecordsToStore");
@SuppressWarnings("unchecked")
ListingHash<String, QRecord> otherTableRecordsToStore = (ListingHash<String, QRecord>) runBackendStepInput.getValue("otherTableRecordsToStore");
if(otherTableIdsToDelete != null)
{

View File

@ -46,7 +46,8 @@ 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_NAME = "reportName";
public static final String FIELD_REPORT_FORMAT = "reportFormat";

View File

@ -31,7 +31,9 @@ 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;
@ -49,6 +51,7 @@ import com.kingsrook.qqq.backend.core.utils.StringUtils;
public class ExecuteReportStep implements BackendStep
{
/*******************************************************************************
**
*******************************************************************************/
@ -57,9 +60,10 @@ public class ExecuteReportStep implements BackendStep
{
try
{
String reportName = runBackendStepInput.getValueString("reportName");
QReportMetaData report = runBackendStepInput.getInstance().getReport(reportName);
File tmpFile = File.createTempFile(reportName, ".xlsx", new File("/tmp/"));
ReportFormat reportFormat = getReportFormat(runBackendStepInput);
String reportName = runBackendStepInput.getValueString("reportName");
QReportMetaData report = QContext.getQInstance().getReport(reportName);
File tmpFile = File.createTempFile(reportName, "." + reportFormat.getExtension());
runBackendStepInput.getAsyncJobCallback().updateStatus("Generating Report");
@ -68,7 +72,7 @@ public class ExecuteReportStep implements BackendStep
ReportInput reportInput = new ReportInput();
reportInput.setReportName(reportName);
reportInput.setReportDestination(new ReportDestination()
.withReportFormat(ReportFormat.XLSX) // todo - variable
.withReportFormat(reportFormat)
.withReportOutputStream(reportOutputStream));
Map<String, Serializable> values = runBackendStepInput.getValues();
@ -78,7 +82,7 @@ public class ExecuteReportStep implements BackendStep
String downloadFileBaseName = getDownloadFileBaseName(runBackendStepInput, report);
runBackendStepOutput.addValue("downloadFileName", downloadFileBaseName + ".xlsx");
runBackendStepOutput.addValue("downloadFileName", downloadFileBaseName + "." + reportFormat.getExtension());
runBackendStepOutput.addValue("serverFilePath", tmpFile.getCanonicalPath());
}
}
@ -90,6 +94,22 @@ 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);
}
/*******************************************************************************
**
*******************************************************************************/

View File

@ -26,6 +26,7 @@ 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;
@ -106,15 +107,16 @@ public class PrepareReportForRecordStep extends PrepareReportStep
}
String reportName = runBackendStepInput.getValueString("reportName");
QReportMetaData report = runBackendStepInput.getInstance().getReport(reportName);
QReportMetaData report = QContext.getQInstance().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. //
/////////////////////////////////////////////////////////////////////////////////////
inputFieldList = (ArrayList<QFieldMetaData>) runBackendStepOutput.getValue("inputFieldList");
if(!CollectionUtils.nullSafeHasContents(inputFieldList))
@SuppressWarnings("unchecked")
ArrayList<QFieldMetaData> updatedInputFieldList = (ArrayList<QFieldMetaData>) runBackendStepOutput.getValue("inputFieldList");
if(!CollectionUtils.nullSafeHasContents(updatedInputFieldList))
{
removeInputStepFromProcess(runBackendStepOutput);
}

View File

@ -25,6 +25,7 @@ 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;
@ -56,7 +57,7 @@ public class PrepareReportStep implements BackendStep
throw (new QException("Process value [reportName] was not given."));
}
QReportMetaData report = runBackendStepInput.getInstance().getReport(reportName);
QReportMetaData report = QContext.getQInstance().getReport(reportName);
if(report == null)
{
throw (new QException("Process named [" + reportName + "] was not found in this instance."));

View File

@ -29,6 +29,7 @@ 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;
@ -44,6 +45,7 @@ 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;
/*******************************************************************************
@ -78,10 +80,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();
@ -89,6 +91,11 @@ 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()));
@ -108,6 +115,11 @@ 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);

View File

@ -145,7 +145,7 @@ public class StoreScriptRevisionProcessStep implements BackendStep
try
{
scriptRevision.setValue("author", input.getSession().getUser().getFullName());
scriptRevision.setValue("author", QContext.getQSession().getUser().getFullName());
}
catch(Exception e)
{

View File

@ -139,7 +139,7 @@ public class TestScriptProcessStep implements BackendStep
//////////////////////////////////
// send script outputs back out //
//////////////////////////////////
output.addValue("scriptLogLines", CollectionUtils.useOrWrap(testScriptOutput.getScriptLogLines(), TypeToken.get(ArrayList.class)));
output.addValue("scriptLogLines", CollectionUtils.useOrWrap(testScriptOutput.getScriptLogLines(), new TypeToken<ArrayList<QRecord>>() {}));
output.addValue("outputObject", testScriptOutput.getOutputObject());
if(testScriptOutput.getException() != null)

View File

@ -365,10 +365,10 @@ public abstract class AbstractTableSyncTransformStep extends AbstractTransformSt
{
if(possibleValueTranslator == null)
{
possibleValueTranslator = new QPossibleValueTranslator(runBackendStepInput.getInstance(), runBackendStepInput.getSession());
possibleValueTranslator = new QPossibleValueTranslator(QContext.getQInstance(), QContext.getQSession());
}
possibleValueTranslator.translatePossibleValuesInRecords(runBackendStepInput.getInstance().getTable(destinationTableName), runBackendStepOutput.getRecords());
possibleValueTranslator.translatePossibleValuesInRecords(QContext.getQInstance().getTable(destinationTableName), runBackendStepOutput.getRecords());
}
}
}

View File

@ -433,6 +433,8 @@ 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)
{

View File

@ -32,6 +32,7 @@ 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;
@ -515,7 +516,7 @@ public class GeneralProcessUtils
*******************************************************************************/
public static Integer validateSingleSelectedId(RunBackendStepInput runBackendStepInput, String tableName) throws QException
{
String tableLabel = runBackendStepInput.getInstance().getTable(tableName).getLabel();
String tableLabel = QContext.getQInstance().getTable(tableName).getLabel();
////////////////////////////////////////////////////
// Get the selected recordId and verify we only 1 //

View File

@ -70,7 +70,9 @@ public class QuartzJobRunner implements Job
QContext.init(qInstance, quartzScheduler.getSessionSupplier().get());
schedulableType = qInstance.getSchedulableType(context.getJobDetail().getJobDataMap().getString("type"));
params = (Map<String, Object>) context.getJobDetail().getJobDataMap().get("params");
@SuppressWarnings("unchecked")
Map<String, Object> paramsFromJobDataMap = (Map<String, Object>) context.getJobDetail().getJobDataMap().get("params");
params = paramsFromJobDataMap;
SchedulableRunner schedulableRunner = QCodeLoader.getAdHoc(SchedulableRunner.class, schedulableType.getRunner());

View File

@ -74,7 +74,9 @@ public class SchedulableProcessRunner implements SchedulableRunner
Map<String, Serializable> backendVariantData = null;
if(params.containsKey("backendVariantData"))
{
backendVariantData = (Map<String, Serializable>) params.get("backendVariantData");
@SuppressWarnings("unchecked")
Map<String, Serializable> dataFromMap = (Map<String, Serializable>) params.get("backendVariantData");
backendVariantData = dataFromMap;
}
Map<String, Serializable> processInputValues = buildProcessInputValuesMap(params, process);

View File

@ -26,6 +26,7 @@ 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;
@ -78,7 +79,7 @@ public class TempFileStateProvider implements StateProviderInterface
try
{
String json = JsonUtils.toJson(data);
FileUtils.writeStringToFile(getFile(key), json);
FileUtils.writeStringToFile(getFile(key), json, StandardCharsets.UTF_8);
}
catch(IOException e)
{
@ -97,7 +98,7 @@ public class TempFileStateProvider implements StateProviderInterface
{
try
{
String json = FileUtils.readFileToString(getFile(key));
String json = FileUtils.readFileToString(getFile(key), StandardCharsets.UTF_8);
return (Optional.of(JsonUtils.toObject(json, type)));
}
catch(FileNotFoundException | NoSuchFileException fnfe)

View File

@ -50,7 +50,7 @@ public class CollectionUtils
** true if c is null or it's empty
**
*******************************************************************************/
public static boolean nullSafeIsEmpty(Collection c)
public static boolean nullSafeIsEmpty(Collection<?> c)
{
if(c == null || c.isEmpty())
{
@ -66,7 +66,7 @@ public class CollectionUtils
** true if c is null or it's empty
**
*******************************************************************************/
public static boolean nullSafeIsEmpty(Map c)
public static boolean nullSafeIsEmpty(Map<?, ?> c)
{
if(c == null || c.isEmpty())
{
@ -82,7 +82,7 @@ public class CollectionUtils
** true if c is NOT null and it's not empty
**
*******************************************************************************/
public static boolean nullSafeHasContents(Collection c)
public static boolean nullSafeHasContents(Collection<?> c)
{
return (!nullSafeIsEmpty(c));
}
@ -93,7 +93,7 @@ public class CollectionUtils
** true if c is NOT null and it's not empty
**
*******************************************************************************/
public static boolean nullSafeHasContents(Map c)
public static boolean nullSafeHasContents(Map<?, ?> c)
{
return (!nullSafeIsEmpty(c));
}
@ -104,7 +104,7 @@ public class CollectionUtils
** 0 if c is empty, otherwise, its size.
**
*******************************************************************************/
public static int nullSafeSize(Collection c)
public static int nullSafeSize(Collection<?> c)
{
if(c == null)
{
@ -120,7 +120,7 @@ public class CollectionUtils
** 0 if c is empty, otherwise, its size.
**
*******************************************************************************/
public static int nullSafeSize(Map c)
public static int nullSafeSize(Map<?, ?> c)
{
if(c == null)
{
@ -302,14 +302,14 @@ public class CollectionUtils
return (rs);
}
List<T> currentPage = new LinkedList<T>();
List<T> currentPage = new LinkedList<>();
rs.add(currentPage);
for(T value : values)
{
if(currentPage.size() >= pageSize)
{
currentPage = new LinkedList<T>();
currentPage = new LinkedList<>();
rs.add(currentPage);
}
@ -423,6 +423,7 @@ public class CollectionUtils
**
** Meant to help avoid null checks on foreach loops.
*******************************************************************************/
@SuppressWarnings("unchecked")
public static <T> T[] nonNullArray(T[] array)
{
if(array == null)
@ -539,7 +540,7 @@ public class CollectionUtils
/*******************************************************************************
**
*******************************************************************************/
public static Map objectToMap(Object o)
public static Map<?, ?> objectToMap(Object o)
{
ObjectMapper mapper = new ObjectMapper()
.registerModule(new JavaTimeModule())
@ -555,6 +556,7 @@ public class CollectionUtils
/*******************************************************************************
**
*******************************************************************************/
@SafeVarargs
public static <T> List<T> mergeLists(List<T>... lists)
{
List<T> rs = new ArrayList<>();
@ -593,6 +595,7 @@ public class CollectionUtils
return (null);
}
@SuppressWarnings("unchecked")
Class<T> targetClass = (Class<T>) typeToken.getRawType();
if(targetClass.isInstance(collection))
{
@ -630,6 +633,7 @@ public class CollectionUtils
return (null);
}
@SuppressWarnings("unchecked")
Class<T> targetClass = (Class<T>) typeToken.getRawType();
if(targetClass.isInstance(collection))
{

View File

@ -0,0 +1,126 @@
/*
* 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.utils;
import java.io.Serializable;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import com.kingsrook.qqq.backend.core.utils.collections.MutableMap;
/*******************************************************************************
** Hash that provides "counting" capability -- keys map to Integers that
** are automatically/easily summed to
**
*******************************************************************************/
public class CountingHash<K extends Serializable> extends AbstractMap<K, Integer> implements Serializable
{
private Map<K, Integer> map = null;
/*******************************************************************************
** Default constructor
**
*******************************************************************************/
public CountingHash()
{
this.map = new HashMap<>();
}
/*******************************************************************************
** Constructor where you can supply a source map (e.g., if you want a specific
** Map type (like LinkedHashMap), or with pre-values.
**
** Note - the input map will be wrapped in a MutableMap - so - it'll be mutable.
**
*******************************************************************************/
public CountingHash(Map<K, Integer> sourceMap)
{
this.map = new MutableMap<>(sourceMap);
}
/*******************************************************************************
** Increment the value for the specified key by 1.
**
*******************************************************************************/
public Integer add(K key)
{
Integer value = getOrCreateListForKey(key);
Integer sum = value + 1;
map.put(key, sum);
return (sum);
}
/*******************************************************************************
** Increment the value for the specified key by the supplied addend
**
*******************************************************************************/
public Integer add(K key, Integer addend)
{
Integer value = getOrCreateListForKey(key);
Integer sum = value + addend;
map.put(key, sum);
return (sum);
}
/*******************************************************************************
**
*******************************************************************************/
private Integer getOrCreateListForKey(K key)
{
Integer value;
if(!this.map.containsKey(key))
{
this.map.put(key, 0);
value = 0;
}
else
{
value = this.map.get(key);
}
return value;
}
/***************************************************************************
*
***************************************************************************/
public Set<Entry<K, Integer>> entrySet()
{
return this.map.entrySet();
}
}

View File

@ -37,6 +37,7 @@ public class ObjectUtils
/*******************************************************************************
** A varargs version of Objects.requireNonNullElse
*******************************************************************************/
@SafeVarargs
public static <T> T requireNonNullElse(T... objects)
{
if(objects == null)

View File

@ -888,6 +888,7 @@ public class ValueUtils
** Return the first argument that isn't null.
** If all were null, return null.
*******************************************************************************/
@SafeVarargs
public static <T> T getFirstNonNull(T... ts)
{
if(ts == null || ts.length == 0)

View File

@ -48,7 +48,11 @@ public class YamlUtils
{
ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
objectMapper.findAndRegisterModules();
return (objectMapper.readValue(yaml, Map.class));
@SuppressWarnings("unchecked")
Map<String, Object> map = objectMapper.readValue(yaml, Map.class);
return map;
}

View File

@ -86,7 +86,7 @@ public class AlphaNumericComparator implements Comparator<String>
////////////////////////////////////////////////////////////////
if(INT_PATTERN.matcher(a).matches() && INT_PATTERN.matcher(b).matches())
{
int intsCompared = new Integer(a).compareTo(new Integer(b));
int intsCompared = Integer.valueOf(a).compareTo(Integer.valueOf(b));
if(intsCompared == TIE)
{
///////////////////////////////////////////////////////////////////////////////
@ -119,7 +119,7 @@ public class AlphaNumericComparator implements Comparator<String>
/////////////////////////////////////////////////////////////
// if the ints compare as non-zero, return that comparison //
/////////////////////////////////////////////////////////////
int intPartCompared = new Integer(aIntPart).compareTo(new Integer(bIntPart));
int intPartCompared = Integer.valueOf(aIntPart).compareTo(Integer.valueOf(bIntPart));
if(intPartCompared != TIE)
{
return (intPartCompared);

View File

@ -26,7 +26,6 @@ import java.util.ArrayList;
import java.util.List;
@SuppressWarnings({ "checkstyle:javadoc", "DanglingJavadoc" })
/*******************************************************************************
** List.of is "great", but annoying because it makes unmodifiable lists...
** So, replace it with this, which returns ArrayLists, which "don't suck"

View File

@ -27,7 +27,6 @@ import java.util.Map;
import java.util.function.Supplier;
@SuppressWarnings({ "checkstyle:javadoc", "DanglingJavadoc" })
/*******************************************************************************
** Map.of is "great", but annoying because it makes unmodifiable maps, and it
** NPE's on nulls... So, replace it with this, which returns HashMaps (or maps

View File

@ -87,7 +87,9 @@ public class MultiLevelMapHelper
{
try
{
return (map.getClass().getConstructor().newInstance());
@SuppressWarnings("unchecked")
Map<K2, V> map1 = map.getClass().getConstructor().newInstance();
return map1;
}
catch(Exception e)
{

View File

@ -174,7 +174,7 @@ class StandardScheduledExecutorTest extends BaseTest
@Override
public void execute(RecordAutomationInput recordAutomationInput) throws QException
{
sessionId = recordAutomationInput.getSession().getIdReference();
sessionId = QContext.getQSession().getIdReference();
}
}

View File

@ -99,7 +99,7 @@ class ChildRecordListRendererTest extends BaseTest
.getWidgetMetaData();
qInstance.addWidget(widget);
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_ORDER), List.of(
TestUtils.insertRecords(qInstance.getTable(TestUtils.TABLE_NAME_ORDER), List.of(
new QRecord().withValue("id", 1)
));
@ -130,12 +130,12 @@ class ChildRecordListRendererTest extends BaseTest
.getWidgetMetaData();
qInstance.addWidget(widget);
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_ORDER), List.of(
TestUtils.insertRecords(qInstance.getTable(TestUtils.TABLE_NAME_ORDER), List.of(
new QRecord().withValue("id", 1),
new QRecord().withValue("id", 2)
));
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_LINE_ITEM), List.of(
TestUtils.insertRecords(qInstance.getTable(TestUtils.TABLE_NAME_LINE_ITEM), List.of(
new QRecord().withValue("orderId", 1).withValue("sku", "ABC").withValue("lineNumber", 2),
new QRecord().withValue("orderId", 1).withValue("sku", "BCD").withValue("lineNumber", 1),
new QRecord().withValue("orderId", 2).withValue("sku", "XYZ") // should not be found.

View File

@ -128,7 +128,7 @@ class ParentWidgetRendererTest extends BaseTest
QContext.getQSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_TYPE_STORE_ALL_ACCESS, true);
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_ORDER), List.of(
TestUtils.insertRecords(qInstance.getTable(TestUtils.TABLE_NAME_ORDER), List.of(
new QRecord().withValue("id", 1)
));
@ -161,12 +161,12 @@ class ParentWidgetRendererTest extends BaseTest
QContext.getQSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_TYPE_STORE_ALL_ACCESS, true);
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_ORDER), List.of(
TestUtils.insertRecords(qInstance.getTable(TestUtils.TABLE_NAME_ORDER), List.of(
new QRecord().withValue("id", 1),
new QRecord().withValue("id", 2)
));
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_LINE_ITEM), List.of(
TestUtils.insertRecords(qInstance.getTable(TestUtils.TABLE_NAME_LINE_ITEM), List.of(
new QRecord().withValue("orderId", 1).withValue("sku", "ABC").withValue("lineNumber", 2),
new QRecord().withValue("orderId", 1).withValue("sku", "BCD").withValue("lineNumber", 1),
new QRecord().withValue("orderId", 2).withValue("sku", "XYZ") // should not be found.

View File

@ -109,7 +109,7 @@ class ProcessWidgetRendererTest extends BaseTest
.getWidgetMetaData();
qInstance.addWidget(widget);
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_ORDER), List.of(
TestUtils.insertRecords(qInstance.getTable(TestUtils.TABLE_NAME_ORDER), List.of(
new QRecord().withValue("id", 1)
));
@ -140,12 +140,12 @@ class ProcessWidgetRendererTest extends BaseTest
.getWidgetMetaData();
qInstance.addWidget(widget);
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_ORDER), List.of(
TestUtils.insertRecords(qInstance.getTable(TestUtils.TABLE_NAME_ORDER), List.of(
new QRecord().withValue("id", 1),
new QRecord().withValue("id", 2)
));
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_LINE_ITEM), List.of(
TestUtils.insertRecords(qInstance.getTable(TestUtils.TABLE_NAME_LINE_ITEM), List.of(
new QRecord().withValue("orderId", 1).withValue("sku", "ABC").withValue("lineNumber", 2),
new QRecord().withValue("orderId", 1).withValue("sku", "BCD").withValue("lineNumber", 1),
new QRecord().withValue("orderId", 2).withValue("sku", "XYZ") // should not be found.

View File

@ -1,5 +1,22 @@
/*
* Copyright © 2022-2023. ColdTrack <contact@coldtrack.com>. All Rights Reserved.
* 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.dashboard.widgets;

View File

@ -1,5 +1,22 @@
/*
* Copyright © 2022-2023. ColdTrack <contact@coldtrack.com>. All Rights Reserved.
* 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.dashboard.widgets;

View File

@ -116,7 +116,7 @@ class USMapRendererTest extends BaseTest
.getWidgetMetaData();
qInstance.addWidget(widget);
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_ORDER), List.of(
TestUtils.insertRecords(qInstance.getTable(TestUtils.TABLE_NAME_ORDER), List.of(
new QRecord().withValue("id", 1)
));
@ -147,12 +147,12 @@ class USMapRendererTest extends BaseTest
.getWidgetMetaData();
qInstance.addWidget(widget);
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_ORDER), List.of(
TestUtils.insertRecords(qInstance.getTable(TestUtils.TABLE_NAME_ORDER), List.of(
new QRecord().withValue("id", 1),
new QRecord().withValue("id", 2)
));
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_LINE_ITEM), List.of(
TestUtils.insertRecords(qInstance.getTable(TestUtils.TABLE_NAME_LINE_ITEM), List.of(
new QRecord().withValue("orderId", 1).withValue("sku", "ABC").withValue("lineNumber", 2),
new QRecord().withValue("orderId", 1).withValue("sku", "BCD").withValue("lineNumber", 1),
new QRecord().withValue("orderId", 2).withValue("sku", "XYZ") // should not be found.

View File

@ -67,7 +67,8 @@ class ProcessMetaDataActionTest extends BaseTest
@Test
public void test_notFound()
{
assertThrows(QNotFoundException.class, () -> {
assertThrows(QNotFoundException.class, () ->
{
ProcessMetaDataInput request = new ProcessMetaDataInput();
request.setProcessName("willNotBeFound");
new ProcessMetaDataAction().execute(request);

View File

@ -65,7 +65,8 @@ class TableMetaDataActionTest extends BaseTest
@Test
public void test_notFound()
{
assertThrows(QUserFacingException.class, () -> {
assertThrows(QUserFacingException.class, () ->
{
TableMetaDataInput request = new TableMetaDataInput();
request.setTableName("willNotBeFound");
new TableMetaDataAction().execute(request);

View File

@ -51,16 +51,16 @@ public class RunProcessUpdateStepListTest extends BaseTest
{
private static final String PROCESS_NAME = RunProcessUpdateStepListTest.class.getSimpleName();
private final static String STEP_START = "start";
private final static String STEP_A = "a";
private final static String STEP_B = "b";
private final static String STEP_C = "c";
private final static String STEP_1 = "1";
private final static String STEP_2 = "2";
private final static String STEP_3 = "3";
private final static String STEP_END = "end";
private static final String STEP_START = "start";
private static final String STEP_A = "a";
private static final String STEP_B = "b";
private static final String STEP_C = "c";
private static final String STEP_1 = "1";
private static final String STEP_2 = "2";
private static final String STEP_3 = "3";
private static final String STEP_END = "end";
private final static List<String> LETTERS_STEP_LIST = List.of(
private static final List<String> LETTERS_STEP_LIST = List.of(
STEP_START,
STEP_A,
STEP_B,
@ -68,7 +68,7 @@ public class RunProcessUpdateStepListTest extends BaseTest
STEP_END
);
private final static List<String> NUMBERS_STEP_LIST = List.of(
private static final List<String> NUMBERS_STEP_LIST = List.of(
STEP_START,
STEP_1,
STEP_2,

View File

@ -155,12 +155,12 @@ class ExportActionTest extends BaseTest
QInstance qInstance = QContext.getQInstance();
QContext.getQSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_TYPE_STORE_ALL_ACCESS, true);
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_ORDER), List.of(
TestUtils.insertRecords(qInstance.getTable(TestUtils.TABLE_NAME_ORDER), List.of(
new QRecord().withValue("id", 1).withValue("orderNo", "ORD1").withValue("storeId", 1),
new QRecord().withValue("id", 2).withValue("orderNo", "ORD2").withValue("storeId", 1)
));
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_LINE_ITEM), List.of(
TestUtils.insertRecords(qInstance.getTable(TestUtils.TABLE_NAME_LINE_ITEM), List.of(
new QRecord().withValue("id", 1).withValue("orderId", 1).withValue("sku", "A").withValue("quantity", 10),
new QRecord().withValue("id", 2).withValue("orderId", 1).withValue("sku", "B").withValue("quantity", 15),
new QRecord().withValue("id", 3).withValue("orderId", 2).withValue("sku", "A").withValue("quantity", 20)

View File

@ -562,7 +562,7 @@ public class GenerateReportActionTest extends BaseTest
*******************************************************************************/
public static void insertPersonRecords(QInstance qInstance) throws QException
{
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY), List.of(
TestUtils.insertRecords(qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY), List.of(
new PersonQRecord().withFirstName("Darin").withLastName("Jonson").withBirthDate(LocalDate.of(1980, Month.JANUARY, 31)).withNoOfShoes(null).withHomeStateId(1).withPrice(null).withCost(new BigDecimal("0.50")), // wrong last initial
new PersonQRecord().withFirstName("Darin").withLastName("Jones").withBirthDate(LocalDate.of(1980, Month.JANUARY, 31)).withNoOfShoes(3).withHomeStateId(1).withPrice(new BigDecimal("1.00")).withCost(new BigDecimal("0.50")), // wrong last initial
new PersonQRecord().withFirstName("Darin").withLastName("Kelly").withBirthDate(LocalDate.of(1979, Month.DECEMBER, 30)).withNoOfShoes(4).withHomeStateId(1).withPrice(new BigDecimal("1.20")).withCost(new BigDecimal("0.50")), // bad birthdate

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