Getting Started

JIRA Extension Points

Atlassian are updating their Cloud REST APIs in response to the much publicized GDPR legislation.

Customers using ScriptRunner for Jira Cloud must update any scripts that inspect or modify user fields when interacting with the Cloud REST APIs.

The full details of the changes Atlassian are making can be found on their Deprecation notice and migration guide for major changes to Jira Cloud REST APIs to improve user privacy.

What is changing?

Atlassian are removing the userkey and username from their REST APIs, webhooks and JQL.

From the 29th March 2019 it will no longer be possible to identify a user based on their username or userkey.

Additionally there will be new privacy settings that each user may use to control whether their display name and email address are available to addons.

What do I need to do?

  1. If your scripts refer to a user by their username or userkey (e.g. when setting the value of a user field on an issue), those scripts need updating to use accountIds instead.

  2. If you run a JQL query from within your script, and that query refers to a user field, that query must be updated to use accountIds instead of userkeys or usernames.

  3. If your scripts read/inspect the value of a user field in order to make a decision, you must update your script to only inspect the accountId property of the user field.

  4. If your scripts make REST API calls to endpoints that take a username or userkey as a query parameter, those REST API requests must be updated to use the new query parameter for accountId as documented in the migration guide.

  5. If your scripts read the email address of users, those scripts need to be modified to handle null email addresses (because individual users may change their privacy settings so that ScriptRunner cannot view the email address)

The system user fields for issues are: creator, assignee, reporter

Additionally: comment authors, project leads, component leads, component assignee, filter owners, filter shared users, group members and attachment authors are also affected by this change.

In other words, only the accountId will be a valid way to set the user on one of those Jira entities, and only the accountId will be a guaranteed way to identify the user specified for that Jira entity.

Can Adaptavist help me?

Yes, we have released a migration tool within ScriptRunner that tells you which scripts we think you need to update and which user fields those scripts reference.

If you have scripts that we think need updating, you’ll see this banner in the ScriptRunner admin section of your Jira Cloud instance.

gdpr banner

Once you click on the 'Show scripts' link at the bottom of the banner, you’ll see this modal dialog which will tell you which scripts we think need updating and which user-fields those scripts reference. You can click on the name of a script to go to the edit page for that script.

gdpr dialog

How do I make the changes?

Here a few examples of scripts before and after the GDPR migration that you need to do. You can find accountIds by searching for users in the /people section of your Jira Cloud instance.

Inspecting user details on system fields

// Before 29th March 2019

logger.info("${issue.field.reporter.name} reported issue ${issue.key}")
logger.info("${issue.field.creator.name} created issue ${issue.key}")
logger.info("${issue.field.assignee.name} is assigned to issue ${issue.key}")

/* -------------------------------------------------------------------------------- */

// After 29th March 2019 the name and key properties on reporter, creator and assignee will no longer exist

logger.info("${issue.field.reporter.displayName} reported issue ${issue.key}")
logger.info("${issue.field.creator.displayName} created issue ${issue.key}")
logger.info("${issue.field.assignee.displayName} is assigned to issue ${issue.key}")

// or

logger.info("${issue.field.reporter.accountId} reported issue ${issue.key}")
logger.info("${issue.field.creator.accountId} created issue ${issue.key}")
logger.info("${issue.field.assignee.accountId} is assigned to issue ${issue.key}")

Inspecting user details on custom fields

// Before 29th March 2019

logger.info("${issue.field.customfield_10234.name} approved this request")

def approvedCustomFieldId = get("/rest/api/2/field")
        .asObject(List)
        .body
        .find { it.custom && it.name == "Approved" }
        .id
def approved = issue.field[approvedCustomFieldId]
logger.info("${approved.name} approved this request")

/* -------------------------------------------------------------------------------- */

// After 29th March 2019 the name and key properties on custom user fields will no longer exist

logger.info("${issue.field.customfield_10234.displayName} approved this request")

def approvedCustomFieldId = get("/rest/api/2/field")
        .asObject(List)
        .body
        .find { it.custom && it.name == "Approved" }
        .id
def approved = issue.field[approvedCustomFieldId]
logger.info("${approved.displayName} approved this request")

// or

logger.info("${issue.field.customfield_10234.accountId} approved this request")

def approvedCustomFieldId = get("/rest/api/2/field")
        .asObject(List)
        .body
        .find { it.custom && it.name == "Approved" }
        .id
def approved = issue.field[approvedCustomFieldId]
logger.info("${approved.accountId} approved this request")

Setting a user field on an issue

// Before 29th March 2019

def assignee = "bsimpson"

put("/rest/api/2/issue/${issue.key}")
        .header('Content-Type', 'application/json')
        .body([
            fields: [
                assignee: [
                    key: assignee
                ]
            ]
        ])
        .asObject(Map)

/* -------------------------------------------------------------------------------- */

// After 29th March 2019 you must use accountIds to identify users when updating/setting user fields

// this is Bart Simpson's accountId
def assignee = "123450:pqrst-98765-uvwxyz-43210-abc"

put("/rest/api/2/issue/${issue.key}")
        .header('Content-Type', 'application/json')
        .body([
            fields: [
                assignee: [
                    id: assignee
                ]
            ]
        ])
        .asObject(Map)

Searching using JQL that contains a user reference

// Before 29th March 2019

def query = "project = ${issue.fields.project.key} AND reporter = bsimpson"

def resp = get("/rest/api/2/search")
    .queryString("jql", query)
    .queryString("fields", 'priority')
    .asObject(Map)
    .body

logger.info("Found ${resp.total} issues assigned to Bart Simpson")

/* -------------------------------------------------------------------------------- */

// After 29th March 2019 JQL queries will only support accountIds to identify users

def query = "project = ${issue.fields.project.key} AND reporter = 123450:pqrst-98765-uvwxyz-43210-abc"

def resp = get("/rest/api/2/search")
        .queryString("jql", query)
        .queryString("fields", 'priority')
        .asObject(Map)
        .body

logger.info("Found ${resp.total} issues assigned to Bart Simpson")

Inspecting user email addresses

// Before 29th March 2019

def issueKey = 'GDPR-123'
def firstComment = get("/rest/api/3/issue/${issueKey}/comment")
    .asObject(Map)
    .body
    .comments[0]

def commentAuthorEmail = firstComment.author.emailAddress
def commentUpdaterEmail = firstComment.updateAuthor.emailAddress

logger.info("The first comment on ${issueKey} was written by ${commentAuthorEmail} and updated by ${commentUpdaterEmail}")

/* -------------------------------------------------------------------------------- */

// After 29th March 2019 email addresses may be null, depending on users' privacy settings

def issueKey = 'GDPR-123'
def firstComment = get("/rest/api/3/issue/${issueKey}/comment")
        .asObject(Map)
        .body
        .comments[0]

def commentAuthorEmail = firstComment.author.emailAddress ?: 'email address hidden'
def commentUpdaterEmail = firstComment.updateAuthor.emailAddress ?: 'email address hidden'

logger.info("The first comment on ${issueKey} was written by ${commentAuthorEmail} and updated by ${commentUpdaterEmail}")