Set ResolutionDate Programmatically?

PeteToscano July 31, 2020

I wanted to change the closed status of a project's issues from A -> B, but made a mistake where I wiped out all of the resolutions that were set (on 1900+ issues). I know the mistake made, but now to recover the best I can. 

Initially, I wrote a Jira Python script that will iterate over all affected issues, search each issue's history to find the resolution it had before "the great wiping" this afternoon, then use update to set the issue's resolution to what it was most recently. After running this on about 50 test issues, I noticed there was a problem: the test issues have their nicely reset resolution set, but no resolutiondate field set.

I went back into jirashell to try to figure out how to set the resolutiondate, but no luck:

text: Field 'resolutiondate' cannot be set. It is not on the appropriate screen, or unknown.

Searching around, it seems that I might not be able to do it with a simple Jira Python script. (Well, maybe I could if I combine it with a connection directly to the Jira database to feed it SQL updates based on the history.created value of the appropriate history item, but that feels like it might be removing one too many safeties.) 

I suspect I'll have some success if I transition the affected issues from B -> B and setting the resolution in the transition, but I'd like to avoid putting them through another transition again. 

Any suggestions? Should I suck it up and go down the reflexive transition path or would there be another way to recover these resolutions and resolutiondates? 

(This is for Jira 7.9.0, not the version I mistakenly gave on the right and now can't edit.)

1 answer

1 accepted

2 votes
Answer accepted
Hana Kučerová
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
August 1, 2020

Hi @PeteToscano ,

this is a little bit strange, I believe the resolution date should be set automatically, when the resolution is set.

ScriptRunner for Jira has the Built-in Script, which is called Bulk Fix Resolutions. Maybe you can try it, if the behaviour would be the same or not.

PeteToscano August 1, 2020

Hana:

Thank you for the response. I did try the Bulk Fix Resolutions built-in script, but it didn't set the resolution date either (despite what the documentation said).

That said, I ScriptRunner for Jira did help me solve this issue. I took some responses from here and combined them with pieces from the Adaptavist Library and came up with a script that'll do what I was looking for. 

(Yes, this is ugly code with lots of room to improve, but it seems to have worked. It's the first time I've played with Groovy. I'm tired, but I wanted to give back.)

This script will iterate over all issues returned by a JQL query and, for each issue, it will use the first resolution-related history item to determine the "real" resolution and timestamp to use. It'll then save the updated issue and reindex it. I hope this is helpful for someone else.

EDIT: Added description of what this script does.

EDIT2: Edited the EDIT. :D 

import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.search.SearchException
import com.atlassian.jira.web.bean.PagerFilter
import org.apache.log4j.Level
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.history.ChangeItemBean
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.issue.IssueFactory
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.index.IssueIndexingService
import com.atlassian.jira.util.ImportUtils

def changeHistoryManager = ComponentAccessor.changeHistoryManager

// Set log level to INFO
log.setLevel(Level.INFO)

// The JQL query you want to search with
final jqlSearch = "status = closed AND project = FOO AND resolution is not EMPTY AND resolutiondate is EMPTY"

// Some components
def user = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def searchService = ComponentAccessor.getComponentOfType(SearchService)

// Parse the query
def parseResult = searchService.parseQuery(user, jqlSearch)
if (!parseResult.isValid()) {
log.error('Invalid query')
return null
}

def indexingService = ComponentAccessor.getOSGiComponentInstanceOfType(IssueIndexingService)

try {
// Perform the query to get the issues
def results = searchService.search(user, parseResult.query, PagerFilter.unlimitedFilter)
def issues = results.issues
issues.each {
MutableIssue miss = ComponentAccessor.getIssueManager().getIssueObject(it.getKey())
log.info(miss.key)
def changeItems = changeHistoryManager.getChangeItemsForField(miss,"resolution")
for (item in changeItems) {
log.info("Time: " + item.created + "\tFrom: " + item.fromString + "\tTo: " + item.toString)
miss.setResolutionDate(item.created)
miss.store()
boolean wasIndexing = ImportUtils.isIndexIssues()
ImportUtils.setIndexIssues(true)
indexingService.reIndex(miss)
ImportUtils.setIndexIssues(wasIndexing)
break
}
}
issues*.key
} catch (SearchException e) {
e.printStackTrace()
null
}

 Thanks again,

Pete

Like # people like this
Jens Walther December 16, 2022

I'll changed Pete's script to use the last transition time by adding:


def timeLastTransition = changeHistoryManager.getChangeItemsForField(it, 'status')?.last()?.created

missue.setResolutionDate(timeLastTransition)
Like Atlassian support likes this

Suggest an answer

Log in or Sign up to answer
DEPLOYMENT TYPE
SERVER
VERSION
6.15.1
TAGS
AUG Leaders

Atlassian Community Events