An example of how to write a trigger on the zAgileConnect ZIssue custom object in Salesforce. The trigger may be used to perform specific actions on a Case based upon specific Issue events (such as change in Status, Assignee, etc.).
Please note that while we offer this as a guide, customizations based on zAgileConnect objects and resources are not supported by zAgile unless performed directly by zAgile.
Create New Trigger
Go to developer console and click on File/New/Apex Trigger. Give a name to your trigger, select the object ZIssue__c and submit.
The object ZIssue is shown with the prefix ‘zsfjira’, then you find it as ‘zsfjira__ZIssue__c’. If the object ZIssue is not visible contact your administrator for profile apex classes access permissions.
Basic Trigger Body
As soon as you create the new Trigger, the new Apex trigger is registered in Salesforce and already active. Salesforce writes a basic body for your Trigger:
trigger
ZIssueCustomTrigger
on
zsfjira__ZIssue__c (before
insert
) {
}
We highly recommend that you use the Trigger in AFTER events in order to minimize any impact on ZIssue update. A Before event may cause an exception in DML operation, causing data inconsistencies between Salesforce and JIRA. Furthermore, you must also take extra care to handle any exceptions that are generated in this trigger, to ensure that updates to ZIssue (coming from JIRA) do not fail.
Your Trigger declaration should be as follows:
trigger ZIssueCustomTrigger on zsfjira__ZIssue__c (after insert, after update, after delete) {
}
Events Based on ZIssue
The following table describes events that will cause a DML operation on ZIssue object:
Event
|
Happens When
|
---|---|
Insert | Linking an Issue that does no exists on ZIssue. |
Update | When updating an existing Issue on ZIssue through JIRA or Salesforce (Update is done only if at least one mapped field is updated, this mean this event is fired also when: performing transitions, setting users, moving issues to different project, etc). Please note that zAgileConnect performs updates on ZIssue on non Issue related fields, like internal flags. These updates should not execute any action in your trigger, as these updates are not JIRA Issue updates. You should look at fields updated in ZIssue and only execute any action if the updated issue field is relevant to you. |
Delete | When deleting an existing Issue in ZIssue through JIRA or Salesforce |
Undelete | Is never fired by zAgileConnect. It should not be implemented. |
Getting Cases related to Issues
The context of ZIssue trigger is the mapped JIRA Issue fields. To get additional information related to the Issues (ex: Cases related to the Issue), you will need to execute a SOQL.
The following SOQL returns the Cases related to an Issues in a List form:
SELECT
Id,zsfjira__IssueKey__c,(
SELECT
zsfjira__CaseId__r.CaseNumber
FROM
zsfjira__ZIssue_Case__r)
FROM
zsfjira__ZIssue__c
WHERE
Id
IN
(
SELECT
zsfjira__ZIssueId__c
FROM
zsfjira__ZIssue_Case__c)
We would use same SOQL query to get related Cases of updated issues (note that condition was set in the SOQL query for filtering only the updated/inserted issues, Trigger.newMap.keySet())
trigger
ZIssueCustomTrigger
on
zsfjira__ZIssue__c (
after
insert
,
after
update
,
after
delete
) {
if(
trigger
.isUpdate||
trigger
.isInsert){
try{//It
is
mandatory that you wrap
all
your
trigger
code
in
try
and
catch statements
for
(zsfjira__ZIssue__c issue:[
SELECT
Id,zsfjira__IssueKey__c,(
SELECT
zsfjira__CaseId__r.CaseNumber
FROM
zsfjira__ZIssue_Case__r)
FROM
zsfjira__ZIssue__c
WHERE
Id
IN
:
Trigger
.newMap.keySet()]){
System.debug(
'Issue: '
+issue.zsfjira__IssueKey__c+
', Related Cases: '
);
for
(zsfjira__ZIssue_Case__c rel:issue.zsfjira__ZIssue_Case__r){
System.debug(
'Case Number:'
+ rel.zsfjira__CaseId__r.CaseNumber+
', Case Id: '
+rel.zsfjira__CaseId__c);
}
}
}catch(Exception exe){
//handle
any
error executing your
trigger
, error logs, send email, etc...
}
}
}
Error Handling
Handling all the trigger exceptions is critical. Trigger exceptions not handled could impact Issue updates in Salesforce, partially disabling the functionality of zAgileConnect.
Custom trigger should filter execution based on updated fields
Any custom trigger on ZIissue events must be executed AFTER UPDATE/INSERT to avoid problems updating issues in salesforce and should use Trigger.newMap, Trigger.oldMap to filter only updates on ZIssue fields of interest (for instance if you are writing a trigger that updates a case field based on JIRA issue status update, you need to make sure that the JIRA status field was updated, and not other field). It is because we have some fields that we use as flags in ZIssue custom object and we update them grammatically any time, these updates of course does not mean JIRA issue updates and should not trigger any execution (for instance, your trigger should not update any case field when our internal flags are updated on ZIssue)
Performing DML in the Trigger
As mentioned previously, any non handled exception will cause the trigger to fail and ZIssue object to become outdated with JIRA. As a consequence, some zAgileConnect features will not work as expected. The most common causes of exceptions given during the trigger are DML operations specially if the related Object has triggers. Please follow this recommendations in order to avoid this problem:
- Surround your DML operation with try and catch to avoid impacting ZIssue Object if any error occurs:
try
{
update cases;
}
catch
(DmlException d){
System.debug(
'Exception during DML operation: '
+ d.getMessage());
}
- Use Database.update(List<SObject>, false) with SaveResult[] to allow partial updates (update all the record that can be updated so if any individual record update fails, all will not fail)
Database.SaveResult[] lsr = Database.update(cases,
false
);
for
(Database.SaveResult sr : lsr){
if
(!sr.isSuccess()) {
myMethodToaddToErrorObjectList(sr);
}
}
We recommend you use both best practices in your trigger. check Salesforce documentation for further details: https://developer.salesforce.com/page/An_Introduction_to_Exception_Handling
Testing the Trigger
For testing the trigger please follow these considerations:
- The ZIssue record when is created is not related to any Case yet.
- For testing a ZIssue insertion, you will need required fields IssueKey__c and IssueId__c for testing purpose you can fill them with any values, also the Name field must be the Issue key.
- For inserting to ZIssue_SF (junction object) you will need to set the IssueId of the JIRA issue which belongs the relation to the ZIssue_SF field IssueId__c, other consideration to take is filling the field IssueCreated__c which is a boolean that indicates if the issue was created from the related case, otherwise it was just linked. finally fill the field zsfjira__ZIssue__c with the SF record ID of ZIssue and zsfjira__Case__c with de SF Case record ID.
This is an example to test the trigger on a inserting and updating scenario, you may need to assert your results according to your trigger logic.
@isTest
public
class
ZIssueCustomTriggerTest {
static
testMethod
void
testTrigger() {
//--INSERT
zsfjira__ZIssue__c ziss =
new
zsfjira__ZIssue__c();
ziss.zsfjira__IssueId__c =
1002
;
ziss.Name=ziss.zsfjira__IssueKey__c =
'ABM-2'
;
ziss.Name=ziss.zsfjira__Status__c =
'Open'
;
insert ziss;
Case caseTest =
new
Case();
caseTest.Subject =
'Case Test'
;
insert caseTest;
zsfjira__ZIssue_SF__c rel =
new
zsfjira__ZIssue_SF__c();
rel.zsfjira__IssueId__c =
1002
;
rel.zsfjira__ZIssue__c = ziss.Id;
rel.zsfjira__Case__c = caseTest.Id;
rel.zsfjira__IssueCreated__c =
false
;
insert rel;
System.
assert
(ziss!=
null
);
//--UPDATE
ziss = [SELECT Id,zsfjira__Status__c FROM zsfjira__ZIssue__c WHERE zsfjira__IssueKey__c=
'ABM-2'
];
ziss.zsfjira__Status__c =
'Closed'
;
upsert ziss;
System.
assert
(ziss!=
null
);
}
}