Getting Started

JIRA Extension Points

Script Listeners

Script Listeners respond to one or more webhook events. Webhooks are fired after an action takes place in JIRA such as when an issue is updated or a project is created. Using Script Listeners it is possible to perform actions such as creating initial issues for a project or notifying users outside of the standard notification scheme about changes to issues.

Calculated Custom Field

Right now in JIRA Cloud we can’t do a real calculated custom field, but we can update a field in response to an IssueUpdated event, creating a simple calculated custom field. The following example has 3 number custom fields configured. The code is designed to be used with the Issue Updated event

def issue = event.issue
// these 4 values will be installation dependent
def input1CfId = 'customfield_10033'
def input2CfId = 'customfield_10034'
def outputCfId = 'customfield_10035'
def projectKey = "TP"

if (issue == null || issue.fields.project.key != projectKey) { (1)
    logger.info("Wrong Project ${issue.fields.project.key}")
    return
}

def input1 = issue.fields[input1CfId] as Integer (2)
def input2 = issue.fields[input2CfId] as Integer

if (input1 == null || input2 == null) { (3)
    logger.info("Calculation using ${input1} and ${input2} was not possible")
    return
}

def output = input1 + input2

if (output == (issue.fields[outputCfId] as Integer)) { (4)
    logger.info("already been updated")
    return
}

put("/rest/api/2/issue/${issue.key}") (5)
    .header("Content-Type", "application/json")
    .body([
        fields:[
                (outputCfId): output
        ]
    ])
    .asString()
1 First check to see if the issue is in the required project
2 Grab the values of the custom fields from the event
3 Sanity check for null values - the issue may not have a value for any input
4 Here we check to see if the output field already has the calculated value, if it does do nothing. This is important because an IssueUpdated event will fire for the update we are about to perform
5 Update the value of the custom field

Post to HipChat on Version Release

The following example will post to HipChat when a version is released. It is designed to be used with the Version Released event. It demonstrates the use of MarkupBuilder and interacting with outher services

import groovy.xml.MarkupBuilder

def apiToken = "YOUR_SECRET_TOKEN" (1)

def version = event.version

def searchResult = get("/rest/api/2/search") (2)
    .queryString("jql", "fixVersion = ${version.id}")
    .queryString("startAt", 0)
    .queryString("maxResults", 1000)
    .asObject(Map)
    .body

def writer = new StringWriter()
def markupBuilder = new MarkupBuilder(writer)

def writeIssues = { String msg, List<Map> issues ->
    markupBuilder.p ("The following ${issues.size()} issue(s) were $msg in released version ${version.name}") {
        issues.each { issue ->
            ul {
                li {
                    a(href: issue.self.toString(), issue.key)
                    span(" - ${issue.fields.summary}")
                }
            }
        }
    }
} (3)

if (searchResult.total) { (4)
    // group by resolved, unresolved
    searchResult.issues.groupBy { it.resolution as boolean }.sort { !it.key }.each { issueSet ->
        if (issueSet.key) {
            writeIssues("resolved", issueSet.value)
        } else {
            writeIssues("NOT resolved", issueSet.value)
        }
    }
}
else {
    markupBuilder.p("No issues were included in released version: ${version.name}.")
}

Unirest.clearDefaultHeaders() (5)
post("https://api.hipchat.com/v2/room/test room/notification")
        .header("Content-Type", "application/json")
        .queryString('auth_token', apiToken)
        .body([
                message       : writer.toString(),
                notify        : false,
                message_format: "html"
        ])
        .asString() (6)
1 Generate a HipChat API token and use it here
2 Search for all issues that had the released version as a fixVersion, setting maxResults to e the maximum possible. It is assumed that no more than 1000 issues are in this version.
3 Build a HTML list of the issues that were released
4 Finish up the HTML with a summary of the number of issues that were released
5 Clear the Unirest default headers to remove the JWT token header
6 Make the POST request to hipchat with the auth_token

Create Issues on Project Creation

When a project is created it can sometimes be desirable to create a set of setup issues to say create a confluence space or produce some project documentation. The following example creates issues on a Project Created event. It contains examples of creating individual issues and creating issues in bulk.

def project = event.project
def projectKey = project.key as String
logger.info(projectKey)

