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"
*/