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.
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
Hi @Diana Gorv
Has your question been answered?
If yes, please accept the answer provided.
Thank you and Kind regards,
Ram
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@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?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
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
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@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.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.