Note: This use case can be implemented using Exalate Classic. Please contact our team for more details.
Given the stormy weather in Belgium today, I had some time to write up an article which I wanted to do for quite some time.
We got sometimes the question from our users how to automatically exalate all subtasks whenever an issue is being exalated
Exalate is an issue sync solution allowing to integrate between Jira, ServiceNow, SalesForce, Zendesk, ... - (and yes - I'm part of the team building that addon).
Therefore I thought to implement this requirement with ScriptRunner ...
The article shows how the exalate classes can be instantiated to trigger an exalate operation.
The way it works is
Setting up the custom Listener
An image is worth 1000 words. The event the listener needs to listen to is 'com.exalate.api.domain.trigger.EXALATED'
How does it work in detail
event.issue contains the parent issue which has been exalated. The exalateNow function is called on every subtask
def subTasks = event.issue.subTaskObjects
subTasks.each {
log.info("Subtask trigger - ${it.key}")
exalateNow(it, "auto_subtask")
}
The exalateNow function is a closure taking an issue object and a connection name.
21
22 def exalateNow = {
23 Issue issue, String connectionName ->
24
25
26 def connection = relrepo.getConnectionByName(connectionName)
27 if (connection == null) {
28 throw new IllegalArgumentException("""Connection with name ${connectionName} is not found""")
29 }
30
31 log.info("Connection found")
32
33 def exaIssueKey = basicIssueKey.newInstance(issue.id, issue.key)
34 log.info("Issue key available ${exaIssueKey.properties}")
35
36 def hubIssue = intNodeHelper.getHubIssueWithInitializedCustomFieldsValues(issue)
37 log.info("hubIssue built")
38
39 ess.schedulePairEvent(exaIssueKey, connection, hubIssue, null )
40 log.info("Pair event scheduled")
41
42 }
The exalateNow code is using a couple of classes that need to be instantiated.
This can be done using the code below
5 // load necessary classes
6 def exaPlugin = ComponentAccessor.pluginAccessor.getEnabledPlugin("com.exalate.jiranode")
7 def exaCl = exaPlugin.getClassLoader()
8
9 def basicIssueKey = exaCl.loadClass("com.exalate.basic.domain.BasicIssueKey")
10 def essClass = exaCl.loadClass("com.exalate.api.replication.out.IEventSchedulerService")
11 def ttRepoClass = exaCl.loadClass("com.exalate.api.persistence.twintrace.ITwinTraceRepository")
12 def relationRepoClass = exaCl.loadClass("com.exalate.api.persistence.relation.IRelationRepository")
13 def intNodeHelperClass = exaCl.loadClass("com.exalate.api.node.hubobject.v1_4.INodeHubIssueHelper")
14
15 def ttrepo = ComponentAccessor.getOSGiComponentInstanceOfType(ttRepoClass)
16 def ess = ComponentAccessor.getOSGiComponentInstanceOfType(essClass)
17 def relrepo = ComponentAccessor.getOSGiComponentInstanceOfType(relationRepoClass)
18 def intNodeHelper = ComponentAccessor.getOSGiComponentInstanceOfType(intNodeHelperClass)
Wrap up
This article is meant to give an example how to instantiate the exalate classes and use these to perform a more advanced synchronisation case.
Using the above logic it is possible to write a procedure to synchronise a complete issue structure (Project -> Epic -> Story) and all related subtasks.
Disclaimer
This example is using non-public api's which will change between different versions of the addon. Take this into account whenever using the api's to implement production scripts.
The example is an example and there is no guarantee that it would work in your case. If in doubt - there are 130+ Exalate partners providing professional services to help out.
francis
5 comments