EDA and NPSP come with Table-Driven Trigger Management (TDTM), a trigger framework that uses records in a custom object to track which "trigger handler" Apex classes are active, rather than, say, using records in Custom Metadata. (I think it just precedes custom metadata or something.)
I've been enjoying CumulusCI as a way of managing my attempt to make clean, modular GitHub repositories for "clumps" of business logic, but one thing that's annoying is having to put steps into the repo's README.md
file instructing admins that if they'd like to actually see how well the triggers in my codebase work, they have to actually bother to add some trigger handler records to that data table.
Is there a best-practice way, using CCI, to ensure that any time I spin up a new scratch org from the repo using cci flow run dev_org --org ScratchOrgAliasName
or any time I install the repo into a persistent sandbox using a command like cci task run dx_push --org PersistentSandboxAliasName
, data records get pushed into the "Trigger Handlers" table if ones like them don't already exist?
I see https://cumulusci.readthedocs.io/en/latest/data.html but it seems really involved (postgreSQL databases and such), so I'd like to make sure it's actually what I need for a task this small before diving in too deep on so complex an approach.
Update: I realized maybe EDA itself was using the advice at https://cumulusci.readthedocs.io/en/latest/data.html and did some copypasta from it and another open-source community commons package, and realized that the whole "SQL" thing isn't so bad -- you don't, like, actually have to set up and run a database or anything.
So far, I've been able to get cci task run load_dataset -o mapping datasets/mapping.yml -o sql_path datasets/data.sql --org ScratchOrgAliasName
to work just by having a .yml
and a .sql
file in my repo:
datasets/mapping.yml
:
Upsert hed__Trigger_Handler__c:
sf_object: hed__Trigger_Handler__c
table: hed__Trigger_Handler__c
fields:
Name: Name
hed__Active__c: hed__Active__c
hed__Asynchronous__c: hed__Asynchronous__c
hed__Class__c: hed__Class__c
hed__Filter_Field__c: hed__Filter_Field__c
hed__Filter_Value__c: hed__Filter_Value__c
hed__Load_Order__c: hed__Load_Order__c
hed__Object__c: hed__Object__c
hed__Owned_by_Namespace__c: hed__Owned_by_Namespace__c
hed__Trigger_Action__c: hed__Trigger_Action__c
hed__User_Managed__c: hed__User_Managed__c
hed__Usernames_to_Exclude__c: hed__Usernames_to_Exclude__c
datasets/data.sql
:
BEGIN
TRANSACTION;
CREATE TABLE "hed__Trigger_Handler__c" (
"Name" VARCHAR(255),
"hed__Active__c" VARCHAR(255),
"hed__Asynchronous__c" VARCHAR(255),
"hed__Class__c" VARCHAR(255),
"hed__Filter_Field__c" VARCHAR(255),
"hed__Filter_Value__c" VARCHAR(255),
"hed__Load_Order__c" VARCHAR(255),
"hed__Object__c" VARCHAR(255),
"hed__Owned_by_Namespace__c" VARCHAR(255),
"hed__Trigger_Action__c" VARCHAR(255),
"hed__User_Managed__c" VARCHAR(255),
"hed__Usernames_to_Exclude__c" VARCHAR(255),
PRIMARY KEY (Name)
);
INSERT INTO
"hed__Trigger_Handler__c"
VALUES
(
'A_Good_Record_Name',
'true',
'false',
'myscope_Contact_TDTM',
'',
'',
'2.00',
'Contact',
'',
'BeforeInsert;BeforeUpdate',
'false',
''
);
COMMIT;
The only catch is if I run it twice, it inserts another record, so still some fiddling to do, I guess.