Unable to Sync Comments From RITMs to and from custom field

Hi there,

While comments are flying nicely between ServiceNow INCs and JIRA, the same can not be said of the comments from our RITMs. We have a custom field for our IT Work Notes, a Journal Entry field named u_journal_2. No matter what I try it seems to throw up an error of some kind, either a type mismatch (String instead of List) or, and currently, No such property: issue for class: Script355.

I have followed the guide on " How to Sync Comments in Jira Cloud", trying replacing the “replica.comments” to “replica.u_journal_2” but it either doesn’t work or there is an error.

I feel like I have tried everything but it might be that I am not understanding what it is trying to do properly. Any help would be much appreciated!

Hi Iain,

I manage to sync comments from RITM to JSM using a journal field named “u_journal_2” and this is how I did it.

The Incoming sync from Jcloud was left as is
issue.comments = commentHelper.mergeComments(issue, replica)

and for the Outgoing script on SNOW inside the table for RITM I used
replica.u_journal_2 = requestItem.u_journal_2

So my RITM outgoing sync looks something like this

if(entity.tableName == "sc_req_item") {
  replica.summary = requestItem.short_description
  replica.description = requestItem.description
  replica.comments = requestItem.comments
  replica.attachments = requestItem.attachments
  // journal field
  replica.u_journal_2 = requestItem.u_journal_2
}

let me know if this works for you too.

Hi there Kevin,

Sadly that didn’t seem to work for me. I left the Incoming Sync on Jira as is and added the new line into the Outgoing Sync on ServiceNow as you mentioned. The sync does stay active on both sides and doesn’t throw up an error but when I try to add a new comment into the u_journal_2 field, it doesn’t appear in the ticket on Jira. However, updates to the Summary happen without an issue.

I noticed you used “requestItem” before the field names on all of the variables. I have the others as “entity” but that seems to be working perfectly. I am assuming that wouldn’t be the issue?

Also, I was looking for this to happen from Jira to ServiceNow as well. I tried throwing some mud at the wall to see if it would stick with:

issue.u_journal_2        = commentHelper.addComment(replica.comments, issue.u_journal_2)

but I am getting the error “No such property: issue for class: Script647”. Are you able to help with this too?

Hi iain,

Could you please share the outgoing script from SNOW and the incoming from Jcloud that are currently using?

I will work on syncing from Jcloud to SNOW too and share the script for both :slight_smile:

Hi again Kevin,

Can do indeed!

Outgoing Sync from SNow:

if (entity.tableName == "incident") {
replica.key            = entity.key
replica.summary        = "SN Incident - " + entity.short_description
replica.description    = "*Caller Name:* " + entity.caller_id.display_value + "\r*SN Priority:* " + entity.priorityValue.name + "\r\r-------------------------- [update below line]\r" + entity.description
replica.attachments    = entity.attachments
replica.comments       = entity.comments
replica.state          = entity.state
replica.priority       = entity.priorityValue

//if(!(entity.assigned_to instanceof String)){
//replica.assigned_to = entity.assigned_to.display_value 
//}
}

