Add instance-level pre- and post- insert and update table customizers

This commit is contained in:
2025-06-12 16:03:35 -05:00
parent 60c5c11549
commit 7089ec92a6
7 changed files with 449 additions and 12 deletions

View File

@ -54,6 +54,7 @@ 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.insert.InsertOutput;
import com.kingsrook.qqq.backend.core.model.data.QRecord; import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData; import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
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.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType; import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn; import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
@ -174,9 +175,21 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
.withRecordList(insertOutput.getRecords())); .withRecordList(insertOutput.getRecords()));
} }
////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
// finally, run the post-insert customizer, if there is one // // finally, run the post-insert customizers, if there are any //
////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
runPostInsertCustomizers(insertInput, table, insertOutput);
return insertOutput;
}
/***************************************************************************
**
***************************************************************************/
private static void runPostInsertCustomizers(InsertInput insertInput, QTableMetaData table, InsertOutput insertOutput)
{
Optional<TableCustomizerInterface> postInsertCustomizer = QCodeLoader.getTableCustomizer(table, TableCustomizers.POST_INSERT_RECORD.getRole()); Optional<TableCustomizerInterface> postInsertCustomizer = QCodeLoader.getTableCustomizer(table, TableCustomizers.POST_INSERT_RECORD.getRole());
if(postInsertCustomizer.isPresent()) if(postInsertCustomizer.isPresent())
{ {
@ -193,7 +206,25 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
} }
} }
return insertOutput; ///////////////////////////////////////////////
// run all of the instance-level customizers //
///////////////////////////////////////////////
List<QCodeReference> tableCustomizerCodes = QContext.getQInstance().getTableCustomizers(TableCustomizers.POST_INSERT_RECORD);
for(QCodeReference tableCustomizerCode : tableCustomizerCodes)
{
try
{
TableCustomizerInterface tableCustomizer = QCodeLoader.getAdHoc(TableCustomizerInterface.class, tableCustomizerCode);
insertOutput.setRecords(tableCustomizer.postInsert(insertInput, insertOutput.getRecords()));
}
catch(Exception e)
{
for(QRecord record : insertOutput.getRecords())
{
record.addWarning(new QWarningMessage("An error occurred after the insert: " + e.getMessage()));
}
}
}
} }
@ -308,6 +339,19 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
insertInput.setRecords(preInsertCustomizer.get().preInsert(insertInput, insertInput.getRecords(), isPreview)); insertInput.setRecords(preInsertCustomizer.get().preInsert(insertInput, insertInput.getRecords(), isPreview));
} }
} }
///////////////////////////////////////////////
// run all of the instance-level customizers //
///////////////////////////////////////////////
List<QCodeReference> tableCustomizerCodes = QContext.getQInstance().getTableCustomizers(TableCustomizers.PRE_INSERT_RECORD);
for(QCodeReference tableCustomizerCode : tableCustomizerCodes)
{
TableCustomizerInterface tableCustomizer = QCodeLoader.getAdHoc(TableCustomizerInterface.class, tableCustomizerCode);
if(whenToRun.equals(tableCustomizer.whenToRunPreInsert(insertInput, isPreview)))
{
insertInput.setRecords(tableCustomizer.preInsert(insertInput, insertInput.getRecords(), isPreview));
}
}
} }

View File

