Sum Story Points in Jira Cloud with ScriptRunner

Hello there,

This ScriptRunner code will be useful if your Epic contains more than 100 sub-tasks. Unfortunately, automation JQL rules runs only first 100 issues. Please, find documentation by link Jira automation actions | Cloud automation Cloud | Atlassian Support 'Search for up to 100 issues using an Assets AQL query.'

Fortunately, ScriptRunner can send JQL request with parameters 'startAt'. This parameter allows to iterate the entire JQL results list. 

Please, double check the code on a test instance because you cannot undo this operation.

How-to:

  • Create a new Script Listener
  • Events: 'Issue created' and 'Issue updated'
  • Code to run:
//Exclude Epic types
if( issue.fields.issuetype.name == 'Epic'){
logger.info('Ignore Epic type')
return
}
// get custom fields
def customFields = get("/rest/api/2/field")
.asObject(List)
.body
.findAll { (it as Map).custom } as List<Map>

def epicId = customFields.find { it.name == 'Epic Link' }?.id
def storyPointId = customFields.find { it.name == 'Story Points' }?.id

def epicLink = issue.fields[epicId] as String

if (epicLink == null) {
logger.info("No Epic Link for this issue")
return
}

//find all sub-tasks for epic
def jqlEncoded = URLEncoder.encode("\"Epic Link\" = $epicLink", "UTF-8")
def startAt = 0
def maxResults = 100
def total = 0
double totalStoryPoints = 0.0
def firstCycle = true
def checkedSubTasks = 0
// Repat jql search if total count more than 100 issues
while((startAt <= total) || firstCycle ){
firstCycle = false
def jqlResponse = get("/rest/api/2/search?jql=$jqlEncoded&startAt=$startAt&maxResults=$maxResults").asObject(Object).body
total = jqlResponse['total'] as Integer
Double sumStoryPointsSubTasks = 0
try{
def subTasks = jqlResponse['issues']['fields'][storyPointId]?.findAll{ it != null }
sumStoryPointsSubTasks = subTasks?.sum() as Double
checkedSubTasks += subTasks?.size()
}catch(Exception e){
logger.error('Can not get sum ', e)
}
totalStoryPoints += sumStoryPointsSubTasks != null?sumStoryPointsSubTasks:0
startAt += maxResults
}

logger.info('Checked sub-tasks '+checkedSubTasks)

def parentEpicStoryPoints = get("/rest/api/2/issue/$epicLink").asObject(Object).body['fields'][storyPointId] as Double
def originEpicStoryPoints = parentEpicStoryPoints!=null?parentEpicStoryPoints:0

//set new sum if it was changed
if(originEpicStoryPoints != totalStoryPoints){
put("/rest/api/2/issue/$epicLink")
.header("Content-Type", "application/json")
.body([
fields:[
(storyPointId): totalStoryPoints
]
]).asJson()
}

 That's all. This listener now updates a Story Points for an Epic as the sum of its linked issues.

P.S.
I've created 122 linked Story issues with the same Epic Link parent.
Screenshot 2023-04-16 224349.png

All sub-tasks have 'Story Points' value equals 1.
Screenshot 2023-04-16 224414.png

The listener set parent issue's value to 122.
Screenshot 2023-04-16 224436.png

I hope the article is helpful.

Regards,
Andrey

0 comments

Comment

Log in or Sign up to comment
TAGS
AUG Leaders

Atlassian Community Events