company_banner

Три способа обновить запрос в Jira из ScriptRunner, используя Jira Java API

    В этой статье будут рассмотрены три способа обновления запроса в Jira, используя Jira Java API.
    Я буду использовать следующие методы Jira Java API:

    • Issue.setCustomFieldValue(CustomField customField, Object value)
    • CustomField.updateValue(FieldLayoutItem fieldLayoutItem, Issue issue, ModifiedValue modifiedValue, IssueChangeHolder issueChangeHolder)
    • IssueService.update(ApplicationUser user, IssueService.UpdateValidationResult updateValidationResult)

    Будут приведены примеры скриптов для обновления всех типов кастомных полей, доступных в Jira из «коробки», с таблицей, в которой указаны отличия работы рассматриваемых методов друг от друга.


    Все скрипты я протестировал на Jira 7.7.0 в консоли скриптов плагина Adaptivist ScriptRunner 5.3.7. Целью статьи было не написание «чистого кода», а изложение принципов работы с запросами.

    Тексты скриптов можно скачать здесь.

    Были созданы следующие кастомные поля:

    image

    Способ 1 Issue.setCustomFieldValue(CustomField customField, Object value)


    Ниже приведен скрипт для изменения полей.

    Код
    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()
    
    // Получаем ссылки на кастомные поля
    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")
    
    // устанавливаем значения полей
    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") 
    
    // применяем изменения
    ComponentAccessor.getIssueManager().updateIssue(user, issue, EventDispatchOption.ISSUE_UPDATED, false)
    
    // получем опции для  кастомных полей типа radio button, checkbox and select
    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 } 
    }
    
    // получаем пользователя для кастомного поля типа user picker 
    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)
    
    }
    
    // получаем опции для кастомного поля типа 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
    }


    Для того, чтобы обнулить поле, необходимо установить значение null. Например, вот так:

    issue.setCustomFieldValue(singleline_field, null) 


    Способ 2 CustomField.updateValue(FieldLayoutItem fieldLayoutItem, Issue issue, ModifiedValue modifiedValue, IssueChangeHolder issueChangeHolder)


    Ниже приведен скрипт для изменения полей:

    Код
    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()
    
    // Получаем ссылки на кастомные поля
    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")
    
    // вносим изменения. Изменения будут применены сразу
    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())
    
    // получаем опции для кастомные полей типа radio button, checkbox and select
    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 } 
    }
    
    // получаем пользователя для кастомного поля типа user picker
    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)
    
    }
    
    // получаем опции для кастомного поля типа 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
    }


    Для того, чтобы обнулить поле, необходимо установить значение null. Например, вот так:

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

    Способ 3 IssueService.update(ApplicationUser user, IssueService.UpdateValidationResult updateValidationResult)


    Ниже приведен скрипт для изменения полей:

    Код
    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()
    
    // Получаем ссылки на кастомные поля
    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();
    // устанавливаем значения кастомных полей
    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")
    // мы устанавливаем не все значения для запроса, поэтому мы должны явно это указать 
          issueInputParameters.setRetainExistingValuesWhenParameterNotProvided(true,true)
    // проверяем корректность наших данных для обновления запроса
    UpdateValidationResult updateValidationResult = issueService.validateUpdate(user, issue.getId(), issueInputParameters);
    if (updateValidationResult.isValid())
    {
        // применяем изменения
        IssueResult updateResult = issueService.update(user, updateValidationResult);
        if (!updateResult.isValid())
        {
            log.error("error updateResult: " + updateResult.getErrorCollection().toString())
        }
    } else {
        log.error("error: updateValidationResult" + updateValidationResult.getErrorCollection().toString())
    }
    
    // получаем опции для кастомных полей radio button, checkbox and select
    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 }
    }
    // переводит опции (option.getOptionId()) в представление типа 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(",")
    
    }
    
    // получаем опции для кастомного поля типа user picker 
    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)
    
    }
    // получаем опции для кастомного поля типа 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("parent", parentOptionObj.getOptionId())
        newValues.put("1", childOptionObj.getOptionId())
        return newValues
    }


    Для того, чтобы обнулить поле, необходимо установить значение null. Например, вот так:

     .addCustomFieldValue(singleline_field.getId(), null)

    Какие типы данных нужно передавать в каждый метод




    Отличия в работе каждого из методов


    image
    Райффайзенбанк 93,89
    Развеиваем мифы об IT в банках
    Поделиться публикацией
    Комментарии 0

    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

    Самое читаемое