@ -57,6 +57,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput; 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.actions.tables.update.UpdateOutput;
import com.kingsrook.qqq.backend.core.model.data.QRecord; 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.fields.DynamicDefaultValueBehavior; import com.kingsrook.qqq.backend.core.model.metadata.fields.DynamicDefaultValueBehavior;
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldBehavior; import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldBehavior;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData; import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
@ -199,6 +200,18 @@ public class UpdateAction
////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////
// finally, run the post-update customizer, if there is one // // finally, run the post-update customizer, if there is one //
////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////
runPostUpdateCustomizers(updateInput, table, updateOutput, oldRecordList);
return updateOutput;
}
/***************************************************************************
**
***************************************************************************/
private static void runPostUpdateCustomizers(UpdateInput updateInput, QTableMetaData table, UpdateOutput updateOutput, Optional<List<QRecord>> oldRecordList)
{
Optional<TableCustomizerInterface> postUpdateCustomizer = QCodeLoader.getTableCustomizer(table, TableCustomizers.POST_UPDATE_RECORD.getRole()); Optional<TableCustomizerInterface> postUpdateCustomizer = QCodeLoader.getTableCustomizer(table, TableCustomizers.POST_UPDATE_RECORD.getRole());
if(postUpdateCustomizer.isPresent()) if(postUpdateCustomizer.isPresent())
{ {
@ -215,7 +228,49 @@ public class UpdateAction
} }
} }
return updateOutput; ///////////////////////////////////////////////
// run all of the instance-level customizers //
///////////////////////////////////////////////
List<QCodeReference> tableCustomizerCodes = QContext.getQInstance().getTableCustomizers(TableCustomizers.POST_UPDATE_RECORD);
for(QCodeReference tableCustomizerCode : tableCustomizerCodes)
{
try
{
TableCustomizerInterface tableCustomizer = QCodeLoader.getAdHoc(TableCustomizerInterface.class, tableCustomizerCode);
updateOutput.setRecords(tableCustomizer.postUpdate(updateInput, updateOutput.getRecords(), oldRecordList));
}
catch(Exception e)
{
for(QRecord record : updateOutput.getRecords())
{
record.addWarning(new QWarningMessage("An error occurred after the update: " + e.getMessage()));
}
}
}
}
/***************************************************************************
**
***************************************************************************/
private static void runPreUpdateCustomizers(UpdateInput updateInput, QTableMetaData table, Optional<List<QRecord>> oldRecordList, boolean isPreview) throws QException
{
Optional<TableCustomizerInterface> preUpdateCustomizer = QCodeLoader.getTableCustomizer(table, TableCustomizers.PRE_UPDATE_RECORD.getRole());
if(preUpdateCustomizer.isPresent())
{
updateInput.setRecords(preUpdateCustomizer.get().preUpdate(updateInput, updateInput.getRecords(), isPreview, oldRecordList));
}
///////////////////////////////////////////////
// run all of the instance-level customizers //
///////////////////////////////////////////////
List<QCodeReference> tableCustomizerCodes = QContext.getQInstance().getTableCustomizers(TableCustomizers.PRE_UPDATE_RECORD);
for(QCodeReference tableCustomizerCode : tableCustomizerCodes)
{
TableCustomizerInterface tableCustomizer = QCodeLoader.getAdHoc(TableCustomizerInterface.class, tableCustomizerCode);
updateInput.setRecords(tableCustomizer.preUpdate(updateInput, updateInput.getRecords(), isPreview, oldRecordList));
}
} }
@ -278,11 +333,7 @@ public class UpdateAction
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// after all validations, run the pre-update customizer, if there is one // // after all validations, run the pre-update customizer, if there is one //
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
Optional<TableCustomizerInterface> preUpdateCustomizer = QCodeLoader.getTableCustomizer(table, TableCustomizers.PRE_UPDATE_RECORD.getRole()); runPreUpdateCustomizers(updateInput, table, oldRecordList, isPreview);
if(preUpdateCustomizer.isPresent())
{
updateInput.setRecords(preUpdateCustomizer.get().preUpdate(updateInput, updateInput.getRecords(), isPreview, oldRecordList));
}
} }

View File

