In my last blog I introduced my Trigger Pattern Framework, now I will cover Trigger Control, which is one of the main improvements to other well documented frameworks.
DMLs are very processor intensive
and conserving this precious resource in a multitenant environment is ever more
so important.
So I built into my framework a
capability using a Custom Setting to activate / deactivate per trigger for any
User, Profile, or the entire Organization; or with a separate Custom Setting to
be able to activate / deactivate ALL triggers for any User, Profile, or the
entire Organization. This was great in situations where companies require
migrating data from 1 system to another and you want to safeguard that no
unwanted actions occur through the execution of code in the trigger.
However there are also many
situations such as in unit tests where we create test data, to avoid running
through all the code in triggers which is not necessary unless we are testing
specifically the triggers to disable the triggers using the Custom Settings
above will require running a DML and since we are trying to avoid DMLs because
they are expensive to run we need another mechanism to bypass the code in the
trigger, therefore we introduce a static variable to do this work. Here is a
section in the TriggerFactory class that controls the execution using these
methods and also the code from the calling classes that are used in this part
of the framework.
boolean notriggerSetting;
boolean
noTriggersPerObject;
try{
notriggerSetting = TriggerController.globalTriggerControlSetting();
noTriggersPerObject =
TriggerController.globalTriggerPerObjectControlSetting(objType);
}
catch
(Exception ex){system.debug('error in trigger controller ' + ex); }
Type soType = Type.forName(objType);
if (!notriggerSetting &&
!noTriggersPerObject &&
!TriggerController.getTriggerControlValue(soType,
TriggerController.TRIGGER_ALL)) {
if (Trigger.isBefore){
if
(Trigger.isUpdate && !TriggerController.getTriggerControlValue(soType,
TriggerController.TRIGGER_UPDATE)){
handler.beforeUpdate(Trigger.oldmap,
Trigger.newmap);
TriggerController.triggerSuccessMap.put(new
TriggerControlKeyValue(soType, TriggerController.TRIGGER_UPDATE), true);
}
else if
(Trigger.isDelete &&
!TriggerController.getTriggerControlValue(soType,
TriggerController.TRIGGER_DELETE)){
handler.beforeDelete(Trigger.oldmap);
TriggerController.triggerSuccessMap.put(new
TriggerControlKeyValue(soType, TriggerController.TRIGGER_DELETE), true);
}
else if
(Trigger.isInsert &&
!TriggerController.getTriggerControlValue(soType,
TriggerController.TRIGGER_INSERT)){
handler.beforeInsert(Trigger.newmap);
TriggerController.triggerSuccessMap.put(new
TriggerControlKeyValue(soType, TriggerController.TRIGGER_INSERT), true);
}
else if
(Trigger.isUnDelete &&
!TriggerController.getTriggerControlValue(soType,
TriggerController.TRIGGER_UNDELETE)){
handler.beforeUnDelete(Trigger.oldmap);
TriggerController.triggerSuccessMap.put(new
TriggerControlKeyValue(soType, TriggerController.TRIGGER_UNDELETE), true);
}
}
else{
if
(Trigger.isUpdate &&
!TriggerController.getTriggerControlValue(soType,
TriggerController.TRIGGER_UPDATE)){
handler.afterUpdate(Trigger.oldmap,
Trigger.newmap);
TriggerController.triggerSuccessMap.put(new
TriggerControlKeyValue(soType, TriggerController.TRIGGER_UPDATE), true);
}
else if
(Trigger.isDelete && !TriggerController.getTriggerControlValue(soType,
TriggerController.TRIGGER_DELETE)){
handler.afterDelete(Trigger.oldmap);
TriggerController.triggerSuccessMap.put(new
TriggerControlKeyValue(soType, TriggerController.TRIGGER_DELETE), true);
}
else if
(Trigger.isInsert &&
!TriggerController.getTriggerControlValue(soType,
TriggerController.TRIGGER_INSERT)){
handler.afterInsert(Trigger.newmap);
TriggerController.triggerSuccessMap.put(new
TriggerControlKeyValue(soType, TriggerController.TRIGGER_INSERT), true);
}
else if
(Trigger.isUnDelete &&
!TriggerController.getTriggerControlValue(soType,
TriggerController.TRIGGER_UNDELETE)){
handler.afterUnDelete(Trigger.oldmap,
Trigger.newmap);
TriggerController.triggerSuccessMap.put(new
TriggerControlKeyValue(soType, TriggerController.TRIGGER_UNDELETE), true);
}
}
}
|
public class
TriggerController {
public static map<TriggerControlKeyValue,
boolean> triggerDisableMap = new map<TriggerControlKeyValue,
boolean>();
public static map<TriggerControlKeyValue,
boolean> triggerSuccessMap = new map<TriggerControlKeyValue,
boolean>();
public static final String TRIGGER_ALL = 'ALL';
public static final String TRIGGER_INSERT =
'INSERT';
public static final String TRIGGER_UPDATE =
'UPDATE';
public static final String TRIGGER_DELETE =
'DELETE';
public static final String TRIGGER_UNDELETE =
'UNDELETE';
public static Boolean
getTriggerControlValue(System.Type objType, String triggerType){
TriggerControlKeyValue tkv = new
TriggerControlKeyValue(objType ,triggerType);
Boolean triggerDisable = false;
if (triggerDisableMap != null
&& triggerDisableMap.containskey(tkv))
triggerDisable =
triggerDisableMap.get(tkv);
return triggerDisable;
}
public static void setTriggerControlValue(System.Type
objType, String triggerType, Boolean triggerDisable){
TriggerControlKeyValue tkv = new
TriggerControlKeyValue(objType ,triggerType);
for (TriggerControlKeyValue eachtk
: triggerDisableMap.keyset()){
if (eachtk == tkv){
tkv
= eachtk;
break;
}
}
triggerDisableMap.put(tkv,
triggerDisable);
}
public static Boolean
getTriggerSuccessValue(System.Type objType, String triggerType){
TriggerControlKeyValue tkv = new
TriggerControlKeyValue(objType ,triggerType);
Boolean triggerSuccess = false;
for (TriggerControlKeyValue eachtk
: triggerSuccessMap.keyset()){
if (eachtk == tkv){
triggerSuccess
= triggerSuccessMap.get(eachtk);
break;
}
}
return triggerSuccess;
}
public static boolean globalTriggerControlSetting(){
return (((Triggers_Off__c.getOrgDefaults() != null)
? Triggers_Off__c.getOrgDefaults().value__c : false) ||
Triggers_Off__c.getInstance(UserInfo.getUserId()).value__c ||
Triggers_Off__c.getInstance(UserInfo.getProfileId()).value__c) ;
}
public static boolean
globalTriggerPerObjectControlSetting(String obj){
if (obj != null && obj != '') {
if (!obj.endswith('__c')) obj +=
'__c';
boolean s = false;
if
(Trigger_Per_Object__c.getOrgDefaults() != null) s = (boolean)Trigger_Per_Object__c.getOrgDefaults().get(obj);
boolean t = false;
if
(Trigger_Per_Object__c.getInstance(UserInfo.getUserId()) != null) t =
(boolean)Trigger_Per_Object__c.getInstance(UserInfo.getUserId()).get(obj);
boolean u = false;
if (Trigger_Per_Object__c.getInstance(UserInfo.getProfileId())
!= null) u =
(boolean)Trigger_Per_Object__c.getInstance(UserInfo.getProfileId()).get(obj);
if (s == null) s = false;
if (t == null) t = false;
if (u == null) u = false;
return (s || t ||
u);
}else
return false;
}
}
|
public class
TriggerControlKeyValue {
public system.type
objectType;
public string triggerType;
public TriggerControlKeyValue(system.type
thisObjectType, string thisTriggerType) {
objectType = thisObjectType;
triggerType = thisTriggerType;
}
public boolean equals(object obj){
if (obj instanceof
TriggerControlKeyValue){
TriggerControlKeyValue
t = (TriggerControlKeyValue)obj;
return
(objectType.equals(t.objectType) && triggerType.equals(t.triggerType));
}
return false;
}
public integer hashCode(){
return system.hashCode(objectType)
* system.hashCode(triggerType);
}
}
|
We also need to be able to unit
test the framework to test the trigger control has been built correctly and remains
operational. To test this part of the framework we don’t want to test the
outcomes from running each individual part of the trigger as the outcomes will
be different per trigger, instead we just need to test that the code passed
through the track of code we expect. For this purpose, another map is used.
@istest
public class
TriggerControllerTest {
public static TestDataCreation td = new
TestDataCreation();
public static Account acc;
public static Triggers_Off__c trig;
public static Trigger_Per_Object__c trigPerObject;
static{
acc = td.insertAccount(null);
}
static testMethod void AccountTriggerGlobalCSTest()
{
//test global CS on/off
trig = td.insertTriggersOff(null);
Test.startTest();
//record should be
inserted
//system.assert([Select
id From Account where Name=:defaultCusName].size() == 1);
system.assert(TriggerController.getTriggerSuccessValue(Account.class,TriggerController.TRIGGER_INSERT)
== true);
//should change
acc.Name =
'ChangeCusName';
update acc;
system.assert(TriggerController.getTriggerSuccessValue(Account.class,TriggerController.TRIGGER_UPDATE)
== true);
//reset
TriggerController.triggerSuccessMap.put(new
TriggerControlKeyValue(Account.class, TriggerController.TRIGGER_UPDATE),
false);
trig.value__c =
true;
update trig;
//should not change
acc.Name =
'DefaultCusName';
update acc;
system.assert(TriggerController.getTriggerSuccessValue(Account.class,TriggerController.TRIGGER_UPDATE)
== false);
//disable insert
test.stopTest();
}
static testMethod void
AccountTriggerPerObjectCSStaticTest() {
//test trigger control using Per
Object CS
trigPerObject =
td.insertTriggersPerObject(null);
Test.startTest();
//reset
TriggerController.triggerSuccessMap.put(new
TriggerControlKeyValue(Account.class, TriggerController.TRIGGER_INSERT),
true);
//record should be
inserted but shouldnt set
Account_Insert_Succeeded
trigPerObject.Account__c
= true;
update trigPerObject;
//reset
TriggerController.triggerSuccessMap.put(new
TriggerControlKeyValue(Account.class, TriggerController.TRIGGER_INSERT),
false);
system.assert([Select
id From Account].size() == 1);
Account acc2 =
td.insertAccount(null);
system.assert([Select
id From Account].size() == 2);
system.assert(TriggerController.getTriggerSuccessValue(Account.class,TriggerController.TRIGGER_INSERT)
== false);
test.stopTest();
}
static testMethod void AccountTriggerGlobalStaticTest()
{
//test trigger control using static
variables
Test.startTest();
//disable update
TriggerController.triggerDisableMap.put(new
TriggerControlKeyValue(Account.class, TriggerController.TRIGGER_ALL), true);
acc.Name =
'ChangeCusName';
update acc;
system.assert(TriggerController.getTriggerSuccessValue(Account.class,TriggerController.TRIGGER_UPDATE)
== false);
//reset
TriggerController.triggerDisableMap.put(new
TriggerControlKeyValue(Account.class, TriggerController.TRIGGER_ALL), false);
TriggerController.triggerDisableMap.put(new
TriggerControlKeyValue(Account.class, TriggerController.TRIGGER_UPDATE),
true);
acc.Name =
'DefaultCusName';
update acc;
//should not change
system.assert(TriggerController.getTriggerSuccessValue(Account.class,TriggerController.TRIGGER_UPDATE)
== false);
//update should run
TriggerController.triggerSuccessMap.put(new
TriggerControlKeyValue(Account.class, TriggerController.TRIGGER_UPDATE),
false);
TriggerController.triggerDisableMap.put(new
TriggerControlKeyValue(Account.class, TriggerController.TRIGGER_UPDATE),
false);
acc.Name =
'DefaultCusName';
update acc;
system.debug('##
TriggerController ' + TriggerController.triggerSuccessMap);
system.assert(TriggerController.getTriggerSuccessValue(Account.class,TriggerController.TRIGGER_UPDATE)
== true);
//test insert
trigger code off
TriggerController.triggerSuccessMap.put(new
TriggerControlKeyValue(Account.class, TriggerController.TRIGGER_INSERT),
false);
TriggerController.triggerDisableMap.put(new
TriggerControlKeyValue(Account.class, TriggerController.TRIGGER_INSERT),
true);
system.assert([Select
id From Account].size() == 1);
Account acc2 =
td.insertAccount(null);
system.assert([Select
id From Account].size() == 2);
//should not change
system.assert(TriggerController.getTriggerSuccessValue(Account.class,TriggerController.TRIGGER_INSERT)
== false);
test.stopTest();
}
}
|
No comments:
Post a Comment
Note: only a member of this blog may post a comment.