Convert QRecord errors and warnings to new QStatusMessage type hierarchy.

This commit is contained in:
2023-05-09 08:49:46 -05:00
parent 647c5968d3
commit b2c7062709
28 changed files with 555 additions and 130 deletions

View File

@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.core.actions.customizers;
import java.util.List;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
@ -50,7 +51,7 @@ public abstract class AbstractPreInsertCustomizer
/*******************************************************************************
**
*******************************************************************************/
public abstract List<QRecord> apply(List<QRecord> records);
public abstract List<QRecord> apply(List<QRecord> records) throws QException;

View File

@ -34,6 +34,8 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput;
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.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.statusmessages.QStatusMessage;
import com.kingsrook.qqq.backend.core.model.statusmessages.QWarningMessage;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
@ -142,9 +144,9 @@ public abstract class ChildInserterPostInsertCustomizer extends AbstractPostInse
QRecord childRecord = insertedRecordIterator.next();
if(CollectionUtils.nullSafeHasContents(childRecord.getErrors()))
{
for(String childWarning : childRecord.getErrors())
for(QStatusMessage error : childRecord.getErrors())
{
record.addWarning("Error creating child " + childTable.getLabel() + " (" + childWarning + ")");
record.addWarning(new QWarningMessage("Error creating child " + childTable.getLabel() + " (" + error.toString() + ")"));
}
rs.add(record);
continue;

View File

@ -54,6 +54,8 @@ import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.Association;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.statusmessages.NotFoundStatusMessage;
import com.kingsrook.qqq.backend.core.model.statusmessages.QWarningMessage;
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;
@ -68,8 +70,6 @@ public class DeleteAction
{
private static final QLogger LOG = QLogger.getLogger(DeleteAction.class);
public static final String NOT_FOUND_ERROR_PREFIX = "No record was found to delete";
/*******************************************************************************
@ -238,7 +238,7 @@ public class DeleteAction
{
for(QRecord record : recordsForCustomizer)
{
record.addWarning("An error occurred after the delete: " + e.getMessage());
record.addWarning(new QWarningMessage("An error occurred after the delete: " + e.getMessage()));
outputRecordsWithWarnings.add(record);
}
}
@ -400,7 +400,7 @@ public class DeleteAction
QRecord recordWithError = new QRecord();
recordsWithErrors.add(recordWithError);
recordWithError.setValue(primaryKeyField.getName(), primaryKeyValue);
recordWithError.addError(NOT_FOUND_ERROR_PREFIX + " for " + primaryKeyField.getLabel() + " = " + primaryKeyValue);
recordWithError.addError(new NotFoundStatusMessage("No record was found to delete for " + primaryKeyField.getLabel() + " = " + primaryKeyValue));
primaryKeysToRemoveFromInput.add(primaryKeyValue);
}
}

View File

@ -58,6 +58,8 @@ import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.Association;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.UniqueKey;
import com.kingsrook.qqq.backend.core.model.statusmessages.BadInputStatusMessage;
import com.kingsrook.qqq.backend.core.model.statusmessages.QWarningMessage;
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;
@ -122,7 +124,7 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
//////////////////////////////
// log if there were errors //
//////////////////////////////
List<String> errors = insertOutput.getRecords().stream().flatMap(r -> r.getErrors().stream()).toList();
List<String> errors = insertOutput.getRecords().stream().flatMap(r -> r.getErrors().stream().map(Object::toString)).toList();
if(CollectionUtils.nullSafeHasContents(errors))
{
LOG.warn("Errors in insertAction", logPair("tableName", table.getName()), logPair("errorCount", errors.size()), errors.size() < 10 ? logPair("errors", errors) : logPair("first10Errors", errors.subList(0, 10)));
@ -160,7 +162,7 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
{
for(QRecord record : insertOutput.getRecords())
{
record.addWarning("An error occurred after the insert: " + e.getMessage());
record.addWarning(new QWarningMessage("An error occurred after the insert: " + e.getMessage()));
}
}
}
@ -188,7 +190,7 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
{
if(record.getValue(requiredField.getName()) == null || (requiredField.getType().isStringLike() && record.getValueString(requiredField.getName()).trim().equals("")))
{
record.addError("Missing value in required field: " + requiredField.getLabel());
record.addError(new BadInputStatusMessage("Missing value in required field: " + requiredField.getLabel()));
}
}
}
@ -274,7 +276,7 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
Optional<List<Serializable>> keyValues = UniqueKeyHelper.getKeyValues(table, uniqueKey, record);
if(keyValues.isPresent() && (existingKeys.get(uniqueKey).contains(keyValues.get()) || keysInThisList.get(uniqueKey).contains(keyValues.get())))
{
record.addError("Another record already exists with this " + uniqueKey.getDescription(table));
record.addError(new BadInputStatusMessage("Another record already exists with this " + uniqueKey.getDescription(table)));
foundDupe = true;
break;
}

View File

@ -62,6 +62,9 @@ import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.Association;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.statusmessages.BadInputStatusMessage;
import com.kingsrook.qqq.backend.core.model.statusmessages.NotFoundStatusMessage;
import com.kingsrook.qqq.backend.core.model.statusmessages.QWarningMessage;
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;
@ -77,8 +80,6 @@ public class UpdateAction
{
private static final QLogger LOG = QLogger.getLogger(UpdateAction.class);
public static final String NOT_FOUND_ERROR_PREFIX = "No record was found to update";
/*******************************************************************************
@ -137,7 +138,7 @@ public class UpdateAction
//////////////////////////////
// log if there were errors //
//////////////////////////////
List<String> errors = updateOutput.getRecords().stream().flatMap(r -> r.getErrors().stream()).toList();
List<String> errors = updateOutput.getRecords().stream().flatMap(r -> r.getErrors().stream().map(Object::toString)).toList();
if(CollectionUtils.nullSafeHasContents(errors))
{
LOG.warn("Errors in updateAction", logPair("tableName", updateInput.getTableName()), logPair("errorCount", errors.size()), errors.size() < 10 ? logPair("errors", errors) : logPair("first10Errors", errors.subList(0, 10)));
@ -180,7 +181,7 @@ public class UpdateAction
{
for(QRecord record : updateOutput.getRecords())
{
record.addWarning("An error occurred after the update: " + e.getMessage());
record.addWarning(new QWarningMessage("An error occurred after the update: " + e.getMessage()));
}
}
}
@ -228,7 +229,7 @@ public class UpdateAction
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if(record.getValue(table.getPrimaryKeyField()) == null)
{
record.addError("Missing value in primary key field");
record.addError(new BadInputStatusMessage("Missing value in primary key field"));
}
}
}
@ -288,7 +289,7 @@ public class UpdateAction
if(!lookedUpRecords.containsKey(value))
{
record.addError(NOT_FOUND_ERROR_PREFIX + " for " + primaryKeyField.getLabel() + " = " + value);
record.addError(new NotFoundStatusMessage("No record was found to update for " + primaryKeyField.getLabel() + " = " + value));
}
}
}
@ -319,7 +320,7 @@ public class UpdateAction
{
if(record.getValue(requiredField.getName()) == null || (requiredField.getType().isStringLike() && record.getValueString(requiredField.getName()).trim().equals("")))
{
record.addError("Missing value in required field: " + requiredField.getLabel());
record.addError(new BadInputStatusMessage("Missing value in required field: " + requiredField.getLabel()));
}
}
}

View File

@ -45,6 +45,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.security.QSecurityKeyType;
import com.kingsrook.qqq.backend.core.model.metadata.security.RecordSecurityLock;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.statusmessages.PermissionDeniedMessage;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.ListingHash;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
@ -225,7 +226,7 @@ public class ValidateRecordSecurityLockHelper
{
if(RecordSecurityLock.NullValueBehavior.DENY.equals(recordSecurityLock.getNullValueBehavior()))
{
inputRecord.addError("You do not have permission to " + action.name().toLowerCase() + " this record - the referenced " + leftMostJoinTable.getLabel() + " was not found.");
inputRecord.addError(new PermissionDeniedMessage("You do not have permission to " + action.name().toLowerCase() + " this record - the referenced " + leftMostJoinTable.getLabel() + " was not found."));
}
}
}
@ -287,7 +288,7 @@ public class ValidateRecordSecurityLockHelper
if(RecordSecurityLock.NullValueBehavior.DENY.equals(recordSecurityLock.getNullValueBehavior()))
{
String lockLabel = CollectionUtils.nullSafeHasContents(recordSecurityLock.getJoinNameChain()) ? recordSecurityLock.getSecurityKeyType() : table.getField(recordSecurityLock.getFieldName()).getLabel();
record.addError("You do not have permission to " + action.name().toLowerCase() + " a record without a value in the field: " + lockLabel);
record.addError(new PermissionDeniedMessage("You do not have permission to " + action.name().toLowerCase() + " a record without a value in the field: " + lockLabel));
}
}
else
@ -299,12 +300,12 @@ public class ValidateRecordSecurityLockHelper
///////////////////////////////////////////////////////////////////////////////////////////////
// avoid telling the user a value from a foreign record that they didn't pass in themselves. //
///////////////////////////////////////////////////////////////////////////////////////////////
record.addError("You do not have permission to " + action.name().toLowerCase() + " this record.");
record.addError(new PermissionDeniedMessage("You do not have permission to " + action.name().toLowerCase() + " this record."));
}
else
{
QFieldMetaData field = table.getField(recordSecurityLock.getFieldName());
record.addError("You do not have permission to " + action.name().toLowerCase() + " a record with a value of " + recordSecurityValue + " in the field: " + field.getLabel());
record.addError(new PermissionDeniedMessage("You do not have permission to " + action.name().toLowerCase() + " a record with a value of " + recordSecurityValue + " in the field: " + field.getLabel()));
}
}
}

View File

@ -29,6 +29,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.model.metadata.fields.ValueTooLongBehavior;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.statusmessages.BadInputStatusMessage;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
@ -76,7 +77,7 @@ public class ValueBehaviorApplier
{
case TRUNCATE -> record.setValue(fieldName, StringUtils.safeTruncate(value, field.getMaxLength()));
case TRUNCATE_ELLIPSIS -> record.setValue(fieldName, StringUtils.safeTruncate(value, field.getMaxLength(), "..."));
case ERROR -> record.addError("The value for " + field.getLabel() + " is too long (max allowed length=" + field.getMaxLength() + ")");
case ERROR -> record.addError(new BadInputStatusMessage("The value for " + field.getLabel() + " is too long (max allowed length=" + field.getMaxLength() + ")"));
case PASS_THROUGH ->
{
}

View File

@ -35,6 +35,8 @@ import java.util.Map;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.statusmessages.QErrorMessage;
import com.kingsrook.qqq.backend.core.model.statusmessages.QWarningMessage;
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
import org.apache.commons.lang.SerializationUtils;
@ -54,7 +56,8 @@ import org.apache.commons.lang.SerializationUtils;
**
** Errors are meant to hold information about things that went wrong when
** processing a record - e.g., in a list of records that may be the output of an
** action, like a bulk load. TODO - redo as some status object?
** action, like a bulk load. Warnings play a similar role, but are just advice
** - they don't mean that the action was failed, just something you may need to know.
*******************************************************************************/
public class QRecord implements Serializable
{
@ -64,8 +67,9 @@ public class QRecord implements Serializable
private Map<String, Serializable> values = new LinkedHashMap<>();
private Map<String, String> displayValues = new LinkedHashMap<>();
private Map<String, Serializable> backendDetails = new LinkedHashMap<>();
private List<String> errors = new ArrayList<>();
private List<String> warnings = new ArrayList<>();
private List<QErrorMessage> errors = new ArrayList<>();
private List<QWarningMessage> warnings = new ArrayList<>();
private Map<String, List<QRecord>> associatedRecords = new HashMap<>();
@ -106,6 +110,7 @@ public class QRecord implements Serializable
this.displayValues = doDeepCopy(record.displayValues);
this.backendDetails = doDeepCopy(record.backendDetails);
this.errors = doDeepCopy(record.errors);
this.warnings = doDeepCopy(record.warnings);
this.associatedRecords = doDeepCopy(record.associatedRecords);
}
@ -123,7 +128,7 @@ public class QRecord implements Serializable
/*******************************************************************************
**
** todo - move to a cloning utils maybe?
*******************************************************************************/
@SuppressWarnings({ "unchecked" })
private <K, V> Map<K, V> doDeepCopy(Map<K, V> map)
@ -144,7 +149,7 @@ public class QRecord implements Serializable
/*******************************************************************************
**
** todo - move to a cloning utils maybe?
*******************************************************************************/
@SuppressWarnings({ "unchecked" })
private <T> List<T> doDeepCopy(List<T> list)
@ -542,7 +547,7 @@ public class QRecord implements Serializable
** Getter for errors
**
*******************************************************************************/
public List<String> getErrors()
public List<QErrorMessage> getErrors()
{
return (errors);
}
@ -553,7 +558,7 @@ public class QRecord implements Serializable
** Setter for errors
**
*******************************************************************************/
public void setErrors(List<String> errors)
public void setErrors(List<QErrorMessage> errors)
{
this.errors = errors;
}
@ -564,7 +569,7 @@ public class QRecord implements Serializable
** Add one error to this record
**
*******************************************************************************/
public void addError(String error)
public void addError(QErrorMessage error)
{
this.errors.add(error);
}
@ -575,7 +580,7 @@ public class QRecord implements Serializable
** Fluently Add one error to this record
**
*******************************************************************************/
public QRecord withError(String error)
public QRecord withError(QErrorMessage error)
{
addError(error);
return (this);
@ -658,7 +663,7 @@ public class QRecord implements Serializable
/*******************************************************************************
** Getter for warnings
*******************************************************************************/
public List<String> getWarnings()
public List<QWarningMessage> getWarnings()
{
return (this.warnings);
}
@ -668,7 +673,7 @@ public class QRecord implements Serializable
/*******************************************************************************
** Setter for warnings
*******************************************************************************/
public void setWarnings(List<String> warnings)
public void setWarnings(List<QWarningMessage> warnings)
{
this.warnings = warnings;
}
@ -678,7 +683,7 @@ public class QRecord implements Serializable
/*******************************************************************************
** Fluent setter for warnings
*******************************************************************************/
public QRecord withWarnings(List<String> warnings)
public QRecord withWarnings(List<QWarningMessage> warnings)
{
this.warnings = warnings;
return (this);
@ -690,7 +695,7 @@ public class QRecord implements Serializable
** Add one warning to this record
**
*******************************************************************************/
public void addWarning(String warning)
public void addWarning(QWarningMessage warning)
{
this.warnings.add(warning);
}

View File

@ -0,0 +1,41 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.statusmessages;
/*******************************************************************************
** Record error message for when some bad input caused the issue (e.g., a missing
** value, or an illegal value). e.g., http 400 style.
*******************************************************************************/
public class BadInputStatusMessage extends QErrorMessage
{
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public BadInputStatusMessage(String message)
{
super(message);
}
}

View File

@ -0,0 +1,41 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.statusmessages;
/*******************************************************************************
** Record error message for when something couldn't be found (e.g., a request
** to update a record, but it isn't in the backend).
*******************************************************************************/
public class NotFoundStatusMessage extends QErrorMessage
{
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public NotFoundStatusMessage(String message)
{
super(message);
}
}

View File

@ -0,0 +1,41 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.statusmessages;
/*******************************************************************************
** Record error message for when the user didn't have permission to do what they
** were trying.
*******************************************************************************/
public class PermissionDeniedMessage extends QErrorMessage
{
/*******************************************************************************
** Constructor
**
******************************************************************************/
public PermissionDeniedMessage(String message)
{
super(message);
}
}

View File

@ -0,0 +1,46 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.statusmessages;
/*******************************************************************************
** Abstract base class for all errors to be attached to QRecords.
**
** Framework is aware of a few specific sultypes here, that drive things like
** returning http 400 vs 500.
**
** Applications can define further subtypes (recommended to subclass this class's
** children, rather than this class), to help give (type-based) definition to errors.
*******************************************************************************/
public abstract class QErrorMessage extends QStatusMessage
{
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public QErrorMessage(String message)
{
super(message);
}
}

View File

@ -0,0 +1,74 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.statusmessages;
import java.io.Serializable;
/*******************************************************************************
** Abstract Base class for status messages (errors or warnings) that can be
** attached to QRecords.
**
** They look like exceptions, but they aren't throwable, and they are meant
** to just be put in a record's error or warning list. Those lists were originally
** just Strings, but we wanted to have some type information communicated with
** them, e.g., for marking an error as caused by bad-data (e.g., from a user, e.g.,
** for an HTTP 400) vs. a server-side error, etc.
*******************************************************************************/
public abstract class QStatusMessage implements Serializable
{
private String message;
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public QStatusMessage(String message)
{
this.message = message;
}
/*******************************************************************************
** Getter for message
**
*******************************************************************************/
public String getMessage()
{
return message;
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public String toString()
{
return (message);
}
}

View File

@ -0,0 +1,43 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.statusmessages;
/*******************************************************************************
** Class to wrap warning messages to attach to QRecords.
**
** Applications can define subtypes as needed, to help give (type-based)
** definition to warnings.
*******************************************************************************/
public class QWarningMessage extends QStatusMessage
{
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public QWarningMessage(String message)
{
super(message);
}
}

View File

@ -0,0 +1,41 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.statusmessages;
/*******************************************************************************
** Record error message for when the system was at fault - not the user. e.g.,
** http 500 style.
*******************************************************************************/
public class SystemErrorStatusMessage extends QErrorMessage
{
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public SystemErrorStatusMessage(String message)
{
super(message);
}
}

View File

@ -35,6 +35,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.statusmessages.QWarningMessage;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -74,7 +75,7 @@ class AbstractPostDeleteCustomizerTest extends BaseTest
assertEquals(1, deleteOutput.getRecordsWithWarnings().size());
assertEquals(1, deleteOutput.getRecordsWithWarnings().get(0).getValue("id"));
assertEquals(2, deleteOutput.getDeletedRecordCount());
assertEquals("You shouldn't have deleted Homer...", deleteOutput.getRecordsWithWarnings().get(0).getWarnings().get(0));
assertEquals("You shouldn't have deleted Homer...", deleteOutput.getRecordsWithWarnings().get(0).getWarnings().get(0).getMessage());
GetInput getInput = new GetInput();
getInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
@ -107,7 +108,7 @@ class AbstractPostDeleteCustomizerTest extends BaseTest
{
if(record.getValue("firstName").equals("Homer"))
{
record.addWarning("You shouldn't have deleted Homer...");
record.addWarning(new QWarningMessage("You shouldn't have deleted Homer..."));
}
}

View File

@ -42,6 +42,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.statusmessages.QWarningMessage;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test;
@ -110,7 +111,7 @@ class AbstractPostUpdateCustomizerTest extends BaseTest
updateInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("firstName", "Warning")));
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
assertTrue(CollectionUtils.nullSafeIsEmpty(updateOutput.getRecords().get(0).getErrors()));
assertTrue(updateOutput.getRecords().get(0).getWarnings().stream().anyMatch(s -> s.contains("updated to a warning value")));
assertTrue(updateOutput.getRecords().get(0).getWarnings().stream().anyMatch(s -> s.getMessage().contains("updated to a warning value")));
GetInput getInput = new GetInput();
getInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
@ -128,7 +129,7 @@ class AbstractPostUpdateCustomizerTest extends BaseTest
updateInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("firstName", "throw")));
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
assertTrue(CollectionUtils.nullSafeIsEmpty(updateOutput.getRecords().get(0).getErrors()));
assertTrue(updateOutput.getRecords().get(0).getWarnings().stream().anyMatch(s -> s.contains("Forced Exception")));
assertTrue(updateOutput.getRecords().get(0).getWarnings().stream().anyMatch(s -> s.getMessage().contains("Forced Exception")));
GetInput getInput = new GetInput();
getInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
@ -178,7 +179,7 @@ class AbstractPostUpdateCustomizerTest extends BaseTest
if("warning".equalsIgnoreCase(record.getValueString("firstName")))
{
record.addWarning("Record was updated to a warning value");
record.addWarning(new QWarningMessage("Record was updated to a warning value"));
}
if("throw".equalsIgnoreCase(record.getValueString("firstName")))

View File

@ -35,6 +35,8 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.statusmessages.BadInputStatusMessage;
import com.kingsrook.qqq.backend.core.model.statusmessages.QWarningMessage;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -74,7 +76,7 @@ class AbstractPreDeleteCustomizerTest extends BaseTest
assertEquals(0, deleteOutput.getRecordsWithWarnings().size());
assertEquals(1, deleteOutput.getRecordsWithErrors().get(0).getValue("id"));
assertEquals(1, deleteOutput.getDeletedRecordCount());
assertEquals("You may not delete a Homer.", deleteOutput.getRecordsWithErrors().get(0).getErrors().get(0));
assertEquals("You may not delete a Homer.", deleteOutput.getRecordsWithErrors().get(0).getErrors().get(0).getMessage());
GetInput getInput = new GetInput();
getInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
@ -99,7 +101,7 @@ class AbstractPreDeleteCustomizerTest extends BaseTest
assertEquals(1, deleteOutput.getRecordsWithWarnings().size());
assertEquals(3, deleteOutput.getRecordsWithWarnings().get(0).getValue("id"));
assertEquals(1, deleteOutput.getDeletedRecordCount());
assertEquals("It was a bad idea to delete Bart", deleteOutput.getRecordsWithWarnings().get(0).getWarnings().get(0));
assertEquals("It was a bad idea to delete Bart", deleteOutput.getRecordsWithWarnings().get(0).getWarnings().get(0).getMessage());
GetInput getInput = new GetInput();
getInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
@ -127,11 +129,11 @@ class AbstractPreDeleteCustomizerTest extends BaseTest
{
if(record.getValue("firstName").equals("Homer"))
{
record.addError("You may not delete a Homer.");
record.addError(new BadInputStatusMessage("You may not delete a Homer."));
}
else if(record.getValue("firstName").equals("Bart"))
{
record.addWarning("It was a bad idea to delete Bart");
record.addWarning(new QWarningMessage("It was a bad idea to delete Bart"));
}
}

View File

@ -35,6 +35,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateOutput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.statusmessages.BadInputStatusMessage;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -70,7 +71,7 @@ class AbstractPreUpdateCustomizerTest extends BaseTest
updateInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
updateInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("firstName", "--")));
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
assertTrue(updateOutput.getRecords().get(0).getErrors().stream().anyMatch(s -> s.contains("must contain at least one letter")));
assertTrue(updateOutput.getRecords().get(0).getErrors().stream().anyMatch(s -> s.getMessage().contains("must contain at least one letter")));
GetInput getInput = new GetInput();
getInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
@ -146,7 +147,7 @@ class AbstractPreUpdateCustomizerTest extends BaseTest
////////////////////////////////////////////////////////////////
if(!record.getValueString("firstName").matches(".*\\w.*"))
{
record.addError("First name must contain at least one letter.");
record.addError(new BadInputStatusMessage("First name must contain at least one letter."));
}
//////////////////////////////////////////////////////////////