@ -42,6 +42,7 @@ import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationHandler; import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationHandler;
import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizerInterface;
import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizers; import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizers;
import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.AbstractWidgetRenderer; import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.AbstractWidgetRenderer;
import com.kingsrook.qqq.backend.core.actions.metadata.JoinGraph; import com.kingsrook.qqq.backend.core.actions.metadata.JoinGraph;
@ -252,6 +253,17 @@ public class QInstanceValidator
{ {
validateSimpleCodeReference("Instance metaDataActionCustomizer ", qInstance.getMetaDataActionCustomizer(), MetaDataActionCustomizerInterface.class); validateSimpleCodeReference("Instance metaDataActionCustomizer ", qInstance.getMetaDataActionCustomizer(), MetaDataActionCustomizerInterface.class);
} }
if(qInstance.getTableCustomizers() != null)
{
for(Map.Entry<String, List<QCodeReference>> entry : qInstance.getTableCustomizers().entrySet())
{
for(QCodeReference codeReference : CollectionUtils.nonNullList(entry.getValue()))
{
validateSimpleCodeReference("Instance tableCustomizer of type " + entry.getKey() + ": ", codeReference, TableCustomizerInterface.class);
}
}
}
} }
@ -301,6 +313,17 @@ public class QInstanceValidator
validatorPlugins.clear(); validatorPlugins.clear();
} }
/*******************************************************************************
** Getter for validatorPlugins
**
*******************************************************************************/
public static ListingHash<Class<?>, QInstanceValidatorPluginInterface<?>> getValidatorPlugins()
{
return validatorPlugins;
}
/******************************************************************************* /*******************************************************************************
@ -2238,8 +2261,7 @@ public class QInstanceValidator
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
@SafeVarargs public void validateSimpleCodeReference(String prefix, QCodeReference codeReference, Class<?>... anyOfExpectedClasses)
private void validateSimpleCodeReference(String prefix, QCodeReference codeReference, Class<?>... anyOfExpectedClasses)
{ {
if(!preAssertionsForCodeReference(codeReference, prefix)) if(!preAssertionsForCodeReference(codeReference, prefix))
{ {

View File

@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.core.model.metadata;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
@ -30,6 +31,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizers;
import com.kingsrook.qqq.backend.core.actions.metadata.JoinGraph; import com.kingsrook.qqq.backend.core.actions.metadata.JoinGraph;
import com.kingsrook.qqq.backend.core.actions.metadata.MetaDataAction; import com.kingsrook.qqq.backend.core.actions.metadata.MetaDataAction;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
@ -65,6 +67,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.security.QSecurityKeyType;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.scheduler.schedulable.SchedulableType; import com.kingsrook.qqq.backend.core.scheduler.schedulable.SchedulableType;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.ListingHash;
import com.kingsrook.qqq.backend.core.utils.StringUtils; import com.kingsrook.qqq.backend.core.utils.StringUtils;
import io.github.cdimascio.dotenv.Dotenv; import io.github.cdimascio.dotenv.Dotenv;
import io.github.cdimascio.dotenv.DotenvEntry; import io.github.cdimascio.dotenv.DotenvEntry;
@ -116,6 +119,8 @@ public class QInstance
private QPermissionRules defaultPermissionRules = QPermissionRules.defaultInstance(); private QPermissionRules defaultPermissionRules = QPermissionRules.defaultInstance();
private QAuditRules defaultAuditRules = QAuditRules.defaultInstanceLevelNone(); private QAuditRules defaultAuditRules = QAuditRules.defaultInstanceLevelNone();
private ListingHash<String, QCodeReference> tableCustomizers;
@Deprecated(since = "migrated to metaDataCustomizer") @Deprecated(since = "migrated to metaDataCustomizer")
private QCodeReference metaDataFilter = null; private QCodeReference metaDataFilter = null;
@ -1623,4 +1628,76 @@ public class QInstance
return (this); return (this);
} }
/*******************************************************************************
** Getter for tableCustomizers
*******************************************************************************/
public ListingHash<String, QCodeReference> getTableCustomizers()
{
return (this.tableCustomizers);
}
/*******************************************************************************
** Setter for tableCustomizers
*******************************************************************************/
public void setTableCustomizers(ListingHash<String, QCodeReference> tableCustomizers)
{
this.tableCustomizers = tableCustomizers;
}
/*******************************************************************************
** Fluent setter for tableCustomizers
*******************************************************************************/
public QInstance withTableCustomizers(ListingHash<String, QCodeReference> tableCustomizers)
{
this.tableCustomizers = tableCustomizers;
return (this);
}
/*******************************************************************************
**
*******************************************************************************/
public QInstance withTableCustomizer(String role, QCodeReference customizer)
{
if(this.tableCustomizers == null)
{
this.tableCustomizers = new ListingHash<>();
}
this.tableCustomizers.add(role, customizer);
return (this);
}
/*******************************************************************************
**
*******************************************************************************/
public QInstance withTableCustomizer(TableCustomizers tableCustomizer, QCodeReference customizer)
{
return (withTableCustomizer(tableCustomizer.getRole(), customizer));
}
/*******************************************************************************
** Getter for tableCustomizers
*******************************************************************************/
public List<QCodeReference> getTableCustomizers(TableCustomizers tableCustomizer)
{
if(this.tableCustomizers == null)
{
return (Collections.emptyList());
}
return (this.tableCustomizers.getOrDefault(tableCustomizer.getRole(), Collections.emptyList()));
}
} }

