It's not the same without you

Join the community to find out what other Atlassian users are discussing, debating and creating.

Atlassian Community Hero Image Collage

Three ways to update an issue in Jira Java Api

In this article I would like to  examine three ways to update an issue in Jira, using Jira Java API. I tested all the code, provided in this article, in Jira 7.7.0 and Adaptivist ScriptRunner 5.3.7.

I will use Issue.setCustomFieldValue, CustomField.updateValue and IssueService.update methods. I will write example scripts on how to update all out of the box custom fields, using these methods.

The following custom fields were created:

Selection_015.png

 

1. Issue.setCustomFieldValue(CustomField customField, Object value):

A sample code looks like this:

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.event.type.EventDispatchOption
import java.sql.Date
import com.atlassian.jira.issue.customfields.option.Option
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.label.Label
import com.atlassian.jira.bc.user.search.UserSearchService
import com.atlassian.jira.bc.user.search.UserSearchParams
import com.atlassian.jira.user.ApplicationUser


def issue = ComponentAccessor.getIssueManager().getIssueByCurrentKey("BP-7")
def user = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()

// Initialize custom fields ----------------------------------
def singleline_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("singleline_field")
def datetimepicker_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("datetimepicker_field")
def checkbox_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("checkbox_field")
def number_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("number_field")
def labels_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("labels_field")
def multi_grouppicker_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("multi_grouppicker_field")
def multiline_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("multiline_field")
def datepicker_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("datepicker_field")
def userpicker_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("userpicker_field")
def radiobuttons_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("radiobuttons_field")
def selectlist_cascading_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("selectlist_cascading_field")
def select_singlechoice_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("select_singlechoice_field")
def selectlist_multichoice_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("selectlist_multichoice_field")
def url_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("url_field")

// set field values -------------------------------
issue.setCustomFieldValue(singleline_field, "test 1")
issue.setCustomFieldValue(datetimepicker_field, new Date(Calendar.getInstance().getTime().getTime()))
issue.setCustomFieldValue(checkbox_field, getOptions(issue, checkbox_field, ["option 1", "option 2"]))
issue.setCustomFieldValue(number_field, (Double) 1)
issue.setCustomFieldValue(labels_field, [new Label(null, issue.getId(), labels_field.getIdAsLong(), "Label")] as Set)
issue.setCustomFieldValue(multi_grouppicker_field, [ComponentAccessor.getGroupManager().getGroup("jira-software-users")])
issue.setCustomFieldValue(multiline_field, "test 1")
issue.setCustomFieldValue(datepicker_field, new Date(Calendar.getInstance().getTime().getTime()))
issue.setCustomFieldValue(userpicker_field, findUser("admin"))
issue.setCustomFieldValue(radiobuttons_field, getOptions(issue, checkbox_field, ["option 1"]).get(0))
issue.setCustomFieldValue(selectlist_cascading_field, getCascadingOptions(issue, selectlist_cascading_field))
issue.setCustomFieldValue(select_singlechoice_field,  getOptions(issue, select_singlechoice_field, ["option 1"]).get(0))
issue.setCustomFieldValue(selectlist_multichoice_field, getOptions(issue, selectlist_multichoice_field, ["option 1", "option 2"]))
issue.setCustomFieldValue(url_field, "http://google.com")

// apply changes to Jira -----------------------
ComponentAccessor.getIssueManager().updateIssue(user, issue, EventDispatchOption.ISSUE_UPDATED, false)

// get option list for radio button, checkbox and select custom fields
def List<Option> getOptions(Issue issue, CustomField customField, List<String> optionList) {
    def config = customField.getRelevantConfig(issue)
    def options = ComponentAccessor.getOptionsManager().getOptions(config)
    def optionsToSelect = options.findAll { it.value in optionList }
}

// get user for the user picker custom field
def ApplicationUser findUser(String userName) {
   def userSearchService = ComponentAccessor.getComponent(UserSearchService.class);
   UserSearchParams userSearchParams = (new UserSearchParams.Builder()).allowEmptyQuery(true).includeActive(true).includeInactive(true).maxResults(100000).build();

   return userSearchService.findUsers(userName, userSearchParams).get(0)

}