View File

@ -76,7 +76,7 @@ class DeleteActionTest extends BaseTest
assertEquals(1, result.getDeletedRecordCount());
assertEquals(1, result.getRecordsWithErrors().size());
assertEquals(2, result.getRecordsWithErrors().get(0).getValueInteger("id"));
assertEquals("No record was found to delete for Id = 2", result.getRecordsWithErrors().get(0).getErrors().get(0));
assertEquals("No record was found to delete for Id = 2", result.getRecordsWithErrors().get(0).getErrors().get(0).getMessage());
}
@ -331,28 +331,28 @@ class DeleteActionTest extends BaseTest
DeleteOutput deleteOutput = new DeleteAction().execute(deleteInput);
assertEquals(0, deleteOutput.getDeletedRecordCount());
assertEquals(1, deleteOutput.getRecordsWithErrors().size());
assertEquals("No record was found to delete for Id = 1", deleteOutput.getRecordsWithErrors().get(0).getErrors().get(0));
assertEquals("No record was found to delete for Id = 1", deleteOutput.getRecordsWithErrors().get(0).getErrors().get(0).getMessage());
deleteInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM);
deleteInput.setPrimaryKeys(List.of(1));
deleteOutput = new DeleteAction().execute(deleteInput);
assertEquals(0, deleteOutput.getDeletedRecordCount());
assertEquals(1, deleteOutput.getRecordsWithErrors().size());
assertEquals("No record was found to delete for Id = 1", deleteOutput.getRecordsWithErrors().get(0).getErrors().get(0));
assertEquals("No record was found to delete for Id = 1", deleteOutput.getRecordsWithErrors().get(0).getErrors().get(0).getMessage());
deleteInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
deleteInput.setPrimaryKeys(List.of(1));
deleteOutput = new DeleteAction().execute(deleteInput);
assertEquals(0, deleteOutput.getDeletedRecordCount());
assertEquals(1, deleteOutput.getRecordsWithErrors().size());
assertEquals("No record was found to delete for Id = 1", deleteOutput.getRecordsWithErrors().get(0).getErrors().get(0));
assertEquals("No record was found to delete for Id = 1", deleteOutput.getRecordsWithErrors().get(0).getErrors().get(0).getMessage());
deleteInput.setTableName(TestUtils.TABLE_NAME_ORDER_EXTRINSIC);
deleteInput.setPrimaryKeys(List.of(1));
deleteOutput = new DeleteAction().execute(deleteInput);
assertEquals(0, deleteOutput.getDeletedRecordCount());
assertEquals(1, deleteOutput.getRecordsWithErrors().size());
assertEquals("No record was found to delete for Id = 1", deleteOutput.getRecordsWithErrors().get(0).getErrors().get(0));
assertEquals("No record was found to delete for Id = 1", deleteOutput.getRecordsWithErrors().get(0).getErrors().get(0).getMessage());
}

