From a9e793dfb88782e6e0ba953ba8c8416fb9eded03 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Thu, 30 Mar 2023 11:55:36 -0500 Subject: [PATCH] Check for required fields --- .../core/actions/tables/UpdateAction.java | 50 ++++++++++++++- .../core/actions/tables/UpdateActionTest.java | 64 +++++++++++++++++++ 2 files changed, 111 insertions(+), 3 deletions(-) diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/UpdateAction.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/UpdateAction.java index 70c5f2b6..1d1eb07d 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/UpdateAction.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/UpdateAction.java @@ -25,6 +25,7 @@ package com.kingsrook.qqq.backend.core.actions.tables; import java.io.Serializable; import java.util.ArrayList; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import com.kingsrook.qqq.backend.core.actions.ActionHelper; import com.kingsrook.qqq.backend.core.actions.audits.DMLAuditAction; @@ -48,6 +49,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput; 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.audits.AuditLevel; +import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData; 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; @@ -83,10 +85,19 @@ public class UpdateAction QBackendModuleDispatcher qBackendModuleDispatcher = new QBackendModuleDispatcher(); QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(updateInput.getBackend()); + + validateRequiredFields(updateInput); + // todo pre-customization - just get to modify the request? - UpdateOutput updateResult = qModule.getUpdateInterface().execute(updateInput); + UpdateOutput updateOutput = qModule.getUpdateInterface().execute(updateInput); // todo post-customization - can do whatever w/ the result if you want + List errors = updateOutput.getRecords().stream().flatMap(r -> r.getErrors().stream()).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))); + } + manageAssociations(updateInput); if(updateInput.getOmitDmlAudit()) @@ -95,10 +106,43 @@ public class UpdateAction } else { - new DMLAuditAction().execute(new DMLAuditInput().withTableActionInput(updateInput).withRecordList(updateResult.getRecords()).withOldRecordList(oldRecordList)); + new DMLAuditAction().execute(new DMLAuditInput().withTableActionInput(updateInput).withRecordList(updateOutput.getRecords()).withOldRecordList(oldRecordList)); } - return updateResult; + return updateOutput; + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private void validateRequiredFields(UpdateInput updateInput) + { + QTableMetaData table = updateInput.getTable(); + Set requiredFields = table.getFields().values().stream() + .filter(f -> f.getIsRequired()) + .collect(Collectors.toSet()); + + if(!requiredFields.isEmpty()) + { + for(QRecord record : updateInput.getRecords()) + { + for(QFieldMetaData requiredField : requiredFields) + { + ///////////////////////////////////////////////////////////////////////////////////////////// + // only consider fields that were set in the record to be updated (e.g., "patch" semantic) // + ///////////////////////////////////////////////////////////////////////////////////////////// + if(record.getValues().containsKey(requiredField.getName())) + { + if(record.getValue(requiredField.getName()) == null || (requiredField.getType().isStringLike() && record.getValueString(requiredField.getName()).trim().equals(""))) + { + record.addError("Missing value in required field: " + requiredField.getLabel()); + } + } + } + } + } } diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/tables/UpdateActionTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/tables/UpdateActionTest.java index afb6ea5e..5148cb2c 100644 --- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/tables/UpdateActionTest.java +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/tables/UpdateActionTest.java @@ -22,12 +22,14 @@ package com.kingsrook.qqq.backend.core.actions.tables; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import com.kingsrook.qqq.backend.core.BaseTest; import com.kingsrook.qqq.backend.core.context.QContext; import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput; +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.actions.tables.update.UpdateOutput; import com.kingsrook.qqq.backend.core.model.data.QRecord; @@ -333,4 +335,66 @@ class UpdateActionTest extends BaseTest )); new InsertAction().execute(insertInput); } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testRequiredFields() throws QException + { + QInstance qInstance = QContext.getQInstance(); + qInstance.getTable(TestUtils.TABLE_NAME_ORDER).getField("orderNo").setIsRequired(true); + QContext.getQSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_TYPE_STORE_ALL_ACCESS, true); + + /////////////////////////////////////////////////// + // insert records that we'll later try to update // + /////////////////////////////////////////////////// + InsertInput insertInput = new InsertInput(); + insertInput.setTableName(TestUtils.TABLE_NAME_ORDER); + insertInput.setRecords(List.of( + new QRecord().withValue("id", 1).withValue("storeId", 999).withValue("orderNo", "ORD1"), + new QRecord().withValue("id", 2).withValue("storeId", 999).withValue("orderNo", "ORD2"), + new QRecord().withValue("id", 3).withValue("storeId", 999).withValue("orderNo", "ORD3"), + new QRecord().withValue("id", 4).withValue("storeId", 999).withValue("orderNo", "ORD4") + )); + InsertOutput insertOutput = new InsertAction().execute(insertInput); + + ////////////////////////////////////////////////// + // do our update that we'll test the results of // + ////////////////////////////////////////////////// + UpdateInput updateInput = new UpdateInput(); + updateInput.setTableName(TestUtils.TABLE_NAME_ORDER); + updateInput.setRecords(List.of( + new QRecord().withValue("id", 1).withValue("orderNo", null), + new QRecord().withValue("id", 2).withValue("total", new BigDecimal("3.50")), + new QRecord().withValue("id", 3).withValue("orderNo", "ORD3B"), + new QRecord().withValue("id", 4).withValue("orderNo", " ") + )); + UpdateOutput updateOutput = new UpdateAction().execute(updateInput); + + //////////////////////////////////////////////////////////////// + // 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)); + + //////////////////////////////////////////////////////////////// + // 2nd record didn't try to change orderNo, so should be fine // + //////////////////////////////////////////////////////////////// + assertEquals(0, updateOutput.getRecords().get(1).getErrors().size()); + + /////////////////////////////////////////////////////////////////// + // 3rd record should have actually set a new order no - no error // + /////////////////////////////////////////////////////////////////// + assertEquals(0, updateOutput.getRecords().get(2).getErrors().size()); + + /////////////////////////////////////////////////////////////////////// + // 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)); + + } }