-
Notifications
You must be signed in to change notification settings - Fork 27
Getting Started with the Trigger Framework
The trigger framework of this library requires custom metadata to be create for any trigger that you create. To add a new trigger, create a new Apex class that implements the rflib_TriggerHandler
interface and its methods.
Next, create the actual trigger, i.e. an Account trigger as displayed below. It is recommended to create the trigger for all events so that future trigger additions only require the new class and the custom metadata record.
trigger AccountTrigger on Account (before insert, after insert, before update, after update, before delete, after delete) {
rflib_TriggerManager.dispatch(Account.SObjectType);
}
Once the trigger is created, create a handler implementing the rflib_TriggerHandler
interface as outlined below.
/*
* Copyright (c) 2019 Johannes Fischer <[email protected]>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of mosquitto nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @group Trigger
* @description Base interface for the trigger framework.
*/
public interface rflib_TriggerHandler {
/**
* Method to be invoked by the Trigger Framework when a trigger is executed in the main event context.
* @param args The TriggerManager.Args object containing all event details.
*/
void run(rflib_TriggerManager.Args args);
/**
* Method to be invoked by the Trigger Framework in the vent of a recursive invocation. The counter will the number
* of times the recursive loop was entered.
*
* @param args The TriggerManager.Args object containing all event details.
* @param numInvocation The number of times this event trigger has been invoked in the recursive context.
* Note that the number increases per event type, i.e. before and after update trigger
* will each have a counter of 1 during the first cycle.
*/
void onConsecutiveRun(rflib_TriggerManager.Args args, Integer numInvocation);
}
The following code is a sample test class that should basically work for any Apex Trigger. You simply need to change the record to be inserted to make sure it complies with your org's required fields and data validations. The test asserts that the framework is invoked and provides 100% test coverage for the trigger.
/*
* Copyright (c) 2019 Johannes Fischer <[email protected]>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of mosquitto nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
@IsTest
@SuppressWarnings('PMD.ClassNamingConventions')
private class SampleAccountTriggerTest {
private static final String AFTER_INSERT = TriggerOperation.AFTER_INSERT.name();
@TestSetup
static void makeData(){
rflib_TestUtil.prepareLoggerForUnitTests();
}
@IsTest
public static void testTriggerExecution() {
rflib_TriggerManager.QUERY_LOCATOR = new rflib_MockTriggerConfigQueryLocator(
createConfiguration(),
Account.SObjectType.getDescribe().getName(),
AFTER_INSERT
);
Test.startTest();
insert new Account(
Name = 'Test Company'
);
Test.stopTest();
// Based on the configuration, the handler should be invoked once for the AFTER_INSERT trigger
System.assertEquals(1, rflib_MockTriggerHandler.CAPTURED_RUN_TRIGGER_ARGS.size());
rflib_TriggerManager.Args args = rflib_MockTriggerHandler.CAPTURED_RUN_TRIGGER_ARGS.get(0);
Account capturedRecord = (Account) args.newRecords.get(0);
System.assertEquals('Test Company', capturedRecord.Name);
}
private static rflib_Trigger_Configuration__mdt createConfiguration() {
// Use the rflib_MockTriggerHandler to capture details about the trigger invocation
return new rflib_Trigger_Configuration__mdt(
Active__c = true,
Class_Name__c = 'rflib_MockTriggerHandler',
Object_Name__c = 'Account',
Event__c = AFTER_INSERT,
Order__c = 1
);
}
}
The Trigger Framework supports two Feature Switches that, when set up, will drive the behavior of the framework. The following table outlines how the Feature Switches need to be set up.
Label | Feature Switch Name | Switch Name | Scope Type | Description |
---|---|---|---|---|
Disable All Retryable Actions | Any Valid Name | rflib_Disable_All_Retryable_Actions | All Scopes Supported | If turned on, all configured Retryable Actions will be ignored for the current user. |
Disable All Triggers | Any Valid Name | rflib_Desable_All_Triggers | All Scopes Supported | If turned on, all configured Trigger Handlers will be ignored during the transaction for the current user. |