View File

@ -113,7 +113,7 @@ class InsertActionTest extends BaseTest
assertEquals(1, TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_PERSON_MEMORY).size());
assertNull(insertOutput.getRecords().get(0).getValueInteger("id"));
assertEquals(1, insertOutput.getRecords().get(0).getErrors().size());
assertThat(insertOutput.getRecords().get(0).getErrors().get(0)).contains("Another record already exists with this First Name and Last Name");
assertThat(insertOutput.getRecords().get(0).getErrors().get(0).getMessage()).contains("Another record already exists with this First Name and Last Name");
//////////////////////////////////////////////////////////////////////////////////////////
// try to insert that person again, with 2 others - the 2 should work, but the one fail //
@ -156,7 +156,7 @@ class InsertActionTest extends BaseTest
assertEquals(1, insertOutput.getRecords().get(0).getValueInteger("id"));
assertNull(insertOutput.getRecords().get(1).getValueInteger("id"));
assertEquals(1, insertOutput.getRecords().get(1).getErrors().size());
assertThat(insertOutput.getRecords().get(1).getErrors().get(0)).contains("Another record already exists with this First Name and Last Name");
assertThat(insertOutput.getRecords().get(1).getErrors().get(0).getMessage()).contains("Another record already exists with this First Name and Last Name");
}
@ -183,7 +183,7 @@ class InsertActionTest extends BaseTest
assertEquals(1, insertOutput.getRecords().get(0).getValueInteger("id"));
assertNull(insertOutput.getRecords().get(1).getValueInteger("id"));
assertEquals(1, insertOutput.getRecords().get(1).getErrors().size());
assertThat(insertOutput.getRecords().get(1).getErrors().get(0)).contains("Another record already exists with this Name");
assertThat(insertOutput.getRecords().get(1).getErrors().get(0).getMessage()).contains("Another record already exists with this Name");
}
@ -303,7 +303,7 @@ class InsertActionTest extends BaseTest
insertInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
insertInput.setRecords(List.of(new QRecord().withValue("lineItemId", null).withValue("key", "kidsCanCallYou").withValue("value", "HoJu")));
InsertOutput insertOutput = new InsertAction().execute(insertInput);
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0));
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0).getMessage());
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -314,7 +314,7 @@ class InsertActionTest extends BaseTest
insertInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
insertInput.setRecords(List.of(new QRecord().withValue("lineItemId", 1701).withValue("key", "kidsCanCallYou").withValue("value", "HoJu")));
InsertOutput insertOutput = new InsertAction().execute(insertInput);
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0));
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0).getMessage());
}
{
@ -341,7 +341,7 @@ class InsertActionTest extends BaseTest
insertLineItemExtrinsicInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
insertLineItemExtrinsicInput.setRecords(List.of(new QRecord().withValue("lineItemId", 4200).withValue("key", "kidsCanCallYou").withValue("value", "HoJu")));
InsertOutput insertLineItemExtrinsicOutput = new InsertAction().execute(insertLineItemExtrinsicInput);
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertLineItemExtrinsicOutput.getRecords().get(0).getErrors().get(0));
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertLineItemExtrinsicOutput.getRecords().get(0).getErrors().get(0).getMessage());
}
{
@ -371,9 +371,9 @@ class InsertActionTest extends BaseTest
new QRecord().withValue("lineItemId", 4700).withValue("key", "theKidsCanCallYou").withValue("value", "HoJu")
));
InsertOutput insertOutput = new InsertAction().execute(insertInput);
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0));
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(1).getErrors().get(0));
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(2).getErrors().get(0));
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0).getMessage());
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(1).getErrors().get(0).getMessage());
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(2).getErrors().get(0).getMessage());
assertEquals(0, insertOutput.getRecords().get(3).getErrors().size());
assertNotNull(insertOutput.getRecords().get(3).getValueInteger("id"));
}
@ -395,14 +395,14 @@ class InsertActionTest extends BaseTest
new QRecord().withValue("lineItemId", 4700).withValue("key", "theKidsCanCallYou").withValue("value", "HoJu")
));
InsertOutput insertOutput = new InsertAction().execute(insertInput);
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0));
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(1).getErrors().get(0));
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(2).getErrors().get(0));
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0).getMessage());
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(1).getErrors().get(0).getMessage());
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(2).getErrors().get(0).getMessage());
assertEquals(0, insertOutput.getRecords().get(3).getErrors().size());
assertNotNull(insertOutput.getRecords().get(3).getValueInteger("id"));
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(4).getErrors().get(0));
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(5).getErrors().get(0));
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(6).getErrors().get(0));
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(4).getErrors().get(0).getMessage());
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(5).getErrors().get(0).getMessage());
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(6).getErrors().get(0).getMessage());
assertEquals(0, insertOutput.getRecords().get(7).getErrors().size());
assertNotNull(insertOutput.getRecords().get(7).getValueInteger("id"));
}
@ -426,7 +426,7 @@ class InsertActionTest extends BaseTest
insertInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM);
insertInput.setRecords(List.of(new QRecord().withValue("orderId", null).withValue("sku", "BASIC1").withValue("quantity", 1)));
InsertOutput insertOutput = new InsertAction().execute(insertInput);
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0));
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0).getMessage());
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -437,7 +437,7 @@ class InsertActionTest extends BaseTest
insertInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM);
insertInput.setRecords(List.of(new QRecord().withValue("orderId", 1701).withValue("sku", "BASIC1").withValue("quantity", 1)));
InsertOutput insertOutput = new InsertAction().execute(insertInput);
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0));
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0).getMessage());
}
{
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -457,7 +457,7 @@ class InsertActionTest extends BaseTest
insertLineItemInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM);
insertLineItemInput.setRecords(List.of(new QRecord().withValue("orderId", 42).withValue("sku", "BASIC1").withValue("quantity", 1)));
InsertOutput insertLineItemOutput = new InsertAction().execute(insertLineItemInput);
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertLineItemOutput.getRecords().get(0).getErrors().get(0));
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertLineItemOutput.getRecords().get(0).getErrors().get(0).getMessage());
}
{
@ -480,9 +480,9 @@ class InsertActionTest extends BaseTest
new QRecord().withValue("orderId", 47).withValue("sku", "BASIC1").withValue("quantity", 1)
));
InsertOutput insertOutput = new InsertAction().execute(insertInput);
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0));
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(1).getErrors().get(0));
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(2).getErrors().get(0));
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0).getMessage());
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(1).getErrors().get(0).getMessage());
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(2).getErrors().get(0).getMessage());
assertEquals(0, insertOutput.getRecords().get(3).getErrors().size());
assertNotNull(insertOutput.getRecords().get(3).getValueInteger("id"));
}
@ -504,14 +504,14 @@ class InsertActionTest extends BaseTest
new QRecord().withValue("orderId", 47).withValue("sku", "BASIC1").withValue("quantity", 1)
));
InsertOutput insertOutput = new InsertAction().execute(insertInput);
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0));
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(1).getErrors().get(0));
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(2).getErrors().get(0));
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0).getMessage());
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(1).getErrors().get(0).getMessage());
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(2).getErrors().get(0).getMessage());
assertEquals(0, insertOutput.getRecords().get(3).getErrors().size());
assertNotNull(insertOutput.getRecords().get(3).getValueInteger("id"));
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(4).getErrors().get(0));
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(5).getErrors().get(0));
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(6).getErrors().get(0));
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(4).getErrors().get(0).getMessage());
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(5).getErrors().get(0).getMessage());
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(6).getErrors().get(0).getMessage());
assertEquals(0, insertOutput.getRecords().get(7).getErrors().size());
assertNotNull(insertOutput.getRecords().get(7).getValueInteger("id"));
}
@ -531,7 +531,7 @@ class InsertActionTest extends BaseTest
insertInput.setTableName(TestUtils.TABLE_NAME_ORDER);
insertInput.setRecords(List.of(new QRecord().withValue("storeId", 2)));
InsertOutput insertOutput = new InsertAction().execute(insertInput);
assertEquals("You do not have permission to insert a record with a value of 2 in the field: Store Id", insertOutput.getRecords().get(0).getErrors().get(0));
assertEquals("You do not have permission to insert a record with a value of 2 in the field: Store Id", insertOutput.getRecords().get(0).getErrors().get(0).getMessage());
assertEquals(0, TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_ORDER).size());
}
@ -549,7 +549,7 @@ class InsertActionTest extends BaseTest
insertInput.setTableName(TestUtils.TABLE_NAME_ORDER);
insertInput.setRecords(List.of(new QRecord()));
InsertOutput insertOutput = new InsertAction().execute(insertInput);
assertEquals("You do not have permission to insert a record without a value in the field: Store Id", insertOutput.getRecords().get(0).getErrors().get(0));
assertEquals("You do not have permission to insert a record without a value in the field: Store Id", insertOutput.getRecords().get(0).getErrors().get(0).getMessage());
assertEquals(0, TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_ORDER).size());
}
@ -617,7 +617,7 @@ class InsertActionTest extends BaseTest
// 1st record had no value in orderNo - assert it errored //
////////////////////////////////////////////////////////////
assertEquals(1, insertOutput.getRecords().get(0).getErrors().size());
assertEquals("Missing value in required field: Order No", insertOutput.getRecords().get(0).getErrors().get(0));
assertEquals("Missing value in required field: Order No", insertOutput.getRecords().get(0).getErrors().get(0).getMessage());
///////////////////////////////////////////////
// 2nd record had a value - it should insert //
@ -629,7 +629,7 @@ class InsertActionTest extends BaseTest
// 3rd record had spaces-only in orderNo - make sure that fails //
//////////////////////////////////////////////////////////////////
assertEquals(1, insertOutput.getRecords().get(2).getErrors().size());
assertEquals("Missing value in required field: Order No", insertOutput.getRecords().get(2).getErrors().get(0));
assertEquals("Missing value in required field: Order No", insertOutput.getRecords().get(2).getErrors().get(0).getMessage());
}
}

