Example Custom Scripts
Calculated Custom Field
This example is similar to the calculated field example for Script Listeners except in this case, the calculation occurs when the issue transitions to a specific status in a workflow.
// get custom fields
def customFields = get("/rest/api/2/field")
.asObject(List)
.body
.findAll { (it as Map).custom } as List<Map>
def input1CfId = customFields.find { it.name == 'Custom Field 1' }?.id
def input2CfId = customFields.find { it.name == 'Custom Field 2' }?.id
def outputCfId = customFields.find { it.name == 'Output Custom Field' }?.id
def input1 = issue.fields[input1CfId] as Integer (1)
def input2 = issue.fields[input2CfId] as Integer
if (input1 == null || input2 == null) {
logger.info("Calculation using ${input1} and ${input2} was not possible")
return
}
def output = input1 + input2
if (output == (issue.fields[outputCfId] as Integer)) {
logger.info("already been updated")
return
}
put("/rest/api/2/issue/${issue.key}")
// .queryString("overrideScreenSecurity", Boolean.TRUE)
.header("Content-Type", "application/json")
.body([
fields:[
(outputCfId): output
]
])
.asString()
Line 1: Grab the values of the custom fields from the event.
Line 14: Sanity check for null values - the issue may not have a value for any input.
Line 20: Here we check if the output field already has the calculated value, if it does, we do nothing. This is important because an IssueUpdated event will fire for the update we are about to perform.
Line 24: Update the value of the custom field.
Line 25: If using the Add-on user to run the script, it is possible to set the overrideScreenSecurity property to modify fields that are not on the current screen.
Create Sub-Task
Run this code in the Script Console to create a sub-task.
groovy// Here we specify and retrieve the details of the parent issue // If you copied this code into a Post Function or an issue-related Script Listener you could remove // the first 5 lines of code as an issue variable would already be available to your script def parentKey = 'DEMO-1' def issueResp = get("/rest/api/2/issue/${parentKey}") .asObject(Map) assert issueResp.status == 200 def issue = issueResp.body as Map // We retrieve all issue types def typeResp = get('/rest/api/2/issuetype') .asObject(List) assert typeResp.status == 200 def issueTypes = typeResp.body as List<Map> // Here we set the basic subtask issue details def summary = "Subtask summary" def issueType = "Sub-task" def issueTypeId = issueTypes.find { it.subtask && it.name == issueType }?.id assert issueTypeId : "No subtasks issue type found called '${issueType}'" def createDoc = [ fields: [ project: (issue.fields as Map).project, issuetype: [ id: issueTypeId ], parent: [ id: issue.id ], summary: summary ] ] // Now we create the subtask def resp = post("/rest/api/2/issue") .header("Content-Type", "application/json") .body(createDoc) .asObject(Map) def subtask = resp.body assert resp.status >= 200 && resp.status < 300 && subtask && subtask.key != null subtask
Post to Slack
Use this example to send a simple Slack post, notifying users that an issue has been updated.
groovydef apiToken = 'YOUR_SECRET_TOKEN' def msg = [ channel: 'your-channel-here', username: 'Any Username Here', icon emoji: ':rocket:', text: "${issue.key} Updated", ] post('https://slack.com/api/chat.postMessage') .header('Content-Type', 'application/json') .header(Authorization', "Bearer ${apiToken}") .body(msg) .asString()
Line 1: Generate a Slack API token and use it here. This API token should be saved as a script variable.
Line 4: Add the name of the channel you wish to post to.
Line 5: Enter the username ScriptRunner will post as.
Line 7: Enter the notification text.
Line 10: Post the message to Slack.
Link to an Issue
Automatically link the issue being transitioned to another issue.
groovydef issueId = issue.id def link = post('/rest/api/2/issueLink') .header('Content-Type', 'application/json') .body([ type: [ name: "Blocks" ], outwardIssue: [ id: issueId ], // This is the issue that the link 'starts' at inwardIssue: [ key: 'EX-1' ] // You'll need to specify an issue ID or key here ]) .asString() assert link.status == 201
Add a Customer Facing Comment on a Linked Service Management
This script allows you to add a comment on a linked Jira Service Management Cloud project after the transition of an issue. For example, you may want to let a bug reporter know that the related development is marked as complete.
We advise you run this script as the ScriptRunner Add-on User.
groovy// Define a counter which will be the number of support tickets updated by this script def counter = 0 // Define the Service Desk Issuetypes you only want updated if a linked Jira software issue is closed def serviceDeskIssueTypes = [ "Incident", "Feature Request" ] // Find the linked issues def linkedIssues = issue.fields.issuelinks // Loop over each linked issue linkedIssues.each { // For each linked issue, comment only on Service Desk Tickets if (serviceDeskIssueTypes.contains(it.outwardIssue.fields.issuetype.name)) { def commentResp = post("/rest/api/2/issue/${it.outwardIssue.key}/comment") .header('Content-Type', 'application/json') .body([ body: """ Good news! We've now completed the development work related to this ticket. """ // Define on the line above what you want the comment to look like to customers on the Service Desk ticket ]) .asObject(Map) counter ++ // Log info about which Service Desk ticket was just updated logger.info("Issue ${issue.key} has been closed. \nService Desk ticket ${it.outwardIssue.key} has been commented to inform affected customers.") } } return "The script finished successfully. ${counter} support ticket/s were commented on."
Add a comment to linked issues when this issue is transitioned
groovyfinal issueKey = issue.key // Make a rest call to return the Issue def issue = get("/rest/api/2/issue/${issueKey}") .header('Content-Type', 'application/json') .asObject(Map) .body as Map def fields = issue.fields as Map // Find the linked issues def linkedIssues = fields.issuelinks as List<Map> // Loop through the linked issues to add comment to inward issues linkedIssues.forEach { Map link -> def linkedKey = link.inwardIssue.key as String def commentResp = post("/rest/api/2/issue/${linkedKey}/comment") .header('Content-Type', 'application/json') .body([ body: """Parent issue ${issueKey} has been transitioned to ${fields.status.name}.""" ]) .asObject(Map) }
Add the current user as a watcher
groovyfinal issueKey = issue.key def result = get('/rest/api/2/user/assignable/search') .queryString('issueKey', "${issueKey}") .header('Content-Type', 'application/json') .asObject(List) def usersAssignableToIssue = result.body as List<Map> def currentUser = get('/rest/api/3/myself') .asObject(Map) .body usersAssignableToIssue.forEach { Map user -> def displayName = user.displayName as String logger.info(displayName + " : " + currentUser) if (displayName == currentUser.displayName) { logger.info("Match") def watcherResp = post("/rest/api/2/issue/${issueKey}/watchers") .header('Content-Type', 'application/json') .body("\"${currentUser.accountId}\"") .asObject(List) if (watcherResp.status == 204) { logger.info("Successfully added ${displayName} as watcher of ${issueKey}") } else { logger.error("Error adding watcher: ${watcherResp.body}") } } }
Add a comment to this issue
groovyfinal issueKey = issue.key def commentResp = post("/rest/api/2/issue/${issueKey}/comment") .header('Content-Type', 'application/json') .body([ body: """This is an issue comment on transition.""" ]) .asObject(Map)
Clear field(s) post function
groovydef issueKey = issue.key def fields = ["customfield_10047","customfield_10007","customfield_10040"] fields.forEach { field -> def clear = put('/rest/api/2/issue/' + issueKey) .header('Content-Type', 'application/json') .body([ fields:[ (field): null ] ]).asString() }