diff --git a/pom.xml b/pom.xml
index e465e8f3..a96d408b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -150,7 +150,7 @@
com.puppycrawl.tools
checkstyle
- 9.0
+ 10.16.0
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/AutomationStatus.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/AutomationStatus.java
index 14635b62..a6ab6159 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/AutomationStatus.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/AutomationStatus.java
@@ -127,7 +127,6 @@ public enum AutomationStatus implements PossibleValueEnum
/*******************************************************************************
**
*******************************************************************************/
- @SuppressWarnings("checkstyle:indentation")
public String getInsertOrUpdate()
{
return switch(this)
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/customizers/RecordCustomizerUtilityInterface.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/customizers/RecordCustomizerUtilityInterface.java
index ff8c2d82..afb54a71 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/customizers/RecordCustomizerUtilityInterface.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/customizers/RecordCustomizerUtilityInterface.java
@@ -50,10 +50,7 @@ public interface RecordCustomizerUtilityInterface
/*******************************************************************************
** Container for an old value and a new value.
*******************************************************************************/
- @SuppressWarnings("checkstyle:MethodName")
- record Change(Serializable oldValue, Serializable newValue)
- {
- }
+ record Change(Serializable oldValue, Serializable newValue) {}
/*******************************************************************************
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/DateTimeGroupBy.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/DateTimeGroupBy.java
index 2cc0ba81..47cedbf9 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/DateTimeGroupBy.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/DateTimeGroupBy.java
@@ -256,7 +256,6 @@ public enum DateTimeGroupBy
/*******************************************************************************
**
*******************************************************************************/
- @SuppressWarnings("checkstyle:indentation")
public Instant roundDown(Instant instant, ZoneId zoneId)
{
ZonedDateTime zoned = instant.atZone(zoneId);
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/permissions/PermissionsHelper.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/permissions/PermissionsHelper.java
index 57a368f7..ab1b56ca 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/permissions/PermissionsHelper.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/permissions/PermissionsHelper.java
@@ -500,7 +500,6 @@ public class PermissionsHelper
/*******************************************************************************
**
*******************************************************************************/
- @SuppressWarnings("checkstyle:indentation")
static PermissionSubType getEffectivePermissionSubType(QPermissionRules rules, PermissionSubType originalPermissionSubType)
{
if(rules == null || rules.getLevel() == null)
@@ -515,10 +514,10 @@ public class PermissionsHelper
if(PrivatePermissionSubType.HAS_ACCESS.equals(originalPermissionSubType))
{
return switch(rules.getLevel())
- {
- case NOT_PROTECTED -> null;
- default -> PrivatePermissionSubType.HAS_ACCESS;
- };
+ {
+ case NOT_PROTECTED -> null;
+ default -> PrivatePermissionSubType.HAS_ACCESS;
+ };
}
else
{
@@ -527,30 +526,30 @@ public class PermissionsHelper
// permission sub-type to what we expect to be set for the table //
////////////////////////////////////////////////////////////////////////////////////////////////////////
return switch(rules.getLevel())
+ {
+ case NOT_PROTECTED -> null;
+ case HAS_ACCESS_PERMISSION -> PrivatePermissionSubType.HAS_ACCESS;
+ case READ_WRITE_PERMISSIONS ->
{
- case NOT_PROTECTED -> null;
- case HAS_ACCESS_PERMISSION -> PrivatePermissionSubType.HAS_ACCESS;
- case READ_WRITE_PERMISSIONS ->
+ if(PrivatePermissionSubType.READ.equals(originalPermissionSubType) || PrivatePermissionSubType.WRITE.equals(originalPermissionSubType))
{
- if(PrivatePermissionSubType.READ.equals(originalPermissionSubType) || PrivatePermissionSubType.WRITE.equals(originalPermissionSubType))
- {
- yield (originalPermissionSubType);
- }
- else if(TablePermissionSubType.INSERT.equals(originalPermissionSubType) || TablePermissionSubType.EDIT.equals(originalPermissionSubType) || TablePermissionSubType.DELETE.equals(originalPermissionSubType))
- {
- yield (PrivatePermissionSubType.WRITE);
- }
- else if(TablePermissionSubType.READ.equals(originalPermissionSubType))
- {
- yield (PrivatePermissionSubType.READ);
- }
- else
- {
- throw new IllegalStateException("Unexpected permissionSubType: " + originalPermissionSubType);
- }
+ yield (originalPermissionSubType);
}
- case READ_INSERT_EDIT_DELETE_PERMISSIONS -> originalPermissionSubType;
- };
+ else if(TablePermissionSubType.INSERT.equals(originalPermissionSubType) || TablePermissionSubType.EDIT.equals(originalPermissionSubType) || TablePermissionSubType.DELETE.equals(originalPermissionSubType))
+ {
+ yield (PrivatePermissionSubType.WRITE);
+ }
+ else if(TablePermissionSubType.READ.equals(originalPermissionSubType))
+ {
+ yield (PrivatePermissionSubType.READ);
+ }
+ else
+ {
+ throw new IllegalStateException("Unexpected permissionSubType: " + originalPermissionSubType);
+ }
+ }
+ case READ_INSERT_EDIT_DELETE_PERMISSIONS -> originalPermissionSubType;
+ };
}
}
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/processes/RunBackendStepAction.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/processes/RunBackendStepAction.java
index 306d365b..8b0d7dd3 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/processes/RunBackendStepAction.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/processes/RunBackendStepAction.java
@@ -35,6 +35,7 @@ import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
import com.kingsrook.qqq.backend.core.logging.QLogger;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
+import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
@@ -93,7 +94,7 @@ public class RunBackendStepAction
//////////////////////////////////////////////////////////////////////////////////////
// ensure input data is set as needed - use callback object to get anything missing //
//////////////////////////////////////////////////////////////////////////////////////
- ensureRecordsAreInRequest(runBackendStepInput, backendStepMetaData);
+ ensureRecordsAreInRequest(runBackendStepInput, backendStepMetaData, process);
ensureInputFieldsAreInRequest(runBackendStepInput, backendStepMetaData);
////////////////////////////////////////////////////////////////////
@@ -178,7 +179,7 @@ public class RunBackendStepAction
** check if this step uses a record list - and if so, if we need to get one
** via the callback
*******************************************************************************/
- private void ensureRecordsAreInRequest(RunBackendStepInput runBackendStepInput, QBackendStepMetaData step) throws QException
+ private void ensureRecordsAreInRequest(RunBackendStepInput runBackendStepInput, QBackendStepMetaData step, QProcessMetaData process) throws QException
{
QFunctionInputMetaData inputMetaData = step.getInputMetaData();
if(inputMetaData != null && inputMetaData.getRecordListMetaData() != null)
@@ -201,9 +202,44 @@ public class RunBackendStepAction
queryInput.setFilter(callback.getQueryFilter());
+ //////////////////////////////////////////////////////////////////////////////////////////
+ // if process has a max-no of records, set a limit on the process of that number plus 1 //
+ // (the plus 1 being so we can see "oh, you selected more than that many; error!" //
+ //////////////////////////////////////////////////////////////////////////////////////////
+ if(process.getMaxInputRecords() != null)
+ {
+ if(callback.getQueryFilter() == null)
+ {
+ queryInput.setFilter(new QQueryFilter());
+ }
+
+ queryInput.getFilter().setLimit(process.getMaxInputRecords() + 1);
+ }
+
QueryOutput queryOutput = new QueryAction().execute(queryInput);
runBackendStepInput.setRecords(queryOutput.getRecords());
- // todo - handle 0 results found?
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // if process defines a max, and more than the max were found, throw an error //
+ ////////////////////////////////////////////////////////////////////////////////
+ if(process.getMaxInputRecords() != null)
+ {
+ if(queryOutput.getRecords().size() > process.getMaxInputRecords())
+ {
+ throw (new QUserFacingException("Too many records were selected for this process. At most, only " + process.getMaxInputRecords() + " can be selected."));
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////
+ // if process defines a min, and fewer than the min were found, throw an error //
+ /////////////////////////////////////////////////////////////////////////////////
+ if(process.getMinInputRecords() != null)
+ {
+ if(queryOutput.getRecords().size() < process.getMinInputRecords())
+ {
+ throw (new QUserFacingException("Too few records were selected for this process. At least " + process.getMinInputRecords() + " must be selected."));
+ }
+ }
}
}
}
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/reporting/GenerateReportAction.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/reporting/GenerateReportAction.java
index aad1aaa1..3c2ace5c 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/reporting/GenerateReportAction.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/reporting/GenerateReportAction.java
@@ -457,7 +457,7 @@ public class GenerateReportAction extends AbstractQActionFunction onlyWriteLocks = RecordSecurityLockFilters.filterForOnlyWriteLocks(CollectionUtils.nonNullList(table.getRecordSecurityLocks()));
for(List page : CollectionUtils.getPages(updateInput.getRecords(), 1000))
@@ -395,7 +399,7 @@ public class UpdateAction
QFieldType fieldType = table.getField(lock.getFieldName()).getType();
Serializable lockValue = ValueUtils.getValueAsFieldType(fieldType, oldRecord.getValue(lock.getFieldName()));
- List errors = ValidateRecordSecurityLockHelper.validateRecordSecurityValue(table, lock, lockValue, fieldType, ValidateRecordSecurityLockHelper.Action.UPDATE);
+ List errors = ValidateRecordSecurityLockHelper.validateRecordSecurityValue(table, lock, lockValue, fieldType, ValidateRecordSecurityLockHelper.Action.UPDATE, Collections.emptyMap());
if(CollectionUtils.nullSafeHasContents(errors))
{
errors.forEach(e -> record.addError(e));
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/helpers/ValidateRecordSecurityLockHelper.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/helpers/ValidateRecordSecurityLockHelper.java
index c8de0e4f..dcc730d9 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/helpers/ValidateRecordSecurityLockHelper.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/helpers/ValidateRecordSecurityLockHelper.java
@@ -101,7 +101,7 @@ public class ValidateRecordSecurityLockHelper
// actually check lock values //
////////////////////////////////
Map errorRecords = new HashMap<>();
- evaluateRecordLocks(table, records, action, locksToCheck, errorRecords, new ArrayList<>());
+ evaluateRecordLocks(table, records, action, locksToCheck, errorRecords, new ArrayList<>(), madeUpPrimaryKeys);
/////////////////////////////////
// propagate errors to records //
@@ -141,7 +141,7 @@ public class ValidateRecordSecurityLockHelper
** BUT - WRITE locks - in their case, we read the record no matter what, and in
** here we need to verify we have a key that allows us to WRITE the record.
*******************************************************************************/
- private static void evaluateRecordLocks(QTableMetaData table, List records, Action action, RecordSecurityLock recordSecurityLock, Map errorRecords, List treePosition) throws QException
+ private static void evaluateRecordLocks(QTableMetaData table, List records, Action action, RecordSecurityLock recordSecurityLock, Map errorRecords, List treePosition, Map madeUpPrimaryKeys) throws QException
{
if(recordSecurityLock instanceof MultiRecordSecurityLock multiRecordSecurityLock)
{
@@ -152,7 +152,7 @@ public class ValidateRecordSecurityLockHelper
for(RecordSecurityLock childLock : CollectionUtils.nonNullList(multiRecordSecurityLock.getLocks()))
{
treePosition.add(i);
- evaluateRecordLocks(table, records, action, childLock, errorRecords, treePosition);
+ evaluateRecordLocks(table, records, action, childLock, errorRecords, treePosition, madeUpPrimaryKeys);
treePosition.remove(treePosition.size() - 1);
i++;
}
@@ -192,7 +192,7 @@ public class ValidateRecordSecurityLockHelper
}
Serializable recordSecurityValue = record.getValue(field.getName());
- List recordErrors = validateRecordSecurityValue(table, recordSecurityLock, recordSecurityValue, field.getType(), action);
+ List recordErrors = validateRecordSecurityValue(table, recordSecurityLock, recordSecurityValue, field.getType(), action, madeUpPrimaryKeys);
if(CollectionUtils.nullSafeHasContents(recordErrors))
{
errorRecords.computeIfAbsent(record.getValue(primaryKeyField), (k) -> new RecordWithErrors(record)).addAll(recordErrors, treePosition);
@@ -337,7 +337,7 @@ public class ValidateRecordSecurityLockHelper
for(QRecord inputRecord : inputRecords)
{
- List recordErrors = validateRecordSecurityValue(table, recordSecurityLock, recordSecurityValue, field.getType(), action);
+ List recordErrors = validateRecordSecurityValue(table, recordSecurityLock, recordSecurityValue, field.getType(), action, madeUpPrimaryKeys);
if(CollectionUtils.nullSafeHasContents(recordErrors))
{
errorRecords.computeIfAbsent(inputRecord.getValue(primaryKeyField), (k) -> new RecordWithErrors(inputRecord)).addAll(recordErrors, treePosition);
@@ -370,14 +370,14 @@ public class ValidateRecordSecurityLockHelper
{
String primaryKeyField = table.getPrimaryKeyField();
Map madeUpPrimaryKeys = new HashMap<>();
- Integer madeUpPrimaryKey = -1;
+ Integer madeUpPrimaryKey = Integer.MIN_VALUE / 2;
for(QRecord record : records)
{
if(record.getValue(primaryKeyField) == null)
{
madeUpPrimaryKeys.put(madeUpPrimaryKey, record);
record.setValue(primaryKeyField, madeUpPrimaryKey);
- madeUpPrimaryKey--;
+ madeUpPrimaryKey++;
}
}
return madeUpPrimaryKeys;
@@ -390,7 +390,6 @@ public class ValidateRecordSecurityLockHelper
** MultiRecordSecurityLock, with only the appropriate lock-scopes being included
** (e.g., read-locks for selects, write-locks for insert/update/delete).
*******************************************************************************/
- @SuppressWarnings("checkstyle:Indentation")
static MultiRecordSecurityLock getRecordSecurityLocks(QTableMetaData table, Action action)
{
List allLocksOnTable = CollectionUtils.nonNullList(table.getRecordSecurityLocks());
@@ -445,9 +444,9 @@ public class ValidateRecordSecurityLockHelper
/*******************************************************************************
**
*******************************************************************************/
- public static List validateRecordSecurityValue(QTableMetaData table, RecordSecurityLock recordSecurityLock, Serializable recordSecurityValue, QFieldType fieldType, Action action)
+ public static List validateRecordSecurityValue(QTableMetaData table, RecordSecurityLock recordSecurityLock, Serializable recordSecurityValue, QFieldType fieldType, Action action, Map madeUpPrimaryKeys)
{
- if(recordSecurityValue == null)
+ if(recordSecurityValue == null || (madeUpPrimaryKeys != null && madeUpPrimaryKeys.containsKey(recordSecurityValue)))
{
/////////////////////////////////////////////////////////////////
// handle null values - error if the NullValueBehavior is DENY //
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/values/QPossibleValueTranslator.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/values/QPossibleValueTranslator.java
index 6d49cc9a..8fac20fc 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/values/QPossibleValueTranslator.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/values/QPossibleValueTranslator.java
@@ -118,6 +118,7 @@ public class QPossibleValueTranslator
}
+
/*******************************************************************************
** Constructor
**
@@ -421,7 +422,6 @@ public class QPossibleValueTranslator
/*******************************************************************************
**
*******************************************************************************/
- @SuppressWarnings("checkstyle:Indentation")
private String doFormatPossibleValue(String formatString, List valueFields, Object id, String label)
{
List