View File

@ -376,7 +376,7 @@ class UpdateActionTest extends BaseTest
// 1st record tried to set a null orderNo - assert it errored //
////////////////////////////////////////////////////////////////
assertEquals(1, updateOutput.getRecords().get(0).getErrors().size());
assertEquals("Missing value in required field: Order No", updateOutput.getRecords().get(0).getErrors().get(0));
assertEquals("Missing value in required field: Order No", updateOutput.getRecords().get(0).getErrors().get(0).getMessage());
////////////////////////////////////////////////////////////////
// 2nd record didn't try to change orderNo, so should be fine //
@ -392,7 +392,7 @@ class UpdateActionTest extends BaseTest
// 4th record tried to set orderNo to all spaces - assert it errored //
///////////////////////////////////////////////////////////////////////
assertEquals(1, updateOutput.getRecords().get(3).getErrors().size());
assertEquals("Missing value in required field: Order No", updateOutput.getRecords().get(3).getErrors().get(0));
assertEquals("Missing value in required field: Order No", updateOutput.getRecords().get(3).getErrors().get(0).getMessage());
}
@ -439,7 +439,7 @@ class UpdateActionTest extends BaseTest
updateInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM);
updateInput.setRecords(List.of(new QRecord().withValue("id", 10).withValue("orderId", null).withValue("sku", "BASIC2")));
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
assertEquals("You do not have permission to update this record - the referenced Order was not found.", updateOutput.getRecords().get(0).getErrors().get(0));
assertEquals("You do not have permission to update this record - the referenced Order was not found.", updateOutput.getRecords().get(0).getErrors().get(0).getMessage());
}
////////////////////////////////////////////////////////////////////////////////////
@ -452,7 +452,7 @@ class UpdateActionTest extends BaseTest
updateInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM);
updateInput.setRecords(List.of(new QRecord().withValue("id", 20).withValue("sku", "BASIC3")));
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
assertEquals("No record was found to update for Id = 20", updateOutput.getRecords().get(0).getErrors().get(0));
assertEquals("No record was found to update for Id = 20", updateOutput.getRecords().get(0).getErrors().get(0).getMessage());
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -464,7 +464,7 @@ class UpdateActionTest extends BaseTest
updateInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM);
updateInput.setRecords(List.of(new QRecord().withValue("id", 10).withValue("orderId", 2).withValue("sku", "BASIC3")));
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
assertEquals("You do not have permission to update this record - the referenced Order was not found.", updateOutput.getRecords().get(0).getErrors().get(0));
assertEquals("You do not have permission to update this record - the referenced Order was not found.", updateOutput.getRecords().get(0).getErrors().get(0).getMessage());
}
///////////////////////////////////////////////////////////
@ -475,7 +475,7 @@ class UpdateActionTest extends BaseTest
updateInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
updateInput.setRecords(List.of(new QRecord().withValue("id", 100).withValue("lineItemId", null).withValue("key", "updatedKey")));
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
assertEquals("You do not have permission to update this record - the referenced Order was not found.", updateOutput.getRecords().get(0).getErrors().get(0));
assertEquals("You do not have permission to update this record - the referenced Order was not found.", updateOutput.getRecords().get(0).getErrors().get(0).getMessage());
}
//////////////////////////////////////////////////////////////////////////////////////////////
@ -488,7 +488,7 @@ class UpdateActionTest extends BaseTest
updateInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
updateInput.setRecords(List.of(new QRecord().withValue("id", 200).withValue("key", "updatedKey")));
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
assertEquals("No record was found to update for Id = 200", updateOutput.getRecords().get(0).getErrors().get(0));
assertEquals("No record was found to update for Id = 200", updateOutput.getRecords().get(0).getErrors().get(0).getMessage());
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -500,7 +500,7 @@ class UpdateActionTest extends BaseTest
updateInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
updateInput.setRecords(List.of(new QRecord().withValue("id", 100).withValue("lineItemId", 20).withValue("key", "updatedKey")));
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
assertEquals("You do not have permission to update this record - the referenced Order was not found.", updateOutput.getRecords().get(0).getErrors().get(0));
assertEquals("You do not have permission to update this record - the referenced Order was not found.", updateOutput.getRecords().get(0).getErrors().get(0).getMessage());
}
}
@ -518,7 +518,7 @@ class UpdateActionTest extends BaseTest
updateInput.setTableName(TestUtils.TABLE_NAME_ORDER);
updateInput.setRecords(List.of(new QRecord().withValue("id", 999).withValue("orderNo", "updated")));
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
assertEquals("No record was found to update for Id = 999", updateOutput.getRecords().get(0).getErrors().get(0));
assertEquals("No record was found to update for Id = 999", updateOutput.getRecords().get(0).getErrors().get(0).getMessage());
}
@ -548,7 +548,7 @@ class UpdateActionTest extends BaseTest
updateInput.setTableName(TestUtils.TABLE_NAME_ORDER);
updateInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("orderNo", "updated")));
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
assertEquals("No record was found to update for Id = 1", updateOutput.getRecords().get(0).getErrors().get(0));
assertEquals("No record was found to update for Id = 1", updateOutput.getRecords().get(0).getErrors().get(0).getMessage());
QContext.getQSession().withSecurityKeyValues(MapBuilder.of(TestUtils.SECURITY_KEY_TYPE_STORE_ALL_ACCESS, ListBuilder.of(true)));
assertThat(TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER)).noneMatch(r -> r.getValueString("orderNo").equals("updated"));
@ -562,7 +562,7 @@ class UpdateActionTest extends BaseTest
updateInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("orderNo", "updated").withValue("storeId", 2)));
updateOutput = new UpdateAction().execute(updateInput);
assertEquals("You do not have permission to update a record with a value of 2 in the field: Store Id", updateOutput.getRecords().get(0).getErrors().get(0));
assertEquals("You do not have permission to update a record with a value of 2 in the field: Store Id", updateOutput.getRecords().get(0).getErrors().get(0).getMessage());
QContext.getQSession().withSecurityKeyValues(MapBuilder.of(TestUtils.SECURITY_KEY_TYPE_STORE_ALL_ACCESS, ListBuilder.of(true)));
assertThat(TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER)).noneMatch(r -> r.getValueString("orderNo").equals("updated"));
@ -593,7 +593,7 @@ class UpdateActionTest extends BaseTest
updateInput.setTableName(TestUtils.TABLE_NAME_ORDER);
updateInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("storeId", null)));
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
assertEquals("You do not have permission to update a record without a value in the field: Store Id", updateOutput.getRecords().get(0).getErrors().get(0));
assertEquals("You do not have permission to update a record without a value in the field: Store Id", updateOutput.getRecords().get(0).getErrors().get(0).getMessage());
assertEquals(0, TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER).stream().filter(r -> r.getValue("storeId") == null).count());
}

