Update a local (sync source) custom field from within the outgoing sync processor

Originally asked by Glenn Bullingham on 18 January 2021 (original question)


Hi.

We’re using Exalate to synchronise issues across our cloud based instances of Jira and GitHub.

Is it possible to set the value of an issue variable field (i.e. input side) from within the outgoing sync processor during sychronisation? That is to say, update a source-side field during the course of a source-side initiated sync?

I’ve attempted this within one of our of outgoing scripts and whilst it doesn’t lead to an error, or affect synchronisation, neither does the source side field get updated. I’m left wondering whether this simply isn’t possible or if there’s a particular way of doing it other than a simple assignment.

For example:

// **outgoing sync processor script**

// usual mapping of source to destination(replica) fields in an outgoing script
replica.description = issue.description
replica.labels = issue.labels
replica.comments = issue.comments

// but in addition, attempt to update a field of the source issue during the same outgoing process
issue.customFields.“CF Name” = <insert appropriate value for custom field>

This question is related to one previously asked here but in which the matter of whether this action is fundamentally possible or not, rather than being the best approach to that particular use-case, didn’t get answered (imho).

Update local field when local issue execute sync (old community)

Thanks,
Glenn


Answer by Jonathon Irwin on 22 February 2021

Actually looking for something similar (going to be posting the question soon) regarding last sync time. But for the question of modifying the issue during an outgoing sync, the server method I’ve used previously is:

import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.Issue
def issueMgr = ComponentAccessor.getIssueManager()
def customFieldManager = ComponentAccessor.getCustomFieldManager()
Issue curIssue = issueMgr.getIssueObject(issue.id as Long)
def cf = customFieldManager.getCustomFieldObjectsByName("WhateverYourFieldIsNamed")[0]
def changeHolder = new DefaultIssueChangeHolder()
curIssue.setCustomFieldValue(cf,valueOfFieldYouWant)
cf.updateValue(null, curIssue, new ModifiedValue(curIssue.getCustomFieldValue(cf), currentTime),changeHolder)

And for cloud you use a REST call as below:

def url = "yourcloudURL.atlassian.net"
def fullUrl = url + '/rest/api/3/issue/'+issue.key
def userAuth = "Basic XXXXXX" //replace with user auth string for user you want to act. Easiest way I've found to get this is in Postman enter username and password into basic authentication then check headers for Authorization's value.

def message = '{"fields": {"customfield_XXXX":"'+currentTime+'"}}' //replace the Xs with the customfield number (found by hovering over the edit button in Custom Fields settings)
def get = new URL(fullUrl).openConnection();
get.setRequestMethod("PUT")
get.setDoOutput(true)
get.setRequestProperty("Authorization",userAuth)
get.setRequestProperty("Content-Type","application/json")
get.setRequestProperty("X-ExperimentalApi","opt-in") //needed for some, but not this, endpoint
get.setRequestProperty("Accept","application/json")
get.getOutputStream().write(message.getBytes("UTF-8"));
def getRC = get.getResponseCode(); // the response code from the REST call
if (getRC.equals(204)) { //success
	return true
}

There is risk that it might just trigger another sync, but it didn’t seem to constantly do so when I was trying it (maybe it knows to ignore that type of change?)


Comments:

Francis Martens (Exalate) commented on 24 February 2021

Updating the local issue in an outgoing sync is currently not supported for the reason you indicated.
We can see the value of having it in the product.

Answer by Yaakov Shamii on 21 January 2021

Did you try to use this instead:

issue.customFields.“CF Name”?.value = *** your value ***

Anyway if that doesn’t work, you can using Atlassian API (CustomFieldManager and IssueManager) to do so.


Comments:

Glenn Bullingham commented on 01 February 2021

Hello Yaakov,

Thank you for your reply. I’ve been able to revisit this task today and can confirm that the approach I might expect to work, that is to say, the first example you gave (i.e. issue.customFields.“CF Name”?.value = *** your value ***) does not work. As with my own previous attempts, whilst it does not lead to an exception or an error being raised during synchronisation, neither does it cause the target custom field to be updated. I able able the read the same custom field’s value elsewhere within the same outgoing sync script, it just seems that it cannot be written.

Please would you mind directing me towards either documentation on, or an example of the use of, CustomFieldManager etc. within the outgoing sync script, as per your alternative suggestion? I found this pertaining to Jira on-premise but I can’t even update the script by adding just the initial import (import com.atlassian.jira.component.ComponentAccessor) without receiving a “Cannot publish changes” error.

Thanks,

Glenn

Yaakov Shamii commented on 03 February 2021

Hi Glenn,

Can you post the error you receive?

Glenn Bullingham commented on 03 February 2021

Sure, no problem.

Glenn Bullingham commented on 10 February 2021

Sorry to pester but does anyone have an answer or solution for this please? If this isn’t the correct way to go about the task in hand, that’s fine. But I would really like to know how to identify, from the Jira side using a JQL query (so that it can be used within boards, gadgets etc.), which side of the synchronization occurred last. i.e. I’d like an easy way to identify at any point in time all the issues under synchronisation which were last updated by a user in GitHub, rather than directly in Jira.

Thanks,

Glenn

Yaakov Shamii commented on 23 February 2021

Glenn, I believe you have another error in your data filter rather than the import (as they seem correct, and I used them before).

Jonathon’s answer seem legit, and if you don’t want to trigger the sync again you can update a custom field without history (but it won’t send update mails to the customer in that case).

If you still encounter problems, please share the logs with us, I couldn’t understand from the picture what is the actual problem.

Goodluck

Glenn Bullingham commented on 23 February 2021

Thank you both for your input. However there must be something fundamental that I am missing. Whilst I appreciate that my scripting skills aren’t infallible, I currently have a fully functional outgoing script set on my Jira cloud connector. If I so much as add just the opening line from Jonathon’s example to it at line 1, i.e. the first import statement

import com.atlassian.jira.component.ComponentAccessor;

,then I’m unable to apply the change to the Data Filter script, as per the screenshot above. Should I be able to do this? That is, is this functionality available in the Jira Cloud version, or do you have it working only with Jira Server on-premise?

Yaakov Shamii commented on 23 February 2021

As far as I know, Atlassian’s javadoc is not compatible in the cloud instance, so you can’t use ComponentAccessor or managers and so on. Please use Jira’s rest api to update the origin issue custom fields.

Glenn Bullingham commented on 23 February 2021

Yaakov Shamii Thank you. I’d completely missed Jonathon’s delineation of using REST for cloud use-cases.