// get options for cascading select
def Map<String, Object> getCascadingOptions(Issue issue, CustomField customField) {
    def parentOptionObj = getOptions(issue, customField, ["option 1"]).get(0) as Option
    def childOptionObj = ComponentAccessor.getOptionsManager().findByParentId(parentOptionObj.getOptionId()).get(0)
    Map<String,Object> newValues = new HashMap<>()
    newValues.put(null, parentOptionObj)
    newValues.put("1", childOptionObj)
    return newValues
}

If you want to empty values then you should set the null value for custom fields:

issue.setCustomFieldValue(singleline_field, null) 

2. CustomField.updateValue(FieldLayoutItem fieldLayoutItem, Issue issue, ModifiedValue modifiedValue, IssueChangeHolder issueChangeHolder):

A sample code looks like this:

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.event.type.EventDispatchOption
import java.sql.Date
import com.atlassian.jira.issue.customfields.option.Option
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.label.Label
import com.atlassian.jira.bc.user.search.UserSearchService
import com.atlassian.jira.bc.user.search.UserSearchParams
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.ModifiedValue


def issue = ComponentAccessor.getIssueManager().getIssueByCurrentKey("BP-7")
def user = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()

// Initialize custom fields --------------------------
def singleline_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("singleline_field")
def datetimepicker_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("datetimepicker_field")
def checkbox_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("checkbox_field")
def number_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("number_field")
def labels_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("labels_field")
def multi_grouppicker_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("multi_grouppicker_field")
def multiline_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("multiline_field")
def datepicker_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("datepicker_field")
def userpicker_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("userpicker_field")
def radiobuttons_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("radiobuttons_field")
def selectlist_cascading_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("selectlist_cascading_field")
def select_singlechoice_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("select_singlechoice_field")
def selectlist_multichoice_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("selectlist_multichoice_field")
def url_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("url_field")

// set custom fields values (changes will be applied immediately) --------------------
singleline_field.updateValue(null, issue, new ModifiedValue("", (Object) "Test 1"), new DefaultIssueChangeHolder())
datetimepicker_field.updateValue(null, issue, new ModifiedValue("", (Object) new Date(Calendar.getInstance().getTime().getTime())), new DefaultIssueChangeHolder())
checkbox_field.updateValue(null, issue, new ModifiedValue("", (Object) getOptions(issue, checkbox_field, ["option 1", "option 2"])), new DefaultIssueChangeHolder())
number_field.updateValue(null, issue, new ModifiedValue("",  (Object) (Double)  1), new DefaultIssueChangeHolder())
labels_field.updateValue(null, issue, new ModifiedValue("",  (Object) ([new Label(null, issue.getId(), labels_field.getIdAsLong(), "Label")] as Set)), new DefaultIssueChangeHolder())
multi_grouppicker_field.updateValue(null, issue, new ModifiedValue("",  (Object) [ComponentAccessor.getGroupManager().getGroup("jira-software-users")]), new DefaultIssueChangeHolder())
multiline_field.updateValue(null, issue, new ModifiedValue("",  (Object)  "test 1"), new DefaultIssueChangeHolder())
datepicker_field.updateValue(null, issue, new ModifiedValue("",  (Object)  new Date(Calendar.getInstance().getTime().getTime())), new DefaultIssueChangeHolder())
userpicker_field.updateValue(null, issue, new ModifiedValue("",  (Object) findUser("admin")), new DefaultIssueChangeHolder())
radiobuttons_field.updateValue(null, issue, new ModifiedValue("",  (Object) getOptions(issue, checkbox_field, ["option 1"]).get(0)), new DefaultIssueChangeHolder())
selectlist_cascading_field.updateValue(null, issue, new ModifiedValue("",  (Object)  getCascadingOptions(issue, selectlist_cascading_field)), new DefaultIssueChangeHolder())
select_singlechoice_field.updateValue(null, issue, new ModifiedValue("",  (Object) getOptions(issue, select_singlechoice_field, ["option 1"]).get(0)), new DefaultIssueChangeHolder())
selectlist_multichoice_field.updateValue(null, issue, new ModifiedValue("",  (Object) getOptions(issue, selectlist_multichoice_field, ["option 1", "option 2"])), new DefaultIssueChangeHolder())
url_field.updateValue(null, issue, new ModifiedValue("",  (Object) "http://google.com"), new DefaultIssueChangeHolder())