if (entity.tableName == "sc_req_item") {
replica.key            = entity.key
replica.summary        = "SN Request (" + entity.cat_item.display_value + ") - " + entity.short_description
replica.description    = "*Requested For:* " + entity.requested_for.name + "\r\r*For more information on this ticket please see:*\r \r[https://hestest.service-now.com/now/nav/ui/classic/params/target/sc_req_item.do%3Fsys_id%3D" + entity.sys_id + "|https://hestest.service-now.com/now/nav/ui/classic/params/target/sc_req_item.do%3Fsys_id%3D" + entity.sys_id + "|smart-link]"
replica.attachments    = entity.attachments
replica.u_journal_2    = requestItem.u_journal_2
replica.comments = requestItem.comments

Incoming Sync from JIRA:

if (firstSync) {
issue.projectKey  = "SNOW"
// 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 ?: "ServiceNow"
issue.description = replica.description
}

issue.summary      = replica.summary
issue.comments     = commentHelper.mergeComments(issue, replica)
issue.attachments  = attachmentHelper.mergeAttachments(issue, replica)

if (!issue.summary.startsWith("SN Request")) {
  issue.description  = replica.description

  if (replica.state == "Resolved" || replica.state == "Closed" || replica.state == "Canceled") {
def statusMap = [
    //"New"   : "In Progress",
    //"Reviewed (Service Desk only)" : "In Progress",
    //"In Progress" : "In Progress",
    "Resolved"  : "Done",
    "Closed"  : "Done",
    "Canceled"  : "Done",
  ]
def remoteStatusName = replica.state
issue.setStatus(statusMap[remoteStatusName] ?: "In Progress")
  }

  def priorityMapping = [
    // remote side priority <-> local side priority             
      "1 - Critical" : "Highest",
      "2 - High" : "High",
      "3 - Moderate" : "Medium",
      "4 - Low" : "Low"
]
  def priorityName = priorityMapping[replica.priority?.name] ?: "Low" // set default priority in case the proper urgency could not be found
  issue.priority = nodeHelper.getPriority(priorityName)

} else {
  def statusMap = [
    "New"   : "In Progress",
    "Triage (Service Desk Only)" : "In Progress",
    "Pending" : "In Progress",
    "Work In Progress" : "In Progress",
    "Closed Complete"  : "Done",
    "Closed Incomplete" : "Done",
  ]
  def remoteStatusName = replica.state
  issue.setStatus(statusMap[remoteStatusName] ?: "In Progress")
  issue.summary      = replica.summary

}

On the SNOW outgoing you are using:
replica.u_journal_2 = requestItem.u_journal_2
replica.comments = requestItem.comments

And that means that you are populating the standard comments object in exalate, and also a custom object called replica.u_journal_2, but if you look at the incoming on Jira, I do not think you are utilizing the u_journal_2 field at all (or am I missing something).

Thanks
Majid

Hi there Majid,

Ah yes! This must have been from a time I had to remove it as it was breaking the rest of the script and I didn’t notice I had not put it back in. This is what I have currently:

Outgoing Sync from SNow:

if (entity.tableName == "incident") {
    replica.key            = entity.key
    replica.summary        = "SN Incident - " + entity.short_description
    replica.description    = "*Caller Name:* " + entity.caller_id.display_value + "\n*SN Priority:* " + entity.priorityValue.name + "\n\n-------------------------- [update below line]\n" + entity.description
    replica.attachments    = entity.attachments
    replica.comments       = entity.comments
    replica.state          = entity.state
    replica.priority       = entity.priorityValue

    //if(!(entity.assigned_to instanceof String)){
    //replica.assigned_to = entity.assigned_to.display_value 
    //}
}

// If the synced ticket is an Request, send the following fields with updated Short Description
if (entity.tableName == "sc_req_item") {
    
    replica.key            = entity.key
    replica.summary        = "SN Request (" + entity.cat_item.display_value + ") - " + entity.short_description
    replica.description    = "*Requested For:* " + entity.requested_for.name + "\r\r*For more information on this ticket please see:*\r \r[https://hestest.service-now.com/now/nav/ui/classic/params/target/sc_req_item.do%3Fsys_id%3D" + entity.sys_id + "|https://hestest.service-now.com/now/nav/ui/classic/params/target/sc_req_item.do%3Fsys_id%3D" + entity.sys_id + "|smart-link]"
replica.attachments    = entity.attachments
replica.customFields.u_journal_2       = entity.customFields.u_journal_2

Incoming Sync from JIRA:

if (firstSync) {
    issue.projectKey  = "SNOW"
    // 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 ?: "ServiceNow"
    
    // The Description sync lives in "firstSync" as, if it is a RITM, this will be the only time it syncs
    issue.description = replica.description
}

issue.summary      = replica.summary
issue.comments     = commentHelper.mergeComments(issue, replica)
issue.attachments  = attachmentHelper.mergeAttachments(issue, replica)

// Append content from 'u_journal_2' to the comments
if (replica.customFields?.u_journal_2) {
    issue.comments += "\n" + replica.customFields.u_journal_2
}

// If the synced ticket comes from an Incident
if (!issue.summary.startsWith("SN Request")) {
  issue.description  = replica.description

  // Unless ServiceNow ticket has been closed, leave State alone. If it is, then close JIRA ticket
  if (replica.state == "Resolved" || replica.state == "Closed" || replica.state == "Canceled") {
    def statusMap = [
        "Resolved"  : "Done",
        "Closed"  : "Done",
        "Canceled"  : "Done",
      ]
    def remoteStatusName = replica.state
    issue.setStatus(statusMap[remoteStatusName] ?: "In Progress")
  }

  // Updates Priority from ServiceNow ticket
  def priorityMapping = [
        // remote side priority <-> local side priority             
          "1 - Critical" : "Highest",
          "2 - High" : "High",
          "3 - Moderate" : "Medium",
          "4 - Low" : "Low"
    ]
      def priorityName = priorityMapping[replica.priority?.name] ?: "Low" // set default priority in case the proper urgency could not be found
      issue.priority = nodeHelper.getPriority(priorityName)

// If ticket is a Request Item
} else {
    // Unless ServiceNow ticket has been closed, leave State alone. If it is, then close JIRA ticket
    if (replica.state == "Closed Complete" || replica.state == "Closed Incomplete") {
      def statusMap = [
        "Closed Complete"  : "Done",
        "Closed Incomplete" : "Done",
      ]
      def remoteStatusName = replica.state
      issue.setStatus(statusMap[remoteStatusName] ?: "In Progress")
      issue.summary      = replica.summary
    }

}

Currently this solution does sync/stay active but doesn’t put the information into the JIRA ticket. Also, the other way around (JIRA to ServiceNow) is probably more important. I can give the script for that too if/when needed.

I would like to see the payload (Remote Replica) as you see it on the Jira side please to ensure that the contents of the journal custom field are actually coming over.
If they are, I think the following line
issue.comments += "\n" + replica.customFields.u_journal_2

would need to change to something like this:
issue.comments = commentHelper.addComment(replica.customFields.u_journal_2, false, issue.comments)

Thanks
Majid

Hi there,

So the Remote Replica on the JIRA side looks like this:

{
  "version": {
    "major": 1,
    "minor": 14,
    "patch": 0
  },
  "hubIssue": {
    "voters": [],
    "fixVersions": [],
    "internalMap": {},
    "labels": [],
    "customKeys": {},
    "entityProperties": {},
    "components": [],
    "attachments": [],
    "customFields": {},
    "eventTriggerContext": {},
    "description": "*Requested For:* null\r\r*For more information on this ticket please see:*\r \r[https://hestest.service-now.com/now/nav/ui/classic/params/target/sc_req_item.do%3Fsys_id%3Df766e0bcfb58621090abf92abeefdc4a|https://hestest.service-now.com/now/nav/ui/classic/params/target/sc_req_item.do%3Fsys_id%3Df766e0bcfb58621090abf92abeefdc4a|smart-link]",
    "watchers": [],
    "key": "RITM0064072",
    "summary": "SN Request (WeTransfer Access) - Access to PIC Trinity House Hi-Res Pictures",
    "comments": [],
    "workLogs": [],
    "affectedVersions": []
  },
  "issueUrl": "https://hestest.service-now.com//nav_to.do?uri=sc_req_item.do?sys_id=f766e0bcfb58621090abf92abeefdc4a"
}

I suspect the information isn’t coming over sadly.

Interesting, so there is no u_journal_2 field. Is it populated in the RITM?

Also, I would change the following in SNOW outgoing:
replica.customFields.u_journal_2 = entity.customFields.u_journal_2
to
replica.u_journal_2 = entity.u_journal_2

Ok, that’s looking a lot more healthy now I have changed that line:

{
  "version": {
    "major": 1,
    "minor": 14,
    "patch": 0
  },
  "hubIssue": {
    "u_journal_2": "01/05/2025 11:13 - Iain Dewar (IT Work Notes)\nOne more time!\n\n01/05/2025 11:07 - Iain Dewar (IT Work Notes)\nLets go with some more work notes!\n\n16/04/2025 10:01 - Iain Dewar (IT Work Notes)\nWork notes?\n\n",
    "voters": [],
    "fixVersions": [],
    "internalMap": {
      "u_journal_2": "01/05/2025 11:13 - Iain Dewar (IT Work Notes)\nOne more time!\n\n01/05/2025 11:07 - Iain Dewar (IT Work Notes)\nLets go with some more work notes!\n\n16/04/2025 10:01 - Iain Dewar (IT Work Notes)\nWork notes?\n\n"
    },
    "labels": [],
    "customKeys": {},
    "entityProperties": {},
    "components": [],
    "attachments": [],
    "customFields": {},
    "eventTriggerContext": {},
    "description": "*Requested For:* null\r\r*For more information on this ticket please see:*\r \r[https://hestest.service-now.com/now/nav/ui/classic/params/target/sc_req_item.do%3Fsys_id%3Df766e0bcfb58621090abf92abeefdc4a|https://hestest.service-now.com/now/nav/ui/classic/params/target/sc_req_item.do%3Fsys_id%3Df766e0bcfb58621090abf92abeefdc4a|smart-link]",
    "watchers": [],
    "key": "RITM0064072",
    "summary": "SN Request (WeTransfer Access) - Access to PIC Trinity House Hi-Res Pictures 3",
    "comments": [],
    "workLogs": [],
    "affectedVersions": []
  },
  "issueUrl": "https://hestest.service-now.com//nav_to.do?uri=sc_req_item.do?sys_id=f766e0bcfb58621090abf92abeefdc4a"
}

I did also change the line you mentioned (using commentHelper) but while the information is going over, it doesn’t seem to be appearing in the Comment box.

Just to say… I realised that I didn’t remove the “customFields.” from:

issue.comments = commentHelper.addComment(replica.customFields.u_journal_2, false, issue.comments)

…and that has started putting the comments into the ticket. It’s ALL the comments but it’s a start. Guess I just need to put them all into an array and just show the first comment.

Better, so we do see the payload now.

Now that you have changed the replica structure on source side, you will need to do the same on destination i.e. change
issue.comments = commentHelper.addComment(replica.customFields.u_journal_2, false, issue.comments)
to
issue.comments = commentHelper.addComment(replica.u_journal_2, false, issue.comments)

p.s.: I see you were faster than me! Yes, that should be the issue indeed

So for the “all the comments” thing,

  • you want to send comments and the custom journal field both?
  • I would pick the 0th element of the array on SNOW side and just send that over.

Hope it helps!

Thanks

What I meant was that each time it sent all previous IT Work Notes along with the new one :smiley:

But yeah, looking at the information it is sending, I should be able to isolate the most recent comment and just post that (the last IT work note ends in \n\n even when you add new lines to the Work Note where it looks like this \r\n\r\n). I should be able to figure this out now when it comes to moving the IT Work Notes from ServiceNow to Jira.

Now we need to go the other way which seems to be more tricky. I have put the following information for this.

Outgoing Sync from JIRA: (It looks like this as it wasn’t telling us who was making the comments when it got it ServiceNow. This works perfectly in INCs)

replica.comments = issue.comments.collect { comment ->
    comment.body = 'Comment added by ' + comment.author.displayName + '\n\n' + comment.body
    return comment
}

Incoming Sync from ServiceNow: (this is what we came up with to fix the type mismatch but doesn’t work)

entity."u_journal_2" = commentHelper.addComment(replica.comments.take(1).toString(), [entity."u_journal_2"])

Let me know if you could give me any insight into this one.

This I would need to set up and test to provide an accurate answer, but here are a couple of observations:

  • The manipulation you are doing in Outgoing Sync from JIRA, I would do this in the Incoming in SNOW. You have the author info available as part of the comment object received in SNOW, so this would be easier done there I think – but minor detail, not important.
  • About the journal field, I would try:
    entity.u_journal_2 = "A test string" – does this work?
    If it works, then
    entity.u_journal_2 = replica.comments,last().body
    and see how it goes.

Hope it helps!

Thanks
Majid

That worked out great and now the comments from one side are now appearing in the other.

Sadly, we have gone from one extreme to the other however. Instead of never appearing, the last comment is now being added every time there is any update to the JIRA ticket even if there is no new comment.

Weirdly I have had a lot of luck with this if statement when the connection is to an Incident ticket:

if (replica.addedComments == null || replica.addedComments.isEmpty())

However, when I try to use this with a record on the Request Item table, it doesn’t work. There is no difference to the Outgoing Sync from JIRA so I don’t know why there would be a difference when it comes to the Incoming sync. I have tried using:

if (replica.addedComments != null && !replica.addedComments.isEmpty()) { entity."u_journal_2" = replica.comments.last().body }

to only add a post when there has been an added comment but it just does it anyway if there is a comment or not.

Any thoughts would be most appreciated.

Your script should actually work already, but I might be missing something. Maybe check on size:

if (!firstSync && replica.comments.size() != previous.comments.size()) {
entity.“u_journal_2” = replica.comments.last().body
}