Use ScriptRunner to modify a custom field when Rank is changed

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

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.

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

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.

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.

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.

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

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") 
}

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.

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
Atlassian Community Anniversary

Happy Anniversary, Atlassian Community!

This community is celebrating its one-year anniversary and Atlassian co-founder Mike Cannon-Brookes has all the feels.

Read more
Community showcase
Julia Dillon
Posted Tuesday in Jira

Tell us how your team runs on Jira!

Hey Atlassian Community! Today we are launching a bunch of customer stories about the amazing work teams, like Dropbox and Twilio, are doing with Jira. You can check out the stories here. The thi...

202 views 1 18
Join discussion

Atlassian User Groups

Connect with like-minded Atlassian users at free events near you!

Find a group

Connect with like-minded Atlassian users at free events near you!

Find my local user group

Unfortunately there are no AUG chapters near you at the moment.

Start an AUG

You're one step closer to meeting fellow Atlassian users at your local meet up. Learn more about AUGs

Groups near you