Answer by Serhiy Onyshchenko on 03 May 2024
Hello there,
Here’s a script for
ZenDesk <> Jira
conflict handling
ZD Out
replica.key = issue.key
replica.assignee = issue.assignee
replica.reporter = issue.reporter
replica.summary = issue.summary
replica.description = issue.description
replica.type = issue.type
replica.labels = issue.labels
replica.attachments = issue.attachments
replica.comments = issue.comments
replica.status = issue.status
// Community 42839472: Avoid updating issue with older changes ZD <> *
def allAudits = []
def getPage = { String nextPageUrl ->
httpClient
// .http(
// "GET",
// "/api/v2/tickets/${ticket.id.toString()}/audits".toString(),
// )
.get(nextPageUrl)
}
boolean isLastPage = false
String nextPageUrlStr = "/api/v2/tickets/${ticket.id.toString()}/audits".toString()
while(!isLastPage){
def result = getPage(nextPageUrlStr)
if(result.audits && result.audits.size() > 0) {
allAudits.addAll(result.audits)
}
isLastPage = result.next_page == null
nextPageUrlStr = result.next_page
}
// we collected all audits in one list: allAudits
def noMsDateFormat = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")//"2021-10-05T12:33:10Z"
ticket.changeHistory = allAudits
.inject([] as List<com.exalate.basic.domain.hubobject.v1.BasicHubChangeHistory>) { _res, hJson ->
def author = new com.exalate.basic.domain.hubobject.v1.BasicHubUser()
author.key = hJson.author_id
author.active = true
// def _emailMatcher = hJson.revisedBy.name =~ /<([^@]+@[^>+])>/
// author.email= _emailMatcher.size() > 0 ? _emailMatcher.iterator().next()[1] : null
// author.displayName = hJson.revisedBy.displayName
// author.username = hJson.revisedBy.uniqueName
def date = ({ dStr ->
if (dStr == null) return null
try { noMsDateFormat.parse(dStr) }
catch (e1) { return null }
})(hJson.created_at)
def timestamp = date == null ? null : new java.sql.Timestamp(date.time)
def changeItems = hJson
.events
?.findAll { event -> ["Create", "Change"].any { it.equals(event.type) } }
?.inject([] as List<com.exalate.basic.domain.hubobject.v1.BasicHubChangeItem>) { r, v ->
def ci = new com.exalate.basic.domain.hubobject.v1.BasicHubChangeItem(
v.previous_value as String,
v.previous_value as String,
v.value as String,
v.value as String,
v.field_name,
("\\d+".matches(v.field_name) ?
"custom" :
"system")
)
r += ci
r
} ?: []
_res += new com.exalate.basic.domain.hubobject.v1.BasicHubChangeHistory(
hJson.id as Long,
author,
timestamp,
changeItems
)
_res
}
def fieldToLastUpdateDateFn = { exalateUserKey -> { history ->
history
.sort { c -> c.created.time }
.reverse()
.findAll { c ->
c.author.key != exalateUserKey
}
.inject([:]) { _result, c ->
c.changeItems.inject(_result) { r, i ->
String k = i.field
if (r[k] == null) {
r[k] = c.created
}
r
}
}
}}
replica.customKeys."fieldToLastUpdateDate" = fieldToLastUpdateDateFn("377510125653")(ticket.changeHistory)// END: Community 42839472: Avoid updating issue with older changes ZD <> *
Note, the changes made by Exalate proxy user are ignored:
fieldToLastUpdateDateFn("377510125653")(ticket.changeHistory)
377510125653 - the exalate proxy user's id in this Zendesk instance
ZD In
// Community 42839472: Avoid updating issue with older changes ZD <> *
def allAudits = []
def getPage = { String nextPageUrl ->
httpClient
// .http(
// "GET",
// "/api/v2/tickets/${ticket.id.toString()}/audits".toString(),
// )
.get(nextPageUrl)
}
boolean isLastPage = false
String nextPageUrlStr = "/api/v2/tickets/${ticket.id.toString()}/audits".toString()
while(!isLastPage){
def result = getPage(nextPageUrlStr)
if(result.audits && result.audits.size() > 0) {
allAudits.addAll(result.audits)
}
isLastPage = result.next_page == null
nextPageUrlStr = result.next_page
}
// we collected all audits in one list: allAudits
def noMsDateFormat = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")//"2021-10-05T12:33:10Z"
ticket.changeHistory = allAudits
.inject([] as List<com.exalate.basic.domain.hubobject.v1.BasicHubChangeHistory>) { _res, hJson ->
def author = new com.exalate.basic.domain.hubobject.v1.BasicHubUser()
author.key = hJson.author_id
author.active = true
// def _emailMatcher = hJson.revisedBy.name =~ /<([^@]+@[^>+])>/
// author.email= _emailMatcher.size() > 0 ? _emailMatcher.iterator().next()[1] : null
// author.displayName = hJson.revisedBy.displayName
// author.username = hJson.revisedBy.uniqueName
def date = ({ dStr ->
if (dStr == null) return null
try { noMsDateFormat.parse(dStr) }
catch (e1) { return null }
})(hJson.created_at)
def timestamp = date == null ? null : new java.sql.Timestamp(date.time)
def changeItems = hJson
.events
?.findAll { event -> ["Create", "Change"].any { it.equals(event.type) } }
?.inject([] as List<com.exalate.basic.domain.hubobject.v1.BasicHubChangeItem>) { r, v ->
def ci = new com.exalate.basic.domain.hubobject.v1.BasicHubChangeItem(
v.previous_value as String,
v.previous_value as String,
v.value as String,
v.value as String,
v.field_name,
("\\d+".matches(v.field_name) ?
"custom" :
"system")
)
r += ci
r
} ?: []
_res += new com.exalate.basic.domain.hubobject.v1.BasicHubChangeHistory(
hJson.id as Long,
author,
timestamp,
changeItems
)
_res
}
def fieldToLastUpdateDateFn = { exalateUserKey -> { history ->
history
.sort { c -> c.created.time }
.reverse()
.findAll { c ->
c.author.key != exalateUserKey
}
.inject([:]) { _result, c ->
c.changeItems.inject(_result) { r, i ->
String k = i.field
if (r[k] == null) {
r[k] = c.created
}
r
}
}
}}
def localFieldToLastUpdateDate = fieldToLastUpdateDateFn("377510125653")(ticket.changeHistory)
def remoteFieldToLastUpdateDate = replica.customKeys."fieldToLastUpdateDate"
remoteFieldToLastUpdateDate = remoteFieldToLastUpdateDate.inject([:]) { r, k, v ->
r[k] = new Date((v) as Long)
r
}
//issue.labels = replica.labels
if (firstSync || (remoteFieldToLastUpdateDate."summary" > localFieldToLastUpdateDate."subject")) {
log.error("#conflict_handling: summary changed more recently on remote: ${remoteFieldToLastUpdateDate.summary} is MORE recent then ${localFieldToLastUpdateDate."subject"}".toString())
issue.summary = replica.summary
} else {
log.error("#conflict_handling: summary NOT changed more recently on remote: ${remoteFieldToLastUpdateDate.summary} is LESS recent then ${localFieldToLastUpdateDate."subject"}".toString())
}
if(firstSync) {
issue.description = replica.description ?: "No description."
}
if (remoteFieldToLastUpdateDate."status" > localFieldToLastUpdateDate."status") {
def statusMapping = [
"To Do" : "new",
"In Progress" : "open",
"Pending on customer" : "pending",
"Waiting for support" : "hold",
"Done" : "solved",
"Cancelled" : "solved",
"Completed" : "solved"
]
def localStatus = statusMapping
.find { k, v -> k.equalsIgnoreCase(replica.status.name) }
?.value
log.error("#conflict_handling: status changed more recently on remote: ${remoteFieldToLastUpdateDate.status} is MORE recent then ${localFieldToLastUpdateDate.status}".toString())
log.error("#in found local status $localStatus in mapping $statusMapping for remote status ${replica.status.name}".toString())
if (localStatus) {
ticket.setStatus(localStatus)
}
} else {
log.error("#conflict_handling: status NOT changed more recently on remote: ${remoteFieldToLastUpdateDate.status} is LESS recent then ${localFieldToLastUpdateDate.status}".toString())
}
// END: Community 42839472: Avoid updating issue with older changes ZD <> *
issue.attachments = attachmentHelper.mergeAttachments(issue, replica)
issue.comments += replica.addedComments
Note, the changes made by Exalate proxy user are ignored:
fieldToLastUpdateDateFn("377510125653")(ticket.changeHistory)
377510125653 - the exalate proxy user's id in this Zendesk instance
Jira Out:
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.project.versions = []
replica.project.components = []// Community 42839472: Avoid updating issue with older changes Jira <> *def fieldToLastUpdateDateFn = { exalateUserKey -> { history ->
history
.sort { c -> c.created.time }
.reverse()
.findAll { c ->
c.author.key != exalateUserKey
}
.inject([:]) { _result, c ->
c.changeItems.inject(_result) { r, i ->
String k = i.field
if (r[k] == null) {
r[k] = c.created
}
r
}
}
}}
replica.customKeys."fieldToLastUpdateDate" = fieldToLastUpdateDateFn("557058:c020323a-70e4-4c07-9ccc-3ad89b1c02ec")(issue.changeHistory)// END: Community 42839472: Avoid updating issue with older changes Jira <> *
Note, that changes made by Exalate proxy user in Jira Cloud are ignored:
fieldToLastUpdateDateFn("557058:c020323a-70e4-4c07-9ccc-3ad89b1c02ec")(issue.changeHistory)
557058:c020323a-70e4-4c07-9ccc-3ad89b1c02ec - is the same for any Jira Cloud
Jira In:
if (firstSync) {
issue.projectKey = "AA"
// Set the same issue type as the source issue. If not found, set a default.
issue.typeName = nodeHelper.getIssueType(replica.type?.name, issue.projectKey)?.name ?: "Task"
}
// Community 42839472: Avoid updating issue with older changes Jira <> *
def remoteFieldToLastUpdateDate = replica.customKeys."fieldToLastUpdateDate"
remoteFieldToLastUpdateDate = remoteFieldToLastUpdateDate.inject([:]) { r, k, v ->
r[k] = new Date((v) as Long)
r
}
def fieldToLastUpdateDateFn = { exalateUserKey -> { history ->
history
.sort { c -> c.created.time }
.reverse()
.findAll { c ->
c.author.key != exalateUserKey
}
.inject([:]) { _result, c ->
c.changeItems.inject(_result) { r, i ->
String k = i.field
if (r[k] == null) {
r[k] = c.created
}
r
}
}
}}
def localFieldToLastUpdateDate = fieldToLastUpdateDateFn("557058:c020323a-70e4-4c07-9ccc-3ad89b1c02ec")(issue.changeHistory)
if (firstSync || (remoteFieldToLastUpdateDate."subject" > localFieldToLastUpdateDate.summary)) {
log.error("#conflict_handling: summary changed more recently on remote: ${remoteFieldToLastUpdateDate."subject"} is MORE recent then ${localFieldToLastUpdateDate.summary}".toString())
issue.summary = replica.summary
} else {
log.error("#conflict_handling: summary NOT changed more recently on remote: ${remoteFieldToLastUpdateDate."subject"} is LESS recent then ${localFieldToLastUpdateDate.summary}".toString())
}
if(firstSync || (remoteFieldToLastUpdateDate."description" > localFieldToLastUpdateDate."description")) {
log.error("#conflict_handling: description changed more recently on remote: ${remoteFieldToLastUpdateDate.description} is MORE recent then ${localFieldToLastUpdateDate.description}".toString())
issue.description = replica.description
} else {
log.error("#conflict_handling: description NOT changed more recently on remote: ${remoteFieldToLastUpdateDate.description} is LESS recent then ${localFieldToLastUpdateDate.description}".toString())
}
if (remoteFieldToLastUpdateDate."status" > localFieldToLastUpdateDate."status") {
def statusMapping = [
"new" : "To Do",
"open" : "In Progress",
"pending" : "Pending on customer",
"on-hold" : "Waiting for support",
"solved" : "Completed"
]
def localStatus = statusMapping
.find { k, v -> k.equalsIgnoreCase(replica.status.name) }
?.value
log.error("#conflict_handling: status changed more recently on remote: ${remoteFieldToLastUpdateDate.status} is MORE recent then ${localFieldToLastUpdateDate.status}".toString())
log.error("#in found local status $localStatus in mapping $statusMapping for remote status ${replica.status.name}".toString())
if (localStatus) {
ticket.setStatus(localStatus)
}
} else {
log.error("#conflict_handling: status NOT changed more recently on remote: ${remoteFieldToLastUpdateDate.status} is LESS recent then ${localFieldToLastUpdateDate.status}".toString())
}
// END: Community 42839472: Avoid updating issue with older changes Jira <> *
issue.comments = commentHelper.mergeComments(issue, replica)
issue.attachments = attachmentHelper.mergeAttachments(issue, replica)
Note, that changes made by Exalate proxy user in Jira Cloud are ignored:
fieldToLastUpdateDateFn("557058:c020323a-70e4-4c07-9ccc-3ad89b1c02ec")(issue.changeHistory)
557058:c020323a-70e4-4c07-9ccc-3ad89b1c02ec - is the same for any Jira Cloud
Happy Exalating!
Serhiy