Scriptrunner Jobs: Do Custom Jobs not finish parsing through filters?

Diana
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
May 22, 2024

I have the following custom job script that works great. It grabs saved filters, parse the query, and save as a txt file to an issue as an attachment. However, I am parsing at least 3-4 filters, and I have some JQLs that run issueFunction addedAfterSprintStart.

//Job: Auto export saved filter

//Mapping: Sandbox issue

//Interval/Cron expression: 0 0 8 */14 * ? (for now, i Run Now to test)

import java.io.FileWriter

import java.io.BufferedWriter

import com.atlassian.jira.bc.issue.search.SearchService

import com.atlassian.jira.web.bean.PagerFilter

import com.atlassian.jira.jql.parser.JqlQueryParser

import com.atlassian.jira.component.ComponentAccessor

import com.atlassian.jira.issue.AttachmentManager

import com.atlassian.jira.issue.attachment.Attachment

def issueManager = ComponentAccessor.getIssueManager()

def currentUserObj = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()

def searchService = ComponentAccessor.getComponent(SearchService)

def aM = ComponentAccessor.getAttachmentManager()

def pM = ComponentAccessor.getAttachmentPathManager()

def issue = issueManager.getIssueByCurrentKey("TEST-49") // where the export will attach too

def attachments = aM.getAttachments(issue)

def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser)

def customFieldManager = ComponentAccessor.getCustomFieldManager()

//start with query

def queries = [

    "Filter for TEST board",

    "Filter for CAKE board",

    "Issues Added to Sprint"] // jql: issueFunction in addedAfterSprintStart("TEST Board","Sprint 1")

queries.each { filterName ->

    //set up stringbuilder for later, define headers

StringBuilder builder = new StringBuilder()

builder.append("Summary|Key|Issue Type|Status|Fix Version|Priority|Radio Button Field|Epic Link|Last Sprint|Story Points|Checkbox Field|Team|Description|Resolved Date\n")

def issueCounter = 0

    def query = jqlQueryParser.parseQuery("filter = '${filterName}'")

    def results = searchService.search(currentUserObj, query, PagerFilter.getUnlimitedFilter())

    def resultCount = results.getTotal()

    //ensure query returns issues

    if(resultCount == 0){

        log.warn("No issues found in query.")

    } else {

        for (def result : results.getResults()){

            //get field values

            def summary = result.summary

            def key = result.key

            def issueType = result.issueType.name

            def status = result.status.getName()

            def fixversionField = result.fixVersions.size()>0 ? result.fixVersions.toString() : ""

            def fixversion = fixversionField.toString().replace("[","").replace("]","")

            def priority = result.priority?.name

            def radioField = customFieldManager.getCustomFieldObjectsByName("Radio Button Custom Field").getAt(0)

            def radio = result.getCustomFieldValue(radioField) != null ? result.getCustomFieldValue(radioField).toString() : ""

            def epic = result.getEpicName() != null ? result.getEpicName() : ""

            def sprint = result.sprints != null ? result.sprints.collect { it.name }.toString().replace("[","").replace("]","") : "Backlog"

            def spField = customFieldManager.getCustomFieldObjectsByName("Story Points").getAt(0)

            def sp = result.getCustomFieldValue(spField) != null ? result.getCustomFieldValue(spField).toString() : ""

            def checkboxField = customFieldManager.getCustomFieldObjectsByName("Checkbox Custom Field").getAt(0)

            def checkbox = result.getCustomFieldValue(checkboxField) != null ? result.getCustomFieldValue(checkboxField).toString().replace("[", "").replace("]", " ") : ""

            def teamField = customFieldManager.getCustomFieldObjectsByName("Custom Team").getAt(0)

            def team = result.getCustomFieldValue(teamField) != null ? result.getCustomFieldValue(teamField).toString().replace("[","").replace("]","") : ""

            def description = result.getDescription().toString().replace("\n",". ").replace("\r",". ").replace("|",";")

            def resolved = result.getResolutionDate().toString()

            //append the rows with jira info

            builder.append(summary + "|")

            builder.append(key + "|")

            builder.append(issueType + "|")

            builder.append(status +"|")

            builder.append(fixversion + "|")

            builder.append(priority + "|")

            builder.append(radio + "|")

            builder.append(epic + "|")

            builder.append(sprint + "|")

            builder.append(sp + "|")

            builder.append(checkbox + "|")

            builder.append(team + "|")

            builder.append(description + "|")

            builder.append(resolved + "\n")

            issueCounter++

        }

         //create new blank file with current date

    def date = new Date().toLocalDate()

    def newFileName = ("sprint-export-" + date.toString() + "-" + filterName + ".txt")

    File outFile = new File("/tmp/" + newFileName)

    outFile.createNewFile()

    outFile.setWritable(true,false)

    outFile.setReadable(true,false)

    outFile.setExecutable(true,false)

    //write builder content into file

    BufferedWriter fileWriter = new BufferedWriter(new FileWriter(outFile))

    try {

        fileWriter.write(builder.toString())

    } catch(Exception e){

        log.error("I/O Exception")

    } finally {

        fileWriter.close()

    }

    //attach file to issue

    issue.addAttachment(outFile)

    log.warn("Total issues: " + issueCounter)

    log.warn("File attached to " + issue + ": " + newFileName)

    }

 }

