The Exalate team will be on holiday for the coming days - returning Jan 4
Enjoy & stay safe

The main requirement for this use case is to maintain the issue hierarchy while Exalating issues between ServiceNow and Azure DevOps (bi-directionally).


The Incident → Incident Task relationship in ServiceNow must be reflected as Task → Issue relationship on Azure DevOps, and vice versa. The following depicts what we are trying to achieve here:

Let us first consider the ServiceNow to Azure DevOps side:


  • ServiceNow outgoing scripts need to ensure that the both the Incident and Incident_task tables are catered for:


    if(entity.tableName == "incident") {
        replica.key            = entity.key
        replica.summary        = entity.short_description
        replica.description    = entity.description
        replica.attachments    = entity.attachments
        replica.comments       = entity.comments
        replica.state          = entity.state
        replica.entityType = "incident"
    }
    if(entity.tableName == "incident_task") {
        replica.key            = entity.key
        replica.summary        = entity.short_description
        replica.description    = entity.description
        replica.attachments    = entity.attachments
        replica.comments       = entity.comments
        replica.state          = entity.state
    
        if (entity?.incident)    
            replica.parentid = nodeHelper.getTableByLink(entity.incident.link).sys_id
        replica.entityType = "incident_task"   
    }
  • On the Azure DevOps side, we need to firstly map the issue types. This can be easily done using the following code segment on the Incoming Script on the Azure DevOps side:

       if (replica.entityType == "incident")
            workItem.typeName = "Task";
       else if (replica.entityType == "incident_task")
            workItem.typeName = "Issue";
  • The next step on the Azure DevOps side would be populate the parent link in order to ensure that the hierarchy is maintained. This can be done via the following code snippet, that uses the getLocalIssueKeyFromRemoteId method, added in the Azure DevOps incoming scripts:

    if(replica.parentid){
        def localParent = syncHelper.getLocalIssueKeyFromRemoteId(replica.parentid, 'incident').id
        if(localParent)
            issue.parentId = localParent
        else 
           throw new com.exalate.api.exception.IssueTrackerException("Cant field parent with id " + replica.parentId)
    }

Let us now consider the Azure DevOps to ServiceNow direction:

  • The key to maintaining the hierarchy here is passing the parentid field in the outgoing script on the Azure DevOps side:

    replica.parentId = workItem.parentId



  • Next we need to ensure that the correct mappings are present on the ServiceNow Incoming scripts:

        if (replica.type.name == "Task")
            entity.tableName = "incident"
        else if (replica.type.name == "Issue")
            entity.tableName = "incident_task"
  • And to close this off, we will employ getLocalIssueKeyFromRemoteId to fetch the local parent and maintain the hierarchy:

    entity.incident  = syncHelper.getLocalIssueKeyFromRemoteId(replica.parentId)?.idStr


The entire code from the ServiceNow side is here, and for Azure DevOps is here


Please review the following video to see the use case in action:

SNOW_ADO_Bi-directional Hierarchy.mp4



Questions