// all functions are the same as in the script above ---------------------

 You can empty custom fields by setting the null value:

singleline_field.updateValue(null, issue, new ModifiedValue("", null), new DefaultIssueChangeHolder())

3.  IssueService.update(ApplicationUser user, IssueService.UpdateValidationResult updateValidationResult):

A sample code looks like this:

import com.atlassian.jira.issue.IssueInputParameters
import com.atlassian.jira.bc.issue.IssueService
import com.atlassian.jira.bc.issue.IssueService.UpdateValidationResult
import com.atlassian.jira.bc.issue.IssueService.IssueResult
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.customfields.option.Option
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.label.Label
import com.atlassian.jira.bc.user.search.UserSearchService
import com.atlassian.jira.bc.user.search.UserSearchParams
import com.atlassian.jira.user.ApplicationUser
import java.text.SimpleDateFormat

def issue = ComponentAccessor.getIssueManager().getIssueByCurrentKey("BP-7")
def user = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()

// initialize custom fields --------------------
def singleline_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("singleline_field")
def datetimepicker_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("datetimepicker_field")
def checkbox_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("checkbox_field")
def number_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("number_field")
def labels_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("labels_field")
def multi_grouppicker_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("multi_grouppicker_field")
def multiline_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("multiline_field")
def datepicker_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("datepicker_field")
def userpicker_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("userpicker_field")
def radiobuttons_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("radiobuttons_field")
def selectlist_cascading_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("selectlist_cascading_field")
def select_singlechoice_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("select_singlechoice_field")
def selectlist_multichoice_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("selectlist_multichoice_field")
def url_field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("url_field")

IssueService issueService = ComponentAccessor.getComponent(IssueService.class);
IssueInputParameters issueInputParameters = issueService.newIssueInputParameters();
// set values for custom fields ------------------
issueInputParameters
      .addCustomFieldValue(singleline_field.getId(), "Test 1")
      .addCustomFieldValue(datetimepicker_field.getId(), new SimpleDateFormat("d/MMM/yy hh:mm a").format(new Date()))
      .addCustomFieldValue(checkbox_field.getId(),  getOptionsAsString(issue, checkbox_field, ["option 1"]))
      .addCustomFieldValue(number_field.getId(), "1")
      .addCustomFieldValue(labels_field.getId(), "Label")
      .addCustomFieldValue(multi_grouppicker_field.getId(), "jira-software-users")
      .addCustomFieldValue(multiline_field.getId(), "Test 2")
      .addCustomFieldValue(datepicker_field.getId(), new SimpleDateFormat("d/MMM/yy").format(new Date()))
      .addCustomFieldValue(userpicker_field.getId(), "admin")
      .addCustomFieldValue(radiobuttons_field.getId(), getOptionsAsString(issue, radiobuttons_field, ["option 1"]))
      .addCustomFieldValue(selectlist_cascading_field.getId(), getCascadingOptions(issue, selectlist_cascading_field).get("parent").toString())
      .addCustomFieldValue(selectlist_cascading_field.getId() + ":1", getCascadingOptions(issue, selectlist_cascading_field).get("1").toString())
      .addCustomFieldValue(select_singlechoice_field.getId(), getOptionsAsString(issue, select_singlechoice_field, ["option 1"]))
      .addCustomFieldValue(selectlist_multichoice_field.getId(), getOptionsAsString(issue, selectlist_multichoice_field, ["option 1"]))
      .addCustomFieldValue(url_field.getId(), "http://google.com")
// we do not provide all the values for an issue, that is why we need to state it
      issueInputParameters.setRetainExistingValuesWhenParameterNotProvided(true,true)
// validate update -------------
UpdateValidationResult updateValidationResult = issueService.validateUpdate(user, issue.getId(), issueInputParameters);
if (updateValidationResult.isValid())
{
// update the issue ----------
    IssueResult updateResult = issueService.update(user, updateValidationResult);
    if (!updateResult.isValid())
    {
        log.error("error updateResult: " + updateResult.getErrorCollection().toString())
    }
} else {
    log.error("error: updateValidationResult" + updateValidationResult.getErrorCollection().toString())
}