The job doesn't parse each issue in filter "Issues Added to Sprint". So the export that is saved doesn't show the real total results (the search results will return 70+ but the export only got 62). How do I get Jobs to make sure it parses each issue? 

In my logs, they successfully ran within 4 seconds.

1 answer

1 accepted

1 vote
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.
May 27, 2024

Hi @Diana Gorv

I have reviewed your code, and I can confirm it will not work.

In your code, you have tried to declare the Epic field and Sprint field like this:-

            def epic = result.getEpicName() != null ? result.getEpicName() : ""

            def sprint = result.sprints != null ? result.sprints.collect { it.name }.toString().replace("[","").replace("]","") : "Backlog"

This will fail because the result, i.e. issue variable does not have such a method.

Instead, I suggest using ScriptRunner's HAPI feature and simplifying your code to something like:-

import com.adaptavist.hapi.jira.issues.Issues

def queries = ['Filter for TEST board', 'Filter for CAKE board', 'Issues Added to Sprint']

def date = new Date().toLocalDate()
def builder = new StringBuilder()

builder.append('Summary|Key|Issue Type|Status|Fix Version|Priority|Radio Button Field|Epic Link|Last Sprint|Story Points|Checkbox Field|Team|Description|Resolved Date\n')

queries.each { filterName ->

Issues.search("filter = '${filterName}'").each { issue ->
def summary = issue.summary
def key = issue.key
def issueType = issue.issueType.name
def status = issue.status.name
def fixversionField = issue.fixVersions.toString()
def fixversion = fixversionField.toString() == '[]' ? 'null' : fixversionField.toString().replace('[','').replace(']','')
def priority = issue.priority.name
def radio = issue.getCustomFieldValue('Radio Button Custom Field').toString()
def epic = issue.getCustomFieldValue('Epic Link')
def sprint = issue.getCustomFieldValue('Sprint') != null ? issue.getCustomFieldValue('Sprint')?.state?.last()?.toString() : 'Backlog'
def sp = issue.getCustomFieldValue('Story Points').toString().replace('[', '').replace(']', ' ')
def checkbox = issue.getCustomFieldValue('Checkbox Custom Field').toString().replace('[', '').replace(']', ' ')
def team = issue.getCustomFieldValue('Custom Team').toString().replace('[', '').replace(']', ' ')

def description = issue.description.replace('\n','. ').replace('\r','. ').replace('|',';')
def resolved = issue.resolutionDate.toString()
builder.append("${summary}|${key}|${issueType}|${status}|${fixversion}|${priority}|${radio}|${epic}|${sprint}|${sp}|${checkbox}|${team}|${description}|${resolved}\n")

def newFileName = "/tmp/sprint-export-${date.toString()}-${filterName}.txt".toString()
def file = new File(newFileName)
def fileWriter = new FileWriter(file)
fileWriter.write(builder.toString())
fileWriter.flush()

issue.addAttachment(file)
}
}

Please note that the sample working code above is not 100% exact to your environment. Hence, you will need to modify it accordingly.

I hope this helps solve your question. :-)

Thank you and Kind regards,
Ram

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.
June 5, 2024

Hi @Diana Gorv

Has your question been answered?

If yes, please accept the answer provided.

