Can't get sprint sync to trigger from Jira Server to Jira Cloud

Originally asked by Eric Hilfer on 24 June 2021 (original question)


Trigger is set up for Sprint - there is no configuration paramater, so it should trigger on all sprint updates.

Outgoing script is below.

Trigger is enabled, and we update the goal in an existing sprint, and there is nothing in outgoing sync queue or in the log indicating that it tried to synch anything.

If we do a bulk exalate from the sprint trigger, it racks up about 300 nullpointer exceptions related to all kinds of issues. Maybe it is also hitting a different trigger for issues - it’s hard to tell.

Is there any way to verify that this script is getting called from the updated Sprint object?

Outgoing script:

if(entityType == “sprint” && sprint.originBoardId == “107” && (sprint.id == “321” || sprint.id == “326” )){

//Only sync sprints on board 107 and sprints on or after sprint 19 (id \>\= 321\)

[replica.name](http://replica.name) \= [sprint.name](http://sprint.name)

replica.goal \= sprint.goal

replica.state \= sprint.state

replica.startDate \= sprint.startDate

replica.endDate \= sprint.endDate

replica.originBoardId \= sprint.originBoardId

}

if(entityType == “issue”){

//Executed when syncing an issue to a remote side

replica.summary = issue.summary

replica.description = issue.description

replica.project = issue.project

replica.type = issue.type

//....

//other script rules to sync issues

//sprint....

replica.customFields.Sprint = issue.customFields.Sprint

replica.key = issue.key

replica.type = issue.type

replica.assignee = issue.assignee

replica.reporter = issue.reporter

replica.summary = issue.summary

replica.description = issue.description

replica.labels = issue.labels

replica.comments = issue.comments

replica.resolution = issue.resolution

replica.status = issue.status

replica.parentId = issue.parentId

replica.priority = issue.priority

replica.attachments = issue.attachments

replica.project = issue.project

replica.customFields.“Story Points” = issue.customFields.“Story Points”

SubTask.send()

// Ensure that the Epic is synced first, such that stories sent later can be associated to the right epic

Epic.sendEpicFirst()

}

//Comment these lines out if you are interested in sending the full list of versions and components of the source project.

replica.project.versions =

replica.project.components =


Comments:

Ariel Aguilar commented on 25 June 2021

Hi Eric,

We see you have on your script:

SubTask.send()

Do you have the external script properly added as per:

https://docs.idalko.com/exalate/x/RQaOAQ?
Also, there is:

Epic.sendEpicFirst()

Same case, do you have the external script already added as per:

https://docs.idalko.com/exalate/x/9ICKAg?

Kind regards,

Ariel

Eric Hilfer commented on 25 June 2021

Epics and subtasks are sync’ing correctly - here is the incoming script on the external Jira Cloud side:

// Sprints
//entityType represent the type of the remote side entity
if(entityType == “sprint”){
//Executed when receiving a sprint sync from the remote side
def sprintMap = [“107”:“4”] //[remoteBoardId: localBoardId]

[sprint.name](http://sprint.name/) \= [replica.name](http://replica.name/)  
sprint.goal \= replica.goal  
sprint.state \= replica.state  
sprint.startDate \= replica.startDate  
sprint.endDate \= replica.endDate  
def localBoardId \= sprintMap\[replica.originBoardId]  
if(localBoardId \=\= null){  
   throw new com.exalate.api.exception.IssueTrackerException("No board mapping for remote board id "\+replica.originBoardId)  
}  
sprint.originBoardId \= localBoardId //Set the board ID where the sprint will be created  

}
if(entityType == “issue”){
//Executed when receiving an issue sync from the remote side
if(firstSync){
issue.projectKey = “JAN”
if(replica.parentId){
issue.typeName = “Sub-task” //Make sure to use the right subtask type here.
def localParent = nodeHelper.getLocalIssueFromRemoteId(replica.parentId.toLong())
if(localParent){
issue.parentId = localParent.id
} else {
throw new com.exalate.api.exception.IssueTrackerException(“Subtask cannot be created: parent issue with remote id " + replica.parentId + " was not found. Please make sure the parent issue is synchronized before resolving this error” )
}

    }

    // Set type name from source issue, if not found set a default  
    issue.typeName     \= nodeHelper.getIssueType(replica.type?.name, issue.projectKey)?.name ?: "Sub\-task"  
          
      
}  
//....  
//other script rules to sync issues  
issue.summary      \= replica.summary  
issue.description  \= replica.description  
// issue.comments     \= commentHelper.mergeComments(issue, replica)  
issue.attachments  \= attachmentHelper.mergeAttachments(issue, replica)  
issue.labels       \= replica.labels

/\*  
User Synchronization (Assignee/Reporter)

Set a Reporter/Assignee from the source side, if the user can't be found set a default user  
You can use this approach for custom fields of type User  
\*/  
def defaultUser \= nodeHelper.getUserByEmail("default@[idalko.com](http://idalko.com/)")  
issue.reporter \= nodeHelper.getUserByEmail(replica.reporter?.email) ?: defaultUser  
issue.assignee \= nodeHelper.getUserByEmail(replica.assignee?.email) ?: defaultUser

  
/\*  
Status Synchronization

Sync status according to the mapping \[remote issue status: local issue status]  
If statuses are the same on both sides don't include them in the mapping  
\*/  
def statusMapping \= \["BA Review":"UAT"]  
def remoteStatusName \= [replica.status.name](http://replica.status.name/)  
issue.setStatus(statusMapping\[remoteStatusName] ?: remoteStatusName)  
  
/\*  
Custom Fields

This line will sync Text, Option(s), Number, Date, Organization, and Labels CFs  
For other types of CF check documentation  
issue.customFields."CF Name".value \= replica.customFields."CF Name".value  
\*/  
issue.customFields."Story Points"?.value \= replica.customFields."Story Points".value  
  
/\*  
Comment Synchronization

Sync comments with the original author if the user exists in the local instance  
Remove original Comments sync line if you are using this approach  
\*/  
issue.comments \= commentHelper.mergeComments(issue, replica){ it.executor \= nodeHelper.getUserByEmail(it.author?.email) }  

  
//....  
def remoteSprintId \= replica.customFields.Sprint?.value?.find { it.state.toUpperCase() !\= "CLOSED" }?.id  
if(remoteSprintId){  
   def localSprintId \= nodeHelper.getLocalIssueKeyFromRemoteId(remoteSprintId, "sprint")?.id  
     if(localSprintId){  
        issue.customFields.Sprint.value \= localSprintId  
    }  
}  
// Epics      
// should be at the end of the incoming sync  script  
Epic.receive()  

}

Eric Hilfer commented on 25 June 2021

To help with debugging, I added the following lines to the on-prem outgoing rules script at the beginning:

log.info("entityType: "+ entityType)

if(entityType == “sprint”){

[log.info](http://log.info)("sprint.originBoardId: "\+  sprint.originBoardId)

[log.info](http://log.info)("[sprint.id](http://sprint.id): "\+  [sprint.id](http://sprint.id))

[log.info](http://log.info)("[sprint.name](http://sprint.name): "\+  [sprint.name](http://sprint.name))

}

And we turned on debug logging on the on-prem Jira as described here:

https://docs.idalko.com/exalate/display/ED/Jira+on-premise%3A+How+to+enable+debug+logging

But, I am not seeing any of my info messages in the exalate.log file, even though some issues have been processed and another attempt was made to update the sprint object on board 107 with sprint id 326. Are we doing something wrong with the logging setup, or looking in the wrong log file?

Eric Hilfer commented on 25 June 2021

Update: the logging info was actually going into the attlasian-Jira log - we were looking in the wrong place.

Eric Hilfer commented on 25 June 2021

The log info indicates a script error related to trying to apply “versions” to the sprint object.

2021-06-24 20:53:05,899+0000 pool-153-thread-1 INFO sburkhart 1162x553072x1 pfcu7y 71.184.147.4,172.17.20.137,127.0.0.1 /secure/ExalateIssueOperation.jspa [com.exalate.script] Executing the data filter for the issue `326`, connection `Argos_to_Unicon`
2021-06-24 20:53:05,941+0000 pool-153-thread-1 INFO sburkhart 1162x553072x1 pfcu7y 71.184.147.4,172.17.20.137,127.0.0.1 /secure/ExalateIssueOperation.jspa [com.exalate.script] entityType: sprint
2021-06-24 20:53:05,941+0000 pool-153-thread-1 INFO sburkhart 1162x553072x1 pfcu7y 71.184.147.4,172.17.20.137,127.0.0.1 /secure/ExalateIssueOperation.jspa [com.exalate.script] sprint.originBoardId: 107
2021-06-24 20:53:05,941+0000 pool-153-thread-1 INFO sburkhart 1162x553072x1 pfcu7y 71.184.147.4,172.17.20.137,127.0.0.1 /secure/ExalateIssueOperation.jspa [com.exalate.script] sprint.id: 326
2021-06-24 20:53:05,941+0000 pool-153-thread-1 INFO sburkhart 1162x553072x1 pfcu7y 71.184.147.4,172.17.20.137,127.0.0.1 /secure/ExalateIssueOperation.jspa [com.exalate.script] sprint.name: PMP Sprint 20
2021-06-24 20:53:05,944+0000 pool-153-thread-1 ERROR sburkhart 1162x553072x1 pfcu7y 71.184.147.4,172.17.20.137,127.0.0.1 /secure/ExalateIssueOperation.jspa [c.e.r.out.schedule.EventSchedulerService] Could not create a replica for issue 326 because of a script error
com.exalate.api.exception.script.ScriptException: Cannot set property ‘versions’ on null object
at com.exalate.error.services.ScriptExceptionCategoryService.categorizeProcessorAndIssueTrackerExceptionsIntoScriptExceptions(ScriptExceptionCategoryService.scala:38)
at com.exalate.processor.ExalateProcessor.executeProcessor(ExalateProcessor.java:57)
at com.exalate.processor.jira.JiraCreateReplicaProcessor.executeDataFilter(JiraCreateReplicaProcessor.java:312)
at com.exalate.processor.jira.JiraCreateReplicaProcessor.createHubReplica(JiraCreateReplicaProcessor.java:189)
at com.exalate.replication.out.schedule.EventSchedulerService$1.call(EventSchedulerService.java:166)
at com.exalate.replication.out.schedule.EventSchedulerService$1.call(EventSchedulerService.java:163)
at com.exalate.replication.out.schedule.EventSchedulerService.schedulePairEventInternal(EventSchedulerService.java:196)
at com.exalate.replication.out.schedule.EventSchedulerService.schedulePairEvent(EventSchedulerService.java:163)
at com.exalate.replication.out.schedule.EventSchedulerServiceLockAware.schedulePairEvent(EventSchedulerServiceLockAware.java:77)
at com.exalate.replication.out.eventhandler.IssueEventHandlerService.handleTriggers(IssueEventHandlerService.java:284)
at com.exalate.replication.out.eventhandler.IssueEventHandlerService.handleEvent(IssueEventHandlerService.java:176)
at com.exalate.replication.out.eventhandler.IssueEventHandlerService.handleSprintEvent(IssueEventHandlerService.java:155)
at com.exalate.replication.out.eventhandler.sprint.SprintEventHandlerTask.run(SprintEventHandlerTask.java:42)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: javax.script.ScriptException: javax.script.ScriptException: java.lang.NullPointerException: Cannot set property ‘versions’ on null object
at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:158)
at java.scripting/javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264)
at com.exalate.processor.ExalateProcessor.execute(ExalateProcessor.java:98)
at com.exalate.processor.ExalateProcessor.executeProcessor(ExalateProcessor.java:55)
… 17 more
at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:320)
at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:155)
… 20 more
Caused by: java.lang.NullPointerException: Cannot set property ‘versions’ on null object
at org.codehaus.groovy.runtime.NullObject.setProperty(NullObject.java:80)
at org.codehaus.groovy.runtime.InvokerHelper.setProperty(InvokerHelper.java:213)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.setProperty(ScriptBytecodeAdapter.java:496)
at Script10.run(Script10.groovy:46)
at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:317)

Eric Hilfer commented on 25 June 2021

Moving the versions and components line fixed the synch issue (but not the bulk exalate errors)

if(entityType == "sprint"){
    log.info("entityType: "+ entityType)
    log.info("sprint.originBoardId: "+  sprint.originBoardId)
    log.info("sprint.id: "+  sprint.id)
    log.info("sprint.name: "+  sprint.name)
}
if(entityType == "sprint" && sprint.originBoardId == "107" && (sprint.id == "321" || sprint.id == "326" )){
    //Only sync sprints on board 107 and sprints on or after sprint 19 (id >= 321)
    replica.name = sprint.name
    replica.goal = sprint.goal
    replica.state = sprint.state
    replica.startDate = sprint.startDate
    replica.endDate = sprint.endDate
    replica.originBoardId = sprint.originBoardId
}
if(entityType == "issue"){
   //Executed when syncing an issue to a remote side
   replica.summary = issue.summary
   replica.description = issue.description
   replica.project = issue.project
   replica.type = issue.type
    //....
    //other script rules to sync issues
    //sprint....
   replica.customFields.Sprint = issue.customFields.Sprint
   replica.key            = issue.key
   replica.type           = issue.type
   replica.assignee       = issue.assignee
   replica.reporter       = issue.reporter
   replica.summary        = issue.summary
   replica.description    = issue.description
   replica.labels         = issue.labels
   replica.comments       = issue.comments
   replica.resolution     = issue.resolution
   replica.status         = issue.status
   replica.parentId       = issue.parentId
   replica.priority       = issue.priority
   replica.attachments    = issue.attachments
   replica.project        = issue.project
   replica.customFields."Story Points" = issue.customFields."Story Points"
   SubTask.send()
   // Ensure that the Epic is synced first, such that stories sent later can be associated to the right epic
   Epic.sendEpicFirst()
   //Comment these lines out if you are interested in sending the full list of versions and components of the source project.
   replica.project.versions = []
   replica.project.components = []
}
/*
Custom Fields
replica.customFields."CF Name" = issue.customFields."CF Name"
*/

Answer by Eric Hilfer on 25 June 2021

I had to move the versions and components assignment lines into the if block for “issues” entityTypes instead of letting it get hit for “sprints” entityTypes, too.
When we bulk exalate the sprints trigger, it still throws a few hundred nullpointer exceptions, but the sprint did synch up this time.


Comments:

Francis Martens (Exalate) commented on 26 June 2021

On what side are you getting this nullpointer exception?

Eric Hilfer commented on 26 June 2021

On the Jira server outgoing side

Eric Hilfer commented on 26 June 2021

And the only way to clear those errors is to go directly into the database and truncate the errors table

Eric Hilfer commented on 26 June 2021

Stack trace and more info here: https://support.idalko.com/plugins/servlet/desk/portal/8/EASE-9432

Francis Martens (Exalate) commented on 27 June 2021

Hi Eric Hilfer
According to the ticket - the problem has been addressed - please confirm

Eric Hilfer commented on 27 June 2021

We were shown a work around to truncate the errors table so we are able to operate, but the problem of approximately 300 nullpointer execptions that get thrown when trying to bulk exalate the sprints trigger has not been resolved. We will just avoid trying to perform a bulk exalate.