Companies often need to set up advanced or customized integrations between Jira and Azure DevOps. Such integrations are commonly used to streamline communication between disparate applications.
In this article, we discuss how to maintain issue links and their relationship types between Jira and Azure DevOps. We also see a secondary requirement of syncing the parent-child equation of issues and sub-tasks.
We use a synchronization solution called Exalate to implement this use case.
The following are the use case requirements:
A visual depiction of the use case will make things clearer.
The requirements are unique and advanced. So there are a couple of challenges we need to address first:
After taking note of these challenges, it’s time to implement the use case.
But first, we need to understand why we chose Exalate.
Exalate is a 3rd-party synchronization solution that supports integrations for various work management systems like Jira, ServiceNow, Azure DevOps, Salesforce, Zendesk, GitHub, etc.
We chose Exalate to implement this advanced integration use case for the following reasons:
Sometimes there is a need to sync entities in bulk if a particular condition is satisfied.
Start by installing Exalate on the integrating systems, Jira and Azure DevOps in our case.
Then you can establish a connection between the 2 instances in the Script Mode.
You can also find a step-by-step guide on how to do that.
The Script mode has Groovy-based scripts in the form of “Incoming sync” and “Outgoing sync”.
When a connection is established, click on the “Configure Sync” button or edit the connection to access the “Rules” tab. This is where the syncs we just talked about reside.
These syncs are present in both Jira and Azure DevOps instances. That means:
Let’s look at the actual scripts required to implement this use case.
Essentially, the only information that must move out of Jira are the issue links and the parent ID for the sub-tasks.
The following code does it:
replica.linkedIssues = issue.issueLinks
replica.parentId = issue.parentId
The main mapping logic is on the Azure DevOps side.
You must add a parent-child relationship corresponding to the task-sub-task relation in Jira.
Take help from the following code to do that:
workItem.parentId = null
if (replica.parentId) {
def localParent = syncHelper.getLocalIssueKeyFromRemoteId(replica.parentId.toLong())
if (localParent)
workItem.parentId = localParent.id
}
Next, we must cover the relationship type requirement, i.e mapping the links and creating them in Azure DevOps.
To do that, we create a mapping and then call an end-point to get the relevant links created in Azure DevOps.
Refer to the following code:
def res =httpClient.get("/Majids%20Development/_apis/wit/workItems/${workItem.id}/revisions",true) def await = { f -> scala.concurrent.Await$.MODULE$.result(f, scala.concurrent.duration.Duration.apply(1, java.util.concurrent.TimeUnit.MINUTES)) } def creds = await(httpClient.azureClient.getCredentials()) def token = creds.accessToken() def baseUrl = creds.issueTrackerUrl() def project = workItem.projectKey def localUrl = baseUrl + '/_apis/wit/workItems/' + workItem.id int x =0 res.value.relations.each{ revision -> def createIterationBody1 = [ [ op: "test", path: "/rev", value: (int) res.value.size() ], [ op:"remove", path:"/relations/${++x}" ] ] }
def linkTypeMapping = [
"blocks": "System.LinkTypes.Dependency-Reverse",
"relates to": "System.LinkTypes.Related"
]
def linkedIssues = replica.linkedIssues
if (linkedIssues) {
replica.linkedIssues.each{
def localParent = syncHelper.getLocalIssueKeyFromRemoteId(it.otherIssueId.toLong())
if (!localParent?.id) { return; }
localUrl = baseUrl + '/_apis/wit/workItems/' + localParent.id
def createIterationBody = [
[
op: "test",
path: "/rev",
value: (int) res.value.size()
],
[
op:"add",
path:"/relations/-",
value: [
rel:linkTypeMapping[it.linkName],
url:localUrl,
attributes: [
comment:""
]
]
]
]
def createIterationBodyStr = groovy.json.JsonOutput.toJson(createIterationBody)
converter = scala.collection.JavaConverters;
arrForScala = [new scala.Tuple2("Content-Type","application/json-patch+json")]
scalaSeq = converter.asScalaIteratorConverter(arrForScala.iterator()).asScala().toSeq();
createIterationBodyStr = groovy.json.JsonOutput.toJson(createIterationBody)
def result = await(httpClient.azureClient.ws
.url(baseUrl+"/${project}/_apis/wit/workitems/${workItem.id}?api-version=6.0")
.addHttpHeaders(scalaSeq)
.withAuth(token, token, play.api.libs.ws.WSAuthScheme$BASIC$.MODULE$)
.withBody(play.api.libs.json.Json.parse(createIterationBodyStr), play.api.libs.ws.JsonBodyWritables$.MODULE$.writeableOf_JsValue)
.withMethod("PATCH")
.execute())
}
}
Once you have set all of this up, it’s time to see the output.
You can set up automatic sync triggers to start the sync whenever a Story is created in Jira.
Work items and the relations are synced and the parent-child hierarchy is maintained.
Note: For additional information about Exalate, watch its Academy videos.
Integrating Jira and Azure DevOps can bring in a lot of benefits. It can reduce manual efforts, increase the visibility of important information, and bring everyone on the same page. You can use 3rd-party tools like Exalate to implement similar integrations.
Book a demo with Exalate to see how it works for your specific scenario.
Syed Majid Hassan -Exalate-
1 comment