View File

@ -0,0 +1,149 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2025. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.actions.tables;
import java.util.List;
import java.util.Optional;
import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizerInterface;
import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizers;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
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.statusmessages.SystemErrorStatusMessage;
import com.kingsrook.qqq.backend.core.utils.ListingHash;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
/*******************************************************************************
**
*******************************************************************************/
public class InsertActionInstanceLevelTableCustomizersTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
void testInstanceLevelCustomizers() throws QException
{
QContext.getQInstance().withTableCustomizer(TableCustomizers.PRE_INSERT_RECORD, new QCodeReference(BreaksEverythingCustomizer.class));
QRecord record = new InsertAction().execute(new InsertInput(TestUtils.TABLE_NAME_SHAPE).withRecord(new QRecord().withValue("name", "octogon"))).getRecords().get(0);
assertEquals("Everything is broken", record.getErrorsAsString());
assertNull(record.getValueInteger("id"));
QContext.getQInstance().setTableCustomizers(new ListingHash<>());
QContext.getQInstance().withTableCustomizer(TableCustomizers.PRE_INSERT_RECORD, new QCodeReference(SetsFirstName.class));
QContext.getQInstance().withTableCustomizer(TableCustomizers.PRE_INSERT_RECORD, new QCodeReference(SetsLastName.class));
QContext.getQInstance().withTableCustomizer(TableCustomizers.POST_INSERT_RECORD, new QCodeReference(DoesNothing.class));
DoesNothing.callCount = 0;
record = new InsertAction().execute(new InsertInput(TestUtils.TABLE_NAME_SHAPE).withRecord(new QRecord().withValue("name", "octogon"))).getRecords().get(0);
assertEquals("Jeff", record.getValueString("firstName"));
assertEquals("Smith", record.getValueString("lastName"));
assertNotNull(record.getValueInteger("id"));
assertEquals(1, DoesNothing.callCount);
}
/*******************************************************************************
**
*******************************************************************************/
public static class BreaksEverythingCustomizer implements TableCustomizerInterface
{
/*******************************************************************************
**
*******************************************************************************/
@Override
public List<QRecord> preInsertOrUpdate(AbstractActionInput input, List<QRecord> records, boolean isPreview, Optional<List<QRecord>> oldRecordList) throws QException
{
records.forEach(r -> r.addError(new SystemErrorStatusMessage("Everything is broken")));
return records;
}
}
/*******************************************************************************
**
*******************************************************************************/
public static class SetsFirstName implements TableCustomizerInterface
{
/*******************************************************************************
**
*******************************************************************************/
@Override
public List<QRecord> preInsertOrUpdate(AbstractActionInput input, List<QRecord> records, boolean isPreview, Optional<List<QRecord>> oldRecordList) throws QException
{
records.forEach(r -> r.setValue("firstName", "Jeff"));
return records;
}
}
/*******************************************************************************
**
*******************************************************************************/
public static class SetsLastName implements TableCustomizerInterface
{
/*******************************************************************************
**
*******************************************************************************/
@Override
public List<QRecord> preInsertOrUpdate(AbstractActionInput input, List<QRecord> records, boolean isPreview, Optional<List<QRecord>> oldRecordList) throws QException
{
records.forEach(r -> r.setValue("lastName", "Smith"));
return records;
}
}
/*******************************************************************************
**
*******************************************************************************/
public static class DoesNothing implements TableCustomizerInterface
{
static int callCount = 0;
/***************************************************************************
*
***************************************************************************/
@Override
public List<QRecord> postInsertOrUpdate(AbstractActionInput input, List<QRecord> records, Optional<List<QRecord>> oldRecordList) throws QException
{
callCount++;
return records;
}
}
}

