Here is a script to bulk convert Epic subtasks to Epic children. It all seems to work but I am not sure I have handled all the behind the scenes management required to cleanly convert subtasks to child tasks. Looking for someone who knows to comment on whether I have handled it completely or not.
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.query.Query
import com.atlassian.jira.jql.parser.JqlParseException
import com.atlassian.jira.jql.parser.JqlQueryParser
import com.atlassian.jira.web.bean.PagerFilter
import com.atlassian.jira.issue.search.SearchQuery
import com.atlassian.jira.issue.search.SearchProvider
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser)
def searchProvider = ComponentAccessor.getComponent(SearchProvider)
def searchQuery = "project = JAM AND type = Epic"
def destinationIssueType = "11600" // Desired new issue type ID
def user = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def issueManager = ComponentAccessor.issueManager
def issueLinkManager = ComponentAccessor.issueLinkManager
// Change subtasks of an Epic to child issues of an Epic
Query query
try { query = jqlQueryParser.parseQuery(searchQuery) } catch (JqlParseException e) { return e }
def searchResults = searchProvider.search(SearchQuery.create(query, user), PagerFilter.unlimitedFilter, (Set<String>)["key"])
log.warn("Query: '${searchQuery}' Results: ${searchResults.results.size()}")
// Iterate over each subtask in each Epic
searchResults.results.each { documentIssue ->
def epicKey = documentIssue.document.fields.find { it.name() == "key" }.stringValue()
def epic = issueManager.getIssueObject(epicKey)
Collection subTasks = epic.getSubTaskObjects()
subTasks.each { it ->
log.warn("processing subtask " + it.key)
//Change the subtask to the indicated type
assert it.getParentObject().key == epic.key : "parent key != epic key"
// Remove the parent-child link and change the issue type
def inwardLinks = issueLinkManager.getInwardLinks(it.id)
inwardLinks.findAll { it.issueLinkType.isSubTaskLinkType() }.each {
issueLinkManager.removeIssueLink(it, user)
}
((MutableIssue) it).setIssueTypeId(destinationIssueType)
// set Epic link
def epicLinkCF = ComponentAccessor.getCustomFieldManager().getCustomFieldObjects(it).findByName('Epic Link')
if (epicLinkCF)
epicLinkCF.updateValue(null, it, new ModifiedValue(it.getCustomFieldValue(epicLinkCF), epic), newDefaultIssueChangeHolder())
else
log.error("Epic custom field not found for issue: " + it.key)
//Update the moved issue
issueManager.updateIssue(user, (MutableIssue) it, EventDispatchOption.ISSUE_UPDATED, false)
}
// Reorder the epic's links as its sub-task links have been removed
ComponentAccessor.subTaskManager.resetSequences(epic)
}
If you want to convert the Sub-tasks in an Epic to different Issue Type(s), you will use the ScriptRunner console with the sample code below:-
import com.adaptavist.hapi.jira.issues.Issues
import com.adaptavist.hapi.jira.users.Users
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.issue.MutableIssue
def issue = Issues.getByKey('MOCK-5')
def loggedInUser = Users.loggedInUser
def issueLinkManager = ComponentAccessor.issueLinkManager
def issueManager = ComponentAccessor.issueManager
def constantsManager = ComponentAccessor.constantsManager
def links = issueLinkManager.getOutwardLinks(issue.id)
def originalIssuesInEpic= links.findAll { it.issueLinkType.name == 'Epic-Story Link' }*.destinationObject
def issueType = constantsManager.allIssueTypeObjects.find {it.name == 'Task' }
originalIssuesInEpic.findAll {
if(it.subTaskObjects) {
it.subTaskObjects.each {subTask ->
def currentIssue = subTask as MutableIssue
currentIssue.setIssueType(issueType)
issueLinkManager.removeIssueLinks(currentIssue, loggedInUser)
issueManager.updateIssue(loggedInUser, currentIssue, EventDispatchOption.ISSUE_UPDATED, false)
}
}
}
Please note that the sample working code above is not 100% exact to your environment. Hence, you will need to modify it accordingly.
Below is a screenshot of the ScriptRunner console for your reference:-
Below are a couple of test screenshots for your reference:-
1. To test, I have created a Story with 2 Sub-Task as shown in the screenshot below.
2. Once I execute the script above in the ScriptRunner console, the Sub-Tasks are converted into Task objects and unlinked from the Story as shown in the screenshot below:-
I hope this helps to answer your question. :-)
Thank you and Kind regards,
Ram
Thank you for your answer. What you provided is a very helpful reference. It is close enough to what I actually need to do to confirm my posted script will work as expected. I had so many of these to convert I did it in batches to avoid the console timeout.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.