Debug Scriptrunner Listener script

Jeanne Howe December 11, 2020

We have implemented a Listener Script to sum up story points to the Epic. The script is working as expected, but we are seeing some errors in the logs. The error occurs against some issues, but not all. What is the best way to "debug" where these errors are coming from? Not knowing what is causing the errors, I am not sure how to debug. I think it may be occurring against Stories that have a Story Point of 0, but I am not sure. The error we see is:
CorrelationId: d09ae111-14a2-4e5d-b2e4-357b61af6377
RUN Script identifier: 71a41f53-a120-410b-b762-7be45295cd66 com.adaptavist.sr.cloud.events.WebhookExecution (jira:issue_updated webhook fired for issue ST-1936)
Script name: Sum child story points and add to parent Epic Took: 616ms Logs:
2020-12-11 16:03:33.109 ERROR - Cannot invoke method size() on null object
2020-12-11 16:03:33.170 ERROR - Class: com.adaptavist.sr.cloud.events.WebhookExecution, Config: null

The script itself does not fail, and the Story Points are summed, it's just logging an error that, if I can "catch", I could resolve/avoid the error in the log.

Any suggestion on how to enhance logging/debugging?

The script that is running: 

// Get the ID of the fields for Story Points and Epic Link
def storyPointsField = get("/rest/api/2/field").asObject(List).body.find {(it as Map).name == 'Story Points'}.id
def epicLinkField = get("/rest/api/2/field").asObject(List).body.find {(it as Map).name == 'Epic Link'}.id

// Retrieve all the issues in this issues' Epic
def epicKey = issue.fields."${epicLinkField}"
def issuesInEpic = get("/rest/agile/1.0/epic/${epicKey}/issue")
        .asObject(Map)
        .body
        .issues as List<Map>
logger.info("Total issues in Epic for ${epicKey}: ${issuesInEpic.size()}")

// Sum the estimates
def estimate = issuesInEpic.collect { Map issueInEpic ->
    issueInEpic.fields."${storyPointsField}" ?: 0
}.sum()
logger.info("Summed estimate: ${estimate}")

// Now update the parent Epic
def result = put("/rest/api/2/issue/${epicKey}")
    .queryString("overrideScreenSecurity", Boolean.TRUE)
    .header('Content-Type', 'application/json')
    .body([
        fields: [
                "${storyPointsField}": estimate,
        ]
    ])
    .asString()

// check that updating the parent issue worked
assert result.status >= 200 && result.status < 300

1 answer

1 accepted

1 vote
Answer accepted
Martin Bayer _MoroSystems_ s_r_o__
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
December 11, 2020