View File

@ -55,6 +55,7 @@ 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;
import com.kingsrook.qqq.backend.core.model.session.QSession;
import com.kingsrook.qqq.backend.core.model.statusmessages.SystemErrorStatusMessage;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
import com.kingsrook.qqq.backend.core.utils.SleepUtils;
@ -202,7 +203,7 @@ public class BaseAPIActionUtil
}
catch(Exception e)
{
record.addError("Error: " + e.getMessage());
record.addError(new SystemErrorStatusMessage("Error: " + e.getMessage()));
insertOutput.addRecord(record);
}
@ -421,13 +422,13 @@ public class BaseAPIActionUtil
JSONObject errorObject = jsonObject.getJSONObject("error");
if(errorObject.has("message"))
{
record.addError("Error: " + errorObject.getString("message"));
record.addError(new SystemErrorStatusMessage("Error: " + errorObject.getString("message")));
}
}
if(CollectionUtils.nullSafeIsEmpty(record.getErrors()))
{
record.addError("Unspecified error executing insert.");
record.addError(new SystemErrorStatusMessage("Unspecified error executing insert."));
}
}

View File

@ -37,6 +37,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.JoinsContext;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.statusmessages.SystemErrorStatusMessage;
import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager;
@ -217,7 +218,7 @@ public class RDBMSDeleteAction extends AbstractRDBMSAction implements DeleteInte
catch(Exception e)
{
LOG.debug("Exception trying to delete [" + tableName + "][" + primaryKey + "]", e);
deleteOutput.addRecordWithError(new QRecord(table, primaryKey).withError("Record was not deleted: " + e.getMessage()));
deleteOutput.addRecordWithError(new QRecord(table, primaryKey).withError(new SystemErrorStatusMessage("Record was not deleted: " + e.getMessage())));
}
}

