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.
Online forums and learning are now in one easy-to-use experience.
By continuing, you accept the updated Community Terms of Use and acknowledge the Privacy Policy. Your public name, photo, and achievements may be publicly visible and available in search engines.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.