Thank you and Kind regards,

Ram

Diana
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
June 5, 2024

@Ram Kumar Aravindakshan _Adaptavist_ I tested using the HAPI method and update the Epic Link and Sprint, but now the exports show no results. 

But I checked using another plugin Structure. In Automation, I enter "Filter = 'Issues Added to Sprint'", and Structure returns 0 results. But when I look up that filter or even do the jql query in Search 

issueFunction in addedAfterSprintStart("TEST Board","Sprint 1")

I do get results.

Is there a HAPI method, or any groovy method, that makes the Scriptrunner Job wait for the query to get the filter's results? Since before, it was only return partial results, so I'm wondering if the Job is execute too fast?

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.
June 17, 2024

Hi @Diana Gorv

Please add a log output in the code to check if any of the issues exist first. 

Also, please make a minor modification to the code and include the .update { } parameter as shown below:-

import com.adaptavist.hapi.jira.issues.Issues

def queries = ['Filter for TEST board', 'Filter for CAKE board', 'Issues Added to Sprint']

def date = new Date().toLocalDate()
def builder = new StringBuilder()

builder.append('Summary|Key|Issue Type|Status|Fix Version|Priority|Radio Button Field|Epic Link|Last Sprint|Story Points|Checkbox Field|Team|Description|Resolved Date\n')

queries.each { filterName ->

Issues.search("filter = '${filterName}'").each { issue ->
log.warn "==============================>>>>> Issue: ${issue.key}"
def summary = issue.summary
def key = issue.key
def issueType = issue.issueType.name
def status = issue.status.name
def fixversionField = issue.fixVersions.toString()
def fixversion = fixversionField.toString() == '[]' ? 'null' : fixversionField.toString().replace('[','').replace(']','')
def priority = issue.priority.name
def radio = issue.getCustomFieldValue('Radio Button Custom Field').toString()
def epic = issue.getCustomFieldValue('Epic Link')
def sprint = issue.getCustomFieldValue('Sprint') != null ? issue.getCustomFieldValue('Sprint').last()?.toString() : 'Backlog'
def sp = issue.getCustomFieldValue('Story Points').toString().replace('[', '').replace(']', ' ')
def checkbox = issue.getCustomFieldValue('Checkbox Custom Field').toString().replace('[', '').replace(']', ' ')
def team = issue.getCustomFieldValue('Custom Team').toString().replace('[', '').replace(']', ' ')

def description = issue.description.replace('\n','. ').replace('\r','. ').replace('|',';')
def resolved = issue.resolutionDate.toString()
builder.append("${summary}|${key}|${issueType}|${status}|${fixversion}|${priority}|${radio}|${epic}|${sprint}|${sp}|${checkbox}|${team}|${description}|${resolved}\n")

def newFileName = "/tmp/sprint-export-${date.toString()}-${filterName}.txt".toString()
def file = new File(newFileName)
def fileWriter = new FileWriter(file)
fileWriter.write(builder.toString())
fileWriter.flush()

issue.update {
addAttachment(file)
}

file.delete()
}
}

Let me know how it goes.

Thank you and Kind regards,
Ram

Diana
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
June 20, 2024

@Ram Kumar Aravindakshan _Adaptavist_ 

The logs show the issues are indeed being parsed, and would return 

==============================>>>>> Issue: TEST-123
etc....

But I found one strange discovery.

I run this Scriptrunner Job right before I close a sprint (Sprint 2), and I would get 0 results on the export file, yet the saved filter in Jira does have search results. When I try to run the Job when no sprint is active, the export file returns only partial issue results.

Then I try running the job when the next sprint is active, not the query's sprint, and the export file returns all the issues!

Meaning, my query is "issueFunction in addedAfterSprintStart("TEST Board","Sprint 2")". Sprint 2 has already closed and I had started Sprint 3. I ran that Scriptrunner Job that searched for issues added to Sprint 2, and the export file finally worked.

So in conclusion, the job does work...but I can only run when there's an active sprint, and if my active sprint is NOT the one I'm querying for.

This only happens when using specially the "addedAfterSprintStart" or "removedAfterSprintStart" is in the query for Scriptrunner Job.

For now, I'll take this as my answer and just set my cron job to be after the next sprint has started.

Suggest an answer

Log in or Sign up to answer