Scriptrunner - convert subtask to task

Chris Melville
Contributor
August 17, 2023

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)
}

 

1 answer

1 accepted

0 votes
Answer accepted
Ram Kumar Aravindakshan _Adaptavist_
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 18, 2023

Hi @Chris Melville

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:-

image_2023-08-18_183137912.png

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.

test1.png

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:-

test2.png

 

I hope this helps to answer your question. :-)

Thank you and Kind regards,

Ram

Chris Melville
Contributor
August 21, 2023

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.

Suggest an answer

Log in or Sign up to answer
TAGS
AUG Leaders

Atlassian Community Events