View File

@ -365,7 +365,7 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
updateInput.setRecords(records);
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
assertFalse(updateOutput.getRecords().get(0).getErrors().isEmpty());
assertEquals("Missing value in primary key field", updateOutput.getRecords().get(0).getErrors().get(0));
assertEquals("Missing value in primary key field", updateOutput.getRecords().get(0).getErrors().get(0).getMessage());
}
{
@ -377,7 +377,7 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
updateInput.setRecords(records);
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
assertFalse(updateOutput.getRecords().get(0).getErrors().isEmpty());
assertEquals("Missing value in primary key field", updateOutput.getRecords().get(0).getErrors().get(0));
assertEquals("Missing value in primary key field", updateOutput.getRecords().get(0).getErrors().get(0).getMessage());
}
{
@ -393,7 +393,7 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
assertFalse(updateOutput.getRecords().get(0).getErrors().isEmpty());
assertEquals("Missing value in primary key field", updateOutput.getRecords().get(0).getErrors().get(0));
assertEquals("Missing value in primary key field", updateOutput.getRecords().get(0).getErrors().get(0).getMessage());
assertTrue(updateOutput.getRecords().get(1).getErrors().isEmpty());

View File

@ -68,6 +68,12 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateOutput;
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;
import com.kingsrook.qqq.backend.core.model.statusmessages.BadInputStatusMessage;
import com.kingsrook.qqq.backend.core.model.statusmessages.NotFoundStatusMessage;
import com.kingsrook.qqq.backend.core.model.statusmessages.PermissionDeniedMessage;
import com.kingsrook.qqq.backend.core.model.statusmessages.QErrorMessage;
import com.kingsrook.qqq.backend.core.model.statusmessages.QStatusMessage;
import com.kingsrook.qqq.backend.core.model.statusmessages.QWarningMessage;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.Pair;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
@ -364,12 +370,12 @@ public class ApiImplementation
InsertAction insertAction = new InsertAction();
InsertOutput insertOutput = insertAction.execute(insertInput);
List<String> errors = insertOutput.getRecords().get(0).getErrors();
List<QErrorMessage> errors = insertOutput.getRecords().get(0).getErrors();
if(CollectionUtils.nullSafeHasContents(errors))
{
boolean isBadRequest = areAnyErrorsBadRequest(errors);
String message = "Error inserting " + table.getLabel() + ": " + StringUtils.joinWithCommasAndAnd(errors);
String message = "Error inserting " + table.getLabel() + ": " + joinErrorsWithCommasAndAnd(errors);
if(isBadRequest)
{
throw (new QBadRequestException(message));
@ -383,10 +389,10 @@ public class ApiImplementation
LinkedHashMap<String, Serializable> outputRecord = new LinkedHashMap<>();
outputRecord.put(table.getPrimaryKeyField(), insertOutput.getRecords().get(0).getValue(table.getPrimaryKeyField()));
List<String> warnings = insertOutput.getRecords().get(0).getWarnings();
List<QWarningMessage> warnings = insertOutput.getRecords().get(0).getWarnings();
if(CollectionUtils.nullSafeHasContents(warnings))
{
outputRecord.put("warning", "Warning inserting " + table.getLabel() + ", some data may have been inserted: " + StringUtils.joinWithCommasAndAnd(warnings));
outputRecord.put("warning", "Warning inserting " + table.getLabel() + ", some data may have been inserted: " + joinErrorsWithCommasAndAnd(warnings));
}
return (outputRecord);
@ -463,19 +469,19 @@ public class ApiImplementation
LinkedHashMap<String, Serializable> outputRecord = new LinkedHashMap<>();
response.add(outputRecord);
List<String> errors = record.getErrors();
List<String> warnings = record.getWarnings();
List<QErrorMessage> errors = record.getErrors();
List<QWarningMessage> warnings = record.getWarnings();
if(CollectionUtils.nullSafeHasContents(errors))
{
outputRecord.put("statusCode", HttpStatus.Code.BAD_REQUEST.getCode());
outputRecord.put("statusText", HttpStatus.Code.BAD_REQUEST.getMessage());
outputRecord.put("error", "Error inserting " + table.getLabel() + ": " + StringUtils.joinWithCommasAndAnd(errors));
outputRecord.put("error", "Error inserting " + table.getLabel() + ": " + joinErrorsWithCommasAndAnd(errors));
}
else if(CollectionUtils.nullSafeHasContents(warnings))
{
outputRecord.put("statusCode", HttpStatus.Code.CREATED.getCode());
outputRecord.put("statusText", HttpStatus.Code.CREATED.getMessage());
outputRecord.put("warning", "Warning inserting " + table.getLabel() + ", some data may have been inserted: " + StringUtils.joinWithCommasAndAnd(warnings));
outputRecord.put("warning", "Warning inserting " + table.getLabel() + ", some data may have been inserted: " + joinErrorsWithCommasAndAnd(warnings));
outputRecord.put(table.getPrimaryKeyField(), record.getValue(table.getPrimaryKeyField()));
}
else
@ -570,7 +576,7 @@ public class ApiImplementation
UpdateAction updateAction = new UpdateAction();
UpdateOutput updateOutput = updateAction.execute(updateInput);
List<String> errors = updateOutput.getRecords().get(0).getErrors();
List<QErrorMessage> errors = updateOutput.getRecords().get(0).getErrors();
if(CollectionUtils.nullSafeHasContents(errors))
{
if(areAnyErrorsNotFound(errors))
@ -581,7 +587,7 @@ public class ApiImplementation
{
boolean isBadRequest = areAnyErrorsBadRequest(errors);
String message = "Error updating " + table.getLabel() + ": " + StringUtils.joinWithCommasAndAnd(errors);
String message = "Error updating " + table.getLabel() + ": " + joinErrorsWithCommasAndAnd(errors);
if(isBadRequest)
{
throw (new QBadRequestException(message));
@ -679,10 +685,10 @@ public class ApiImplementation
//////////
}
List<String> errors = record.getErrors();
List<QErrorMessage> errors = record.getErrors();
if(CollectionUtils.nullSafeHasContents(errors))
{
outputRecord.put("error", "Error updating " + table.getLabel() + ": " + StringUtils.joinWithCommasAndAnd(errors));
outputRecord.put("error", "Error updating " + table.getLabel() + ": " + joinErrorsWithCommasAndAnd(errors));
if(areAnyErrorsNotFound(errors))
{
outputRecord.put("statusCode", HttpStatus.Code.NOT_FOUND.getCode());
@ -729,13 +735,14 @@ public class ApiImplementation
DeleteOutput deleteOutput = deleteAction.execute(deleteInput);
if(CollectionUtils.nullSafeHasContents(deleteOutput.getRecordsWithErrors()))
{
if(areAnyErrorsNotFound(deleteOutput.getRecordsWithErrors().get(0).getErrors()))
List<QErrorMessage> errors = deleteOutput.getRecordsWithErrors().get(0).getErrors();
if(areAnyErrorsNotFound(errors))
{
throw (new QNotFoundException("Could not find " + table.getLabel() + " with " + table.getFields().get(table.getPrimaryKeyField()).getLabel() + " of " + primaryKey));
}
else
{
throw (new QException("Error deleting " + table.getLabel() + ": " + StringUtils.joinWithCommasAndAnd(deleteOutput.getRecordsWithErrors().get(0).getErrors())));
throw (new QException("Error deleting " + table.getLabel() + ": " + joinErrorsWithCommasAndAnd(errors)));
}
}
}
@ -811,8 +818,8 @@ public class ApiImplementation
///////////////////////////////////////
List<Map<String, Serializable>> response = new ArrayList<>();
List<QRecord> recordsWithErrors = deleteOutput.getRecordsWithErrors();
Map<String, List<String>> primaryKeyToErrorsMap = new HashMap<>();
List<QRecord> recordsWithErrors = deleteOutput.getRecordsWithErrors();
Map<String, List<QErrorMessage>> primaryKeyToErrorsMap = new HashMap<>();
for(QRecord recordWithError : CollectionUtils.nonNullList(recordsWithErrors))
{
String primaryKey = recordWithError.getValueString(table.getPrimaryKeyField());
@ -825,11 +832,11 @@ public class ApiImplementation
response.add(outputRecord);
outputRecord.put(table.getPrimaryKeyField(), primaryKey);
String primaryKeyString = ValueUtils.getValueAsString(primaryKey);
List<String> errors = primaryKeyToErrorsMap.get(primaryKeyString);
String primaryKeyString = ValueUtils.getValueAsString(primaryKey);
List<QErrorMessage> errors = primaryKeyToErrorsMap.get(primaryKeyString);
if(CollectionUtils.nullSafeHasContents(errors))
{
outputRecord.put("error", "Error deleting " + table.getLabel() + ": " + StringUtils.joinWithCommasAndAnd(errors));
outputRecord.put("error", "Error deleting " + table.getLabel() + ": " + joinErrorsWithCommasAndAnd(errors));
if(areAnyErrorsNotFound(errors))
{
outputRecord.put("statusCode", HttpStatus.Code.NOT_FOUND.getCode());
@ -853,6 +860,16 @@ public class ApiImplementation
/*******************************************************************************
**
*******************************************************************************/
private static String joinErrorsWithCommasAndAnd(List<? extends QStatusMessage> errors)
{
return StringUtils.joinWithCommasAndAnd(errors.stream().map(QStatusMessage::getMessage).toList());
}
/*******************************************************************************
**
*******************************************************************************/
@ -1109,13 +1126,9 @@ public class ApiImplementation
/*******************************************************************************
**
*******************************************************************************/
private static boolean areAnyErrorsBadRequest(List<String> errors)
private static boolean areAnyErrorsBadRequest(List<QErrorMessage> errors)
{
boolean isBadRequest = errors.stream().anyMatch(e ->
e.contains("Missing value in required field")
|| e.contains("You do not have permission")
);
return isBadRequest;
return errors.stream().anyMatch(e -> (e instanceof BadInputStatusMessage) || (e instanceof PermissionDeniedMessage));
}
@ -1123,9 +1136,9 @@ public class ApiImplementation
/*******************************************************************************
**
*******************************************************************************/
private static boolean areAnyErrorsNotFound(List<String> errors)
private static boolean areAnyErrorsNotFound(List<QErrorMessage> errors)
{
return errors.stream().anyMatch(e -> e.startsWith(UpdateAction.NOT_FOUND_ERROR_PREFIX) || e.startsWith(DeleteAction.NOT_FOUND_ERROR_PREFIX));
return errors.stream().anyMatch(e -> (e instanceof NotFoundStatusMessage));
}
}

View File

@ -52,6 +52,8 @@ import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.Association;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.UniqueKey;
import com.kingsrook.qqq.backend.core.model.statusmessages.BadInputStatusMessage;
import com.kingsrook.qqq.backend.core.model.statusmessages.QWarningMessage;
import com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryBackendModule;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
@ -156,7 +158,7 @@ public class TestUtils
{
if(!record.getValueString("firstName").matches(".*[a-z].*"))
{
record.addWarning("First name does not contain any letters...");
record.addWarning(new QWarningMessage("First name does not contain any letters..."));
}
}
@ -238,6 +240,7 @@ public class TestUtils
{
return new QTableMetaData()
.withName(TABLE_NAME_ORDER)
.withCustomizer(TableCustomizers.PRE_INSERT_RECORD.getRole(), new QCodeReference(OrderPreInsertCustomizer.class))
.withBackendName(MEMORY_BACKEND_NAME)
.withMiddlewareMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData().withInitialVersion(V2022_Q4)))
.withPrimaryKeyField("id")
@ -398,4 +401,37 @@ public class TestUtils
insertPersonRecord(4, "Lisa", "Simpson");
insertPersonRecord(5, "Maggie", "Simpson");
}
/*******************************************************************************
**
*******************************************************************************/
public static class OrderPreInsertCustomizer extends AbstractPreInsertCustomizer
{
/*******************************************************************************
**
*******************************************************************************/
@Override
public List<QRecord> apply(List<QRecord> records) throws QException
{
for(QRecord record : records)
{
for(QRecord orderLine : CollectionUtils.nonNullList(record.getAssociatedRecords().get("orderLines")))
{
if(orderLine.getValueInteger("quantity") != null && orderLine.getValueInteger("quantity") <= 0)
{
record.addError(new BadInputStatusMessage("Quantity may not be less than 0. See SKU " + orderLine.getValueString("sku")));
}
}
if("throw".equals(record.getValueString("orderNo")))
{
throw (new QException("Throwing error, as requested..."));
}
}
return (records);
}
}
}

View File

@ -684,6 +684,35 @@ class QJavalinApiHandlerTest extends BaseTest
/*******************************************************************************
**
*******************************************************************************/
@Test
void testInsertErrorsFromCustomizer() throws QException
{
HttpResponse<String> response = Unirest.post(BASE_URL + "/api/" + VERSION + "/order/")
.body("""
{"orderNo": "ORD123", "storeId": 47, "orderLines":
[
{"lineNumber": 1, "sku": "BASIC1", "quantity": 0},
]
}
""")
.asString();
System.out.println(response.getBody());
assertErrorResponse(HttpStatus.BAD_REQUEST_400, "Quantity may not be less than 0", response);
response = Unirest.post(BASE_URL + "/api/" + VERSION + "/order/")
.body("""
{"orderNo": "throw", "storeId": 47}
""")
.asString();
System.out.println(response.getBody());
assertErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR_500, "Throwing error, as requested", response);
}
/*******************************************************************************
**
*******************************************************************************/