// get options for radio button, checkbox and select custom fields ------
def List<Option> getOptions(Issue issue, CustomField customField, List<String> optionList) {
    def config = customField.getRelevantConfig(issue)
    def options = ComponentAccessor.getOptionsManager().getOptions(config)
    return options.findAll{ it.value in optionList }
}
// represent options as String
def String getOptionsAsString(Issue issue, CustomField customField, List<String> optionList) {
    List<Long> optionIdList = new ArrayList<>()
    getOptions(issue, customField, optionList).each {
       optionIdList.add(((Option) it).getOptionId())
    }
    return optionIdList.join(",")

}

// get user for user picker custom field
def ApplicationUser findUser(String userName) {
   def userSearchService = ComponentAccessor.getComponent(UserSearchService.class);
   UserSearchParams userSearchParams = (new UserSearchParams.Builder()).allowEmptyQuery(true).includeActive(true).includeInactive(true).maxResults(100000).build();

   return userSearchService.findUsers(userName, userSearchParams).get(0)

}
// get cascading options
def Map<String, Object> getCascadingOptions(Issue issue, CustomField customField) {
    def parentOptionObj = getOptions(issue, customField, ["option 1"]).get(0) as Option
    def childOptionObj = ComponentAccessor.getOptionsManager().findByParentId(parentOptionObj.getOptionId()).get(0)
    Map<String,Object> newValues = new HashMap<>()
    newValues.put("parent", parentOptionObj.getOptionId())
    newValues.put("1", childOptionObj.getOptionId())
    return newValues
}

 You can empty field by:

 .addCustomFieldValue(singleline_field.getId(), null)

 What type of values to pass to the three methods?

Selection_016.png 

 Functionality difference:

 Selection_017.png

 You can find all the code here:

Issue.setCustomFieldValue

CustomField.updateValue

IssueService.update

8 comments

 

Thank you Alexey for the overview. 

Actually, functionality difference table will decrease my work for small customizes.

 

Cheers,

Gonchik Tsymzhitov

Thanks! A very useful write up! Its something which I have tried understanding in the past and walked towards other methods.

Nic Brough Community Leader Mar 03, 2018

A question that I've been asked (and never had a good answer for) - which methods work best where?

By "where", I mean where the code is run.  For example, a post-function on issue create is different to a post-function everywhere else in the workflow, and both are different to listeners.

Thank you for a very useful article.

Thank you Alexey for such a great write up on this. I have struggling on this for a week now & was unable to decide on which method to use. Your write up indeed gave me a direction to to work.

I'm using the updateValue() function to update value into a custom field. But I'm getting a method invocation error. I'm sharing the error trace for reference. Pls. lemme know if anybody can help me get past this exception:

*****************************************************************************************
Sourced file: inline evaluation of: `` import com.atlassian.jira.issue.Issue; import com.atlassian.jira.issue.Modifi . . . '' : Error in method invocation: Method updateValue( com.atlassian.jira.issue.fields.layout.field.FieldLayoutItemImpl, com.innovalog.jmcf.fields.IssueProxy, com.atlassian.jira.issue.ModifiedValue, com.atlassian.jira.issue.util.DefaultIssueChangeHolder ) not found in class'com.atlassian.jira.issue.fields.ImmutableCustomField' : at Line: 28 : in file: inline evaluation of: 

*****************************************************************************************

Just not sure why it's going to ImmutableCustomField :(

 

Appreciate all your help in advance.

Thanks,

Abhi

Thanks for a very useful article.

A simple reflexion for beginners like me:

When using ...

issueService.update(user, updateValidationResult)

...in a listener class, one must decide if we want another event to be launched or not, then use overloaded method:

update(ApplicationUser user, IssueService.UpdateValidationResult updateValidationResult, EventDispatchOption eventDispatchOption, boolean sendMail)

how to do the same with JRJC ?

Valuable knowledge. Thank you!

Comment

Log in or Sign up to comment
Community showcase
Published in Agile

Why can't I manage my Sprint while I am the SCRUM Master?

As a SCRUM Master, one of your key tasks involves planning Sprints in your team and in order to do this, you must be able to create new Sprints and complete active ones. In order to fulfil these ta...

305 views 0 7
Read article

Community Events

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

Find an event

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

Unfortunately there are no Community Events near you at the moment.

Host an event

You're one step closer to meeting fellow Atlassian users at your local event. Learn more about Community Events

Events near you