Use ScriptRunner to modify a custom field when Rank is changed

Nathan Nantais January 19, 2016

For our backlog prioritization meetings I want to color-highlight issues in our scrum board which have not yet been manually ranked. My idea is to create a custom field (single choice, yes or no) indicating whether or not the Rank has been changed since the issue was created.

The custom field would default to 'No' on issue creation. I want to use a ScriptRunner listener on all issue updates to set the custom field to 'Yes' if and only if the Rank was changed in the update.

I cannot find any ScriptRunner examples to (1) check whether the Rank was changed in the issue update and (2) update my custom field.

Thank you,
Nathan 

p.s. I would be receptive to other suggestions if you think I'm going about this the wrong way.

2 answers

1 accepted

2 votes
Answer accepted
Thanos Batagiannis _Adaptavist_
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
January 19, 2016

Hi Nathan

During a listener event you can determine which field has changed like 

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder

Issue issue = event.issue
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def change = event?.getChangeLog()?.getRelated("ChildChangeItem").find {it.field == "Name of field"}
if (change) {
    def tgtField = customFieldManager.getCustomFieldObjects(issue).find {it.name == "My custom field"}
    def changeHolder = new DefaultIssueChangeHolder()
    tgtField.updateValue(null, issue, new ModifiedValue(issue.getCustomFieldValue(tgtField, "new value"), changeHolder)
} else
    return

Try the above and let me know if this works for you.

Nathan Nantais January 19, 2016

Thank you. Do you recommend doing this as a "Custom listener" or a "Fires an event when condition is true", or other?

Thanos Batagiannis _Adaptavist_
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
January 19, 2016

The above is for a custom listener, I just added like this in order to see how you can retrieve the changed values. But yes in your case you can use the fires an event when condition is true. There are examples and in particular see the priority has changed. And then set the custom field value.

Thanos Batagiannis _Adaptavist_
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
January 19, 2016

For the above script if you associate an Issue Updated event, then every time someone updates the rank the custom field will be updated as well. The custom field will not get updated during the issue create event, this means that unless someone edits the rank, after the creation, the custom field will hold always the initial value. Hope I was clear enough.

Jeremy Gaudet
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
January 19, 2016

Additionally, if "Rank" is available on a transition screen, and that transition emits a different type of event, you'll have to include those as well.

Thanos Batagiannis _Adaptavist_
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
January 19, 2016

Yes that's true, thanks for mentioning that Jeremy. 

Nathan Nantais January 19, 2016

Thank you, I got it working. Here is my final code:

 

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import org.ofbiz.core.entity.GenericValue;
import org.ofbiz.core.entity.GenericEntityException;
import com.atlassian.jira.issue.fields.config.FieldConfig 
import com.atlassian.jira.issue.customfields.option.Options


log.setLevel(org.apache.log4j.Level.INFO)
log.info("start");

Issue issue = event.issue

// Log changes
List<GenericValue> changeItems = null;

try {
    GenericValue changeLog = event.getChangeLog();
    changeItems = changeLog.getRelated("ChildChangeItem"); // changeLog.internalDelegator.findByAnd("ChangeItem", EasyMap.build("group",changeLog.get("id")));
} catch (GenericEntityException e){
    log.error(e.getMessage());
}

log.info("number of changes: " + changeItems.size());
for (Iterator<GenericValue> iterator = changeItems.iterator(); iterator.hasNext();){
    GenericValue changetemp = (GenericValue) iterator.next();
    String field = changetemp.getString("field");
    String oldstring = changetemp.getString("oldstring");
    String newstring = changetemp.getString("newstring");
    StringBuilder fullstring = new StringBuilder();
    fullstring.append("Issue ");
    fullstring.append(issue.getKey());
    fullstring.append(" field ");
    fullstring.append(field);
    fullstring.append(" has been updated from ");
    fullstring.append(oldstring);
    fullstring.append(" to ");
    fullstring.append(newstring);
    log.info("changes " + fullstring.toString());
}

// Check if Rank changed and update custom field
def customFieldManager = ComponentAccessor.getCustomFieldManager()

def change = event?.getChangeLog()?.getRelated("ChildChangeItem").find {it.field == "Rank"}

if (change) {
    log.info ("Rank changed") 
    def tgtField = customFieldManager.getCustomFieldObjects(issue).find {it.name == "Rank Set"}
    log.info ("tgtField: " + tgtField);   
    def changeHolder = new DefaultIssueChangeHolder()
    FieldConfig fieldConfig = tgtField.getRelevantConfig(issue);
    def optionsManager = ComponentAccessor.getOptionsManager();
    final Options options = optionsManager.getOptions(fieldConfig);
    com.atlassian.jira.issue.customfields.option.Option yesOption = options.getOptionForValue("Yes", null);
    log.info ("Previous value of 'Rank Set': " + issue.getCustomFieldValue(tgtField))
    tgtField.updateValue(null, issue, new ModifiedValue(issue.getCustomFieldValue(tgtField), yesOption), changeHolder)
    log.info ("New value of 'Rank Set': " + issue.getCustomFieldValue(tgtField))
} else {
    log.info ("Rank did not change") 
}
1 vote
Jeremy Gaudet
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
January 19, 2016

The approach is sound; the alternative is to remove "rank" from the update screen, and use self-targeting transitions in the workflow to "Rank" the issue.  There's a certain elegance to that approach; if you do it that way, you can set the custom field in a post-function, and it makes "Rank" a first-class operation.  Whether or not it complicates the workflow will depend on how many states require the option.

This link (http://stackoverflow.com/questions/18428756/update-jira-subtask-fix-version-when-parent-issue-fix-version-updated) shows an example of how to check if a field is changed, and how to do an update; it doesn't include a custom field, but I have that example below.  The code is a little stale, some of the object types needed updating.  I can provide a more modern example if you need it, I've been meaning to make my version generic so I can share it more readily anyways.

import com.atlassian.jira.ComponentManager;
import com.atlassian.jira.issue.CustomFieldManager;
import com.atlassian.jira.issue.fields.CustomField;
CustomFieldManager customFieldManager = ComponentManager.getInstance().getCustomFieldManager();
CustomField manRankCF = customFieldManager.getCustomFieldObjectByName("Manually Ranked");
Boolean ranked = true;
issue.setCustomFieldValue(manRankCF,ranked);

Note that I'm not certain a Boolean is the correct type here, I'd need to do more research.

Nathan Nantais January 19, 2016

Thank you for the answer. I used the other code from Thanos, only because I tried it first and it worked.

Suggest an answer

Log in or Sign up to answer