Hi @Jeanne Howe I checked the documentation (https://scriptrunner-docs.connect.adaptavist.com/jiracloud/logs.html) and it looks like there is Logs screen in the script runner:

Selection_094.png

You can log messages in your script using following methods:

  • logger.trace
  • logger.debug
  • logger.info
  • logger.warn
  • logger.error
Jeanne Howe December 13, 2020

Hi Martin,

I think the issue causing the error is that some Stories do not have an Epic Link. 

I know I can add a condition to be evaluated before the listener runs. Do you know what the format of the condition would be if I wanted to verify the issue has an Epic Link? Would it be something like this:

issue.issueType.name.match('^(Story|Bug|Task)$') != null and link.issueLinkType.name.match == ('^(Epic-Story Link)$')

Can this be done as a condition, or do I need some type of IF statement in the listener script itself?

Martin Bayer _MoroSystems_ s_r_o__
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
December 14, 2020

Hi @Jeanne Howe sorry, I thought you needed to point to a place where the log message are displayed.

When I'm checking your code and error message, I would suggest you to wrap following part of code with IF condition:

if(issuesInEpic){

logger.info("Total issues in Epic for ${epicKey}: ${issuesInEpic.size()}")

// Sum the estimates
def estimate = issuesInEpic.collect { Map issueInEpic ->
    issueInEpic.fields."${storyPointsField}" ?: 0
}.sum()
logger.info("Summed estimate: ${estimate}")

// Now update the parent Epic
def result = put("/rest/api/2/issue/${epicKey}")
    .queryString("overrideScreenSecurity", Boolean.TRUE)
    .header('Content-Type', 'application/json')
    .body([
        fields: [
                "${storyPointsField}": estimate,
        ]
    ])
    .asString()

// check that updating the parent issue worked
assert result.status >= 200 && result.status < 300

}

Is it what you needed? Or can you clarify it further? 

Jeanne Howe December 16, 2020

Hi @Martin Bayer _MoroSystems_ s_r_o__  Thank you. I did something very similar to this (once I figured out the IF statement). My code looks like this:

// Get the ID of the fields for Story Points and Epic Link
def storyPointsField = get("/rest/api/2/field").asObject(List).body.find {(it as Map).name == 'Story Points'}.id
def epicLinkField = get("/rest/api/2/field").asObject(List).body.find {(it as Map).name == 'Epic Link'}.id

// Retrieve all the issues in this issues' Epic
def epicKey = issue.fields."${epicLinkField}"
logger.info("Epic Key is: " + epicKey)

if (epicKey != null) { //This is to check if the event.issue's epic is null
def issuesInEpic = get("/rest/agile/1.0/epic/${epicKey}/issue")
.asObject(Map)
.body
.issues as List<Map>
logger.info("Total issues in Epic for ${epicKey}: ${issuesInEpic.size()}")

// Sum the estimates
def estimate = issuesInEpic.collect { Map issueInEpic ->
issueInEpic.fields."${storyPointsField}" ?: 0
}.sum()
logger.info("Summed estimate: ${estimate}")

// Now update the parent Epic
def result = put("/rest/api/2/issue/${epicKey}")
.queryString("overrideScreenSecurity", Boolean.TRUE)
.header('Content-Type', 'application/json')
.body([
fields: [
"${storyPointsField}": estimate,
]
])
.asString()

// check that updating the parent issue worked
assert result.status >= 200 && result.status < 300
}

Martin Bayer _MoroSystems_ s_r_o__
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
December 16, 2020

hi @Jeanne Howe ,so is it working now?🙂

Jeanne Howe December 16, 2020

Yes, it is working and it is no longer writing errors for those issues without an Epic Link.

 

Thank you!

Jeanne Howe December 23, 2020

@Martin Bayer _MoroSystems_ s_r_o__ 

May I ask you one more question about my listener script.

After running with no errors for several days, I am now seeing an error code -

returned an error code: status: 429 - Too Many Requests
body: java.lang.RuntimeException: com.fasterxml.jackson.core.JsonParseException: Unexpected character ('<' (code 60)): expected a valid value (JSON String, Number, Array, Object or token 'null', 'true' or 'false')
 at [Source: (String)"<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><title>Oops - an error has occurred</title><link type='text/css' rel='stylesheet' href='/static-assets/metal-all.css' media='all'><script src='/static-assets/metal-all.js'></script><!--[if lt IE 9]><link type='text/css' rel='stylesheet' href='/static-assets/metal-all-ie.css' media='all'><script src='/static-assets/metal-all-ie.js'></script><![endif]--><!--[if IE 9]><link type='text/css"[truncated 1422 chars]; line: 1, column: 2]
2020-12-23 17:16:37.210 ERROR - Cannot get property 'issues' on null object on line 10
2020-12-23 17:16:37.211 ERROR - Class: com.adaptavist.sr.cloud.events.WebhookExecution, Config: null

This appears to be happening at the "get" call at the start of the IF statement:

if (epicKey != null) { //This is to check if the event.issue's epic is null
def issuesInEpic = get("/rest/agile/1.0/epic/${epicKey}/issue")
.asObject(Map)
.body
.issues as List<Map>
logger.info("Total issues in Epic for ${epicKey}: ${issuesInEpic.size()}")

 

I think the issue may be that one or more Stories in the Epic do not have any story Points assigned. Can I nest an IF statement to check the Epic Key and check the Story Point field? Something like this:

if (epicKey != null) && (storyPointsField != null)

I am unsure of how to capture the information coming back from the "get" call to log it and find the error. Is there a way to put the IF statement, or the entire script, into a debug mode so that everything gets logged?

 

Jeanne

Martin Bayer _MoroSystems_ s_r_o__
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
December 29, 2020

Hi @Jeanne Howe I think you are getting error because you have "too much requests". Each subscription plan has limitation of rest API requests which can be done.  This information is not published anywhere (if nothing changed :)) because it is calculated based on the platform workload etc. More information can be found here:

https://developer.atlassian.com/cloud/jira/platform/rate-limiting/

Suggest an answer

Log in or Sign up to answer