Post Exalate Processor

    Disclaimer

    • This is an experimental feature and there is no guarantee of its stability, as it didn't run through a complete validation cycle
    • The feature can evolve and even be retracted in case it doesn't meet our quality criteria
    • We love to get feedback on experimental features which can be sent to support@exalate.com

    The post-exalate processor executes a change on the original issue once the synchronization has been finished. It's an advanced option of the 
    Sync Rules configuration.
    property nametypedescription
    issueKeyIssueKeyA key of an issue that was synchronized

    remoteIssueKey

    IssueKeyA Key of an issue, created on the other side

    jiraIssue

    MutableIssueAn issue that was synchronized

    isParentIssue

    BooleanTrue if the issue was initiating the sync

    workflowUtil

    WorkflowUtilHelper method for JIRA issue transitions 

    jiraProxyUser

    ApplicationUserThe proxy user configured in General Settings


    You can use the Post exalate processor to automate some actions, which should be done once the issue has been synchronized. For example:

    • Sync sub-tasks after parent issue sync
    • Sync epic stories, after epic sync
    • Update a custom field with the remote issue key
    • Transition Exalated issue
    • Delete an issue if the twin has been deleted


    Sync Sub-tasks after Parent Issue Sync

    import com.exalate.api.replication.out.IEventSchedulerService
    import com.atlassian.jira.component.ComponentAccessor
    import com.exalate.basic.domain.BasicIssueKey
    import com.exalate.api.domain.trigger.ExalateJiraEventType
     
    if(isParentIssue && eventType == ExalateJiraEventType.EXALATED){
      final def eventScheduler = ComponentAccessor.getOSGiComponentInstanceOfType(IEventSchedulerService.class)
      def stm = ComponentAccessor.getSubTaskManager()
      def subtasks = stm.getSubTaskObjects(jiraIssue)
      subtasks.each{
        def subtaskKey = new BasicIssueKey(it.id, it.key)
        eventScheduler.schedulePairEvent(subtaskKey, connection)
      }
    }


    Sync epic Stories, after epic Sync

    import com.exalate.api.replication.out.IEventSchedulerService
    import com.atlassian.jira.component.ComponentAccessor
    import com.exalate.basic.domain.BasicIssueKey
    import com.exalate.api.domain.trigger.ExalateJiraEventType
    def getStories = { BasicIssueKey epicExIssueKey ->
            def esClass = ComponentAccessor.getPluginAccessor().getClassLoader().findClass("com.atlassian.greenhopper.api.issuelink.ManagedIssueLinkTypesService")
            def es = ComponentAccessor.getOSGiComponentInstanceOfType(esClass)
            if (es == null) {
                throw relationLevelErrorInternal("Unable to load the epic link manager, even though the Jira Agile plugin is found. The script is wrong and should be adapted")
            }
            def iltResult = es.getEpicLinkIssueLinkType()
            if (!iltResult.isValid()) {
                def errorCollection = iltResult.errorCollection
                throw relationLevelErrorInternal(
                        "Unable to get the story-epic issue link: " +
                                "\n\tErrors: " + errorCollection?.errors +
                                "\n\tReasons: " + errorCollection?.reasons
                )
            }
            def epicLinkType = iltResult.get()
            if (epicLinkType == null) {
                throw relationLevelErrorInternal(
    			"Epic Link Type is null. Something is terribly wrong with either your Jira Agile installation or your script."
    			)
            }
            def stories = ComponentAccessor.getIssueLinkManager().getOutwardLinks(epicExIssueKey.id)
                    .findAll { epicLinkType.id.equals(it.linkTypeId) }
                    .collect { it.destinationObject }
            return stories.collect { ["id" : it.id, "key": it.key]}
        }
    if(isParentIssue && eventType == ExalateJiraEventType.EXALATED){
      final def eventScheduler = ComponentAccessor.getOSGiComponentInstanceOfType(IEventSchedulerService.class)
      def stories = getStories(issueKey)
      stories.each{
        eventScheduler.schedulePairEvent(new BasicIssueKey(it.id, it.key), connection)
      }
    }



    Update a Custom Field with the Remote Issue Key

    import com.atlassian.jira.component.ComponentAccessor
    import  com.atlassian.jira.event.type.EventDispatchOption
    import com.exalate.api.domain.trigger.ExalateJiraEventType
    if(isParentIssue && eventType == ExalateJiraEventType.EXALATED){
      def cf = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("Remote Issue Key")
      jiraIssue.setCustomFieldValue(cf, remoteIssueKey.getURN())
      ComponentAccessor.getIssueManager().updateIssue(jiraProxyUser, jiraIssue, EventDispatchOption.ISSUE_UPDATED, false)
    }



    Transition Exalated Issue 

    import com.exalate.api.domain.trigger.ExalateJiraEventType
     
    if(eventType == ExalateJiraEventType.EXALATED)
    	workflowUtil.doTransition(jiraProxyUser, jiraIssue, "Start Progress") 


    Delete an Issue if the Twin has been Deleted

    import com.atlassian.jira.component.ComponentAccessor
    import com.exalate.api.domain.trigger.ExalateJiraEventType
    
    
    def issueService = ComponentAccessor.getIssueService()
    
    
    if(isParentIssue && eventType == ExalateJiraEventType.DELETED){
      def validation = issueService.validateDelete(jiraProxyUser, jiraIssue.id)
      
      log.info("validation of the delete is ${validation.isValid()} and error collection is ${validation.getErrorCollection()}")
      if (validation.isValid()) {
        log.info("Going to delete issue with key ${jiraIssue.key}")
        issueService.delete(jiraProxyUser, validation)
      }
    }