def issueTypesReq = get("/rest/api/2/issuetype").asObject(Map) (1)
assert issueTypesReq.status == 200
def taskType = issueTypesReq.body.find { it.name = "Task" }
assert taskType // Must have an issue type named Task

// create two issues

post("/rest/api/2/issue") (2)
    .header("Content-Type", "application/json")
    .body(
        [
                fields: [
                        summary    : "Create Confluence space associated to the project",
                        description: "Don't forget to do this!.",
                        project    : [
                                id: project.id
                        ],
                        issuetype  : [
                                id: taskType.id
                        ]
                ]
        ])
    .asString()

post("/rest/api/2/issue")
    .header("Content-Type", "application/json")
    .body(
        [
                fields: [
                        summary    : "Bootstrap connect add-on",
                        description: "Some other task",
                        project    : [
                                id: project.id
                        ],
                        issuetype  : [
                                id: taskType.id
                        ]
                ]
        ])
    .asString()

// example of bulk update:
post("/rest/api/2/issue")
    .header("Content-Type", "application/json")
    .body(
        [
                issueUpdates: [ (3)
                        [
                                fields: [
                                        summary    : "Bulk task one",
                                        description: "First example of a bulk update",
                                        project    : [
                                                id: project.id
                                        ],
                                        issuetype  : [
                                                id: taskType.id
                                        ]
                                ]
                        ],
                        [
                                fields: [
                                        summary    : "Bulk task two",
                                        description: "2nd example of a bulk update",
                                        project    : [
                                                id: project.id
                                        ],
                                        issuetype  : [
                                                id: taskType.id
                                        ]
                                ]
                        ]
                ]
        ])
    .asString()
1 First get all Issue Types to find the Task type, we will use this id to create all our tasks
2 Create a single issue of type task
3 Note in the bulk update we use instead of using a single fields structure we nest fields inside of issueUpdates

Email Notify on Priority Change

When particular fields change it might be necessary to notify users who wouldn’t usually get notified, with some specific text to grab some attention. The following example shows how to send a notification by email to all interested parties (reporter, assignee, watchers, voters), the user admin and the jira-administrators group when the priority is changed to Highest. This functionality is useful to send messages beyond those that are usually sent by JIRA.

import groovy.xml.MarkupBuilder
def issue = event.issue

def priorityChange = get(issue.self) (1)
    .queryString('expand', 'changelog')
    .asObject(Map)
    .body
    .changelog
    .histories
    .last()
    .items
    .find { it['field'] == 'priority' }

if (priorityChange?.to as Integer == 1) { (2)
    def writer = new StringWriter()
    def markupBuilder = new MarkupBuilder(writer)
    markupBuilder.div { (3)
        p {
            // update url below:#x
            a(href: "http://myjira.atlassian.net/issue/${issue.key}", issue.key) (4)
            span(" has had priority changed from ${priorityChange.fromString} to ${priorityChange.toString}")
        }
        p("You're important so we thought you should know")
    }
    def htmlMessage = writer.toString()
    def textMessage = new XmlSlurper().parseText(htmlMessage).text() (5)

    def resp = post("/rest/api/2/issue/${issue.id}/notify") (6)
        .header("Content-Type", "application/json")
        .body([
            subject: 'Priority Increased',
            textBody: textMessage,
            htmlBody: htmlMessage,
            to: [
                    reporter: issue.fields.reporter != null, // bug - 500 error when no reporter
                    assignee: issue.fields.assignee != null, // bug - 500 error when no assignee
                    watchers: true,
                    voters: true,
                    users: [[
                        name: 'admin'
                    ]],
                    groups: [[
                            name: 'jira-administrators'
                    ]]
            ]
        ])
        .asString()
    assert resp.status == 204
}
1 The issue object passed with the event doesn’t have the changelog, so we need to fetch it again to get it. Once we have it we find the last history change, and then find any items where the priority field changed
2 Only do anything here if the priority change was to Highest
3 Generate a HTML message using MarkupBuilder
4 The issue URI here is created manually. It should be updated
5 Quick hack to produce a text only message from the HTML string
6 Post to the Notify endpoint. Note there is a JIRA bug where a missing reporter or assignee will cause a 500 error. The example here is of sending the message to the reporter and assignee, any watchers and voters as well as a user and group list