Как проставить резолюцию в уже выполненных задачах

Часто возникает такая ситуация, что необходимо проставить резолюцию в задачах, которые уже находятся в закрывающих "зеленых" статусах, но по разным причинам резолюция не была проставлена. Можно это сделать руками, однако дата резолюции станет текущей датой, что не исправит кривое отображение в некоторых отчетах, ну и проставить руками резолюции в пару сотнях, а может и тысячах задач, то еще удовольствие. Для устранения этого бага необходимо использовать скрипт, который можно запускать из консоли или пихнуть его в джобу.

import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.config.ResolutionManager
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.index.IssueIndexingService
import com.atlassian.jira.issue.resolution.Resolution
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.web.bean.PagerFilter

import java.sql.Timestamp

import static com.atlassian.jira.issue.IssueFieldConstants.STATUS

def jql = "" // запрос для поиска задач, в которых необходимо проставить резолюцию
def botLogin = "name" // логин бота
def statusesNameResolutionName = [ // мапа в которой ключ это название закрывающего статуса, а значение это название резолюции, которая будет проставляться в этом статусе
        "Done"    : "Done", // пример в статусе Done резолюцию будет Done
        "Отказано": "Отказано",
]

def bot = getUserByName(botLogin)
def issues = getIssuesFromJQL(jql, bot)

issues.each { issue ->
    def resolutionDate = getLastTransitionDate(issue, statusesNameResolutionName.keySet() as List<String>)
    def resolution = getResolution(issue, statusesNameResolutionName)
    setResolution(issue, resolution, resolutionDate)
    issue.store()
    reindexIssue(issue)
}

/**
 * Получить все задачи по JQL запросу
 * @param jql запрос
 * @param user пользователь, который будет использоваться при поиске задач. 
 * Если у пользователя нет доступа к проекту или задаче, то это задача не будет добавлена в массив, лучше использовать тех учетку с админскими правами
 * @return
 */
static List<MutableIssue> getIssuesFromJQL(String jql, ApplicationUser user) {
    def searchService = ComponentAccessor.getComponentOfType(SearchService)
    def issueManager = ComponentAccessor.getIssueManager()
    def query = searchService.parseQuery(user, jql).getQuery()
    def results = searchService.search(user, query, PagerFilter.unlimitedFilter).getResults()
    return results.collect({ issue -> issueManager.getIssueObject(issue.id) })
}

/**
 * получить дату последнего перехода в закрывающий статус
 * @param issue задача
 * @param statusesName массив названий закрывающих статусов
 * @return
 */
static Timestamp getLastTransitionDate(MutableIssue issue, List<String> statusesName) {
    return ComponentAccessor.getChangeHistoryManager()
            .getChangeItemsForField(issue, STATUS)
            .findAll({ item -> statusesName.contains(item.toString) }).created.last()
}

/**
 * Выполнить реиндекс задачи
 * @param issue
 */
static void reindexIssue(MutableIssue issue) {
    ComponentAccessor.getComponent(IssueIndexingService).reIndex(issue)
}

/**
 * Проставление резолюции и даты резолюции
 * @param issue задача 
 * @param resolution объект резолюции
 * @param resolutionDate дата проставления резолюции
 * @return
 */
static setResolution(MutableIssue issue, Resolution resolution, Timestamp resolutionDate) {
    issue.setResolution(resolution)
    issue.setResolutionDate(resolutionDate)
}

/**
 * получение нужной резолюции
 * @param issue задача
 * @param map которая содержит в себе маппинг называния закрывающего статуса и названия резолюции для него
 * @return
 */
static Resolution getResolution(MutableIssue issue, Map<String, String> map) {
    return ComponentAccessor.getComponent(ResolutionManager).getResolutionByName(map.get(issue.getStatus().getName()))
}

/**
 * получить объект пользователя по его логину
 * @param login
 * @return
 */
static ApplicationUser getUserByName(String login) {
    return ComponentAccessor.getUserManager().getUserByName(login)
}