Originally asked by Harold Oconitrillo on 09 November 2022 (original question)
I have some weird issues when trying to synchronize sprints between Jira Cloud instances. I followed up to your documentation and I receive error “Multiple entities touched on first sync”.
Likewise, I have tried to remove a conditional if(first sync), move it to a different place, but with no luck.
Whole configuration based on :https://docs.idalko.com/exalate/display/ED/How+to+sync+sprints+in+Jira+Cloud
/*
if(firstSync){
issue.projectKey = "SYNC"
// Set type name from source issue, if not found set a default
issue.typeName = nodeHelper.getIssueType(replica.type?.name, issue.projectKey)?.name ?: "Task"
}
*/
//entityType represent the type of the remote side entity
if(entityType == "sprint"){
//Executed when receiving a sprint sync from the remote side
def sprintMap = ["160":"25"] //[remoteBoardId: localBoardId]
sprint.name = 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 = "BMS"
issue.typeName = "Task"
}
issue.summary = replica.summary
issue.description = replica.description
issue.comments = commentHelper.mergeComments(issue, replica)
issue.attachments = attachmentHelper.mergeAttachments(issue, replica)
issue.labels = replica.labels
//....
//other script rules to sync issues
//....
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
}
}
}
if (replica.parentId) {
// look up the twin parent
def localParent = nodeHelper.getLocalIssueFromRemoteId(replica.parentId.toLong())
// if found, then set it, else don't create the subtask
if(localParent) {
issue.parentId = localParent.id
}
}
def defaultUser = nodeHelper.getUserByEmail("client@strix.app")
//issue.reporter = nodeHelper.getUserByEmail(replica.reporter?.email) ?: defaultUser
issue.assignee = nodeHelper.getUserByEmail(replica.assignee?.email) ?: defaultUser
/*
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")
issue.reporter = nodeHelper.getUserByEmail(replica.reporter?.email) ?: defaultUser
issue.assignee = nodeHelper.getUserByEmail(replica.assignee?.email) ?: defaultUser
*/
/*
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) }
*/
/*
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 = ["Open":"New", "To Do":"Backlog"]
def remoteStatusName = 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
*/
Comments:
Harold Oconitrillo commented on 10 November 2022
Hi,
Basically, all the rules related to issue entity should be in the ifblock for issue
/*
if(firstSync){
issue.projectKey = "SYNC"
// Set type name from source issue, if not found set a default
issue.typeName = nodeHelper.getIssueType(replica.type?.name, issue.projectKey)?.name ?: "Task"
}
*/
//entityType represent the type of the remote side entity
if(entityType == "sprint"){
//Executed when receiving a sprint sync from the remote side
def sprintMap = ["160":"25"] //[remoteBoardId: localBoardId]
sprint.name = 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 = "BMS"
issue.typeName = "Task"
}
issue.summary = replica.summary
issue.description = replica.description
issue.comments = commentHelper.mergeComments(issue, replica)
issue.attachments = attachmentHelper.mergeAttachments(issue, replica)
issue.labels = replica.labels
//....
//other script rules to sync issues
//....
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
}
}
if (replica.parentId) {
// look up the twin parent
def localParent = nodeHelper.getLocalIssueFromRemoteId(replica.parentId.toLong())
// if found, then set it, else don't create the subtask
if(localParent) {
issue.parentId = localParent.id
}
}
def defaultUser = nodeHelper.getUserByEmail("client@strix.app")
//issue.reporter = nodeHelper.getUserByEmail(replica.reporter?.email) ?: defaultUser
issue.assignee = nodeHelper.getUserByEmail(replica.assignee?.email) ?: defaultUser
/*
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")
issue.reporter = nodeHelper.getUserByEmail(replica.reporter?.email) ?: defaultUser
issue.assignee = nodeHelper.getUserByEmail(replica.assignee?.email) ?: defaultUser
*/
/*
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) }
*/
/*
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 = ["Open":"New", "To Do":"Backlog"]
def remoteStatusName = 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
*/
}
Feel free to contact us back for further questions you may have.
Kind regards.