View File

@ -0,0 +1,70 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2025. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.actions.tables;
import com.kingsrook.qqq.backend.core.BaseTest;
import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizers;
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.update.UpdateInput;
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.utils.ListingHash;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/*******************************************************************************
**
*******************************************************************************/
public class UpdateActionInstanceLevelTableCustomizersTest extends BaseTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
void testInstanceLevelCustomizers() throws QException
{
QRecord record = new InsertAction().execute(new InsertInput(TestUtils.TABLE_NAME_SHAPE).withRecord(new QRecord().withValue("name", "octogon"))).getRecords().get(0);
QContext.getQInstance().withTableCustomizer(TableCustomizers.PRE_UPDATE_RECORD, new QCodeReference(InsertActionInstanceLevelTableCustomizersTest.BreaksEverythingCustomizer.class));
record = new UpdateAction().execute(new UpdateInput(TestUtils.TABLE_NAME_SHAPE).withRecord(new QRecord().withValue("id", record.getValue("id")).withValue("name", "octogon"))).getRecords().get(0);
assertEquals("Everything is broken", record.getErrorsAsString());
QContext.getQInstance().setTableCustomizers(new ListingHash<>());
QContext.getQInstance().withTableCustomizer(TableCustomizers.PRE_UPDATE_RECORD, new QCodeReference(InsertActionInstanceLevelTableCustomizersTest.SetsFirstName.class));
QContext.getQInstance().withTableCustomizer(TableCustomizers.PRE_UPDATE_RECORD, new QCodeReference(InsertActionInstanceLevelTableCustomizersTest.SetsLastName.class));
QContext.getQInstance().withTableCustomizer(TableCustomizers.POST_UPDATE_RECORD, new QCodeReference(InsertActionInstanceLevelTableCustomizersTest.DoesNothing.class));
InsertActionInstanceLevelTableCustomizersTest.DoesNothing.callCount = 0;
record = new UpdateAction().execute(new UpdateInput(TestUtils.TABLE_NAME_SHAPE).withRecord(new QRecord().withValue("id", record.getValue("id")).withValue("name", "octogon"))).getRecords().get(0);
assertEquals("Jeff", record.getValueString("firstName"));
assertEquals("Smith", record.getValueString("lastName"));
assertNotNull(record.getValueInteger("id"));
assertEquals(1, InsertActionInstanceLevelTableCustomizersTest.DoesNothing.callCount);
}
}

View File

@ -176,6 +176,30 @@ public class QInstanceValidatorTest extends BaseTest
/*******************************************************************************
**
*******************************************************************************/
@Test
void testInstanceLevelTableCustomizers()
{
assertValidationFailureReasons((qInstance) -> qInstance.withTableCustomizer(TableCustomizers.PRE_INSERT_RECORD, new QCodeReference(QInstanceValidator.class)),
"Instance tableCustomizer of type preInsertRecord: CodeReference is not of the expected type");
assertValidationFailureReasons((qInstance) ->
{
qInstance.withTableCustomizer(TableCustomizers.POST_UPDATE_RECORD, new QCodeReference(QInstanceValidator.class));
qInstance.withTableCustomizer(TableCustomizers.POST_UPDATE_RECORD, new QCodeReference(QInstanceValidator.class));
qInstance.withTableCustomizer(TableCustomizers.PRE_DELETE_RECORD, new QCodeReference(QInstanceValidator.class));
},
"Instance tableCustomizer of type postUpdateRecord: CodeReference is not of the expected type",
"Instance tableCustomizer of type postUpdateRecord: CodeReference is not of the expected type",
"Instance tableCustomizer of type preDeleteRecord: CodeReference is not of the expected type");
assertValidationSuccess((qInstance) -> qInstance.withTableCustomizer(TableCustomizers.POST_UPDATE_RECORD, new QCodeReference(CustomizerValid.class)));
}
/******************************************************************************* /*******************************************************************************
** Test an instance with null backends - should throw. ** Test an instance with null backends - should throw.
** **