Why should I use ScriptRunner?

Using Groovy scripts in Jira Software/Core or Service Management (Server/Data Center) adds a huge amount of new possibilities in customizing workflows, enhancing the user experience and automating processes. The most powerful App in this area is ScriptRunner for Jira. Out of the box it allows you to use Groovy scripts in:

  • Workflow Transitions (Post functions, Conditions, Validators)
  • Custom Script Listeners
  • Custom REST-Endpoints
  • Custom Jobs
  • Behaviours (manipulating field behaviour on create, edit and transition screens)
  • Custom JQL Functions
  • Fragments (manipulating Jira’s user interface)
  • and some useful built-in scripts

By default Jira’s data model is quite static. Also you need to be Jira Administrator to modify fields. In combination with other apps you are able to provide a completely new Jira experience. For example, using the Jira app Insight - Asset Management you can define asset objects and access them in your scripts. By that you can make Jira behave more dynamic.

How to use this blog article?

In this article I will share script snippets for ScriptRunner to handle some common user related use cases I got in touch with. I don’t go into the details of the use cases. The purpose of the snippets is to get you started. Use copy & paste at your own discretion.

Import packages

First of all you import all the packages you’ll need in your script context. For debugging it’s helpful to import org.apache.log4j.Logger classes to be able to write into the Jira logs. You will soon recognize that developing in the integrated ScriptRunner script console without custom logging is quite shitty.

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.event.type.EventDispatchOption
import org.apache.log4j.Logger

def log = Logger.getLogger("com.acme.workflows")

log.info("Script starts...")

For providing detailed debug information I recommend to use the appropriate log4j log level log.debug(”custom debugging message”). Whether the log.debug statement will appear in the logs, depends on the configured log level of your Jira instance. There is even a webinterface for configuring log levels in Jira Server and Data Center.

Getting the issue object

ScriptRunner scripts can be used in different contexts (listeners, postfunctions, …). How to access the issue object depends on the context of your script. Here some common examples.

Getting the issue object in a listener script

Issue issue = event.issue

Getting the issue object in script postfunctions

In a ScriptRunner script postfunction you don’t need to fetch the issue object of the current issue. It already exists as a prepopulated variable with name issue. You can directly access it to e.g. get the issue key by using issue.getKey().

Getting the issue object by the issue key

Independent of the context, you can always get the issue object by the issue key.

import com.atlassian.jira.component.ComponentAccessor

def issueManager = ComponentAccessor.getIssueManager()

String issueKey = "TEST-1";

issue = issueManager.getIssueObject(issueKey)

User related script examples

Now, everything is prepared and you can start using the following script snippets. They’re all tested in Jira 8.5.x (Long Term Support release) with ScriptRunner 6.15.0 and Insight - Asset Management 8.6.15.

Setting the current user as assignee

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.event.type.EventDispatchOption

def authContext = ComponentAccessor.getJiraAuthenticationContext()

def currentUser = authContext.getLoggedInUser()

// Example: populate the assignee field using a setter method
issue.setAssignee(currentUser)

// Example: populate the reporter field using a property accessor
issue.reporter = currentUser

// More complex example: populate a customfield
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def myCustomfield = customFieldManager.getCustomFieldObject("customfield_xxxxx")
issue.setCustomFieldValue(myCustomfield, currentUser)

// Commit changes to issue
ComponentAccessor.getIssueManager().updateIssue(currentUser, issue, EventDispatchOption.DO_NOT_DISPATCH, false)

We use several classes from the Jira Java API here:

In the examples you see two different styles to access a field.

Performing actions on behalf of another user

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.event.type.EventDispatchOption

def customFieldManager = ComponentAccessor.getCustomFieldManager()
def authContext = ComponentAccessor.getJiraAuthenticationContext()
def userManager = ComponentAccessor.getUserUtil()

// Store current user for later reset
def currentUser = authContext.getLoggedInUser()

// Switch user
def otherUser =  userManager.getUserByName("SomeUser")
authContext.setLoggedInUser(otherUser)

// Do something, e.g. manipulate issue

// Commit changes
ComponentAccessor.getIssueManager().updateIssue(otherUser, issue, EventDispatchOption.DO_NOT_DISPATCH, false)

// Reset user
authContext.setLoggedInUser(currentUser)

Make sure you’re reseting the user at the end of your script. Otherwise, the following scripts or postfunctions may use this context, too.

Adding a user to the request participants in Jira Service Management

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.user.ApplicationUser

def customFieldManager = ComponentAccessor.getCustomFieldManager()
def authContext = ComponentAccessor.getJiraAuthenticationContext()
def userManager = ComponentAccessor.getUserUtil()

def currentUser = authContext.getLoggedInUser()

// Customfield 10001 corresponds to the request participant field in our Jira instance
def requestParticipantsField = customFieldManager.getCustomFieldObject("customfield_10001")

def user = userManager.getUserByName("username")

// We need an ArrayList because we are going to modify it
ArrayList<ApplicationUser> requestParticipants = issue.getCustomFieldValue(requestParticipantsField)

requestParticipants.add(user)

issue.setCustomFieldValue(requestParticipantsField, requestParticipants)

ComponentAccessor.getIssueManager().updateIssue(currentUser, issue, EventDispatchOption.DO_NOT_DISPATCH, false)

ArrayList is an implementation of java.util.List, that supports the .add(...) operation. Other list implementations will throw an UnsupportedOperationException.

Getting the users from multiple groups

import com.atlassian.jira.component.ComponentAccessor 

def groupManager = ComponentAccessor.getGroupManager()

def users = []
def groups =  ["jira-administrators", "jira-service-desk-users"]

// If a user is in both groups, they would be added twice
for (group in groups) {
    users += groupManager.getUsersInGroup(group)
}

// Remove duplicate users
users = users.unique()

Setting approvers by group membership in Jira Service Management

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.ModifiedValue

def groupManager = ComponentAccessor.getGroupManager()
def changeHolder = new DefaultIssueChangeHolder
def userManager = ComponentAccessor.getUserManager()
def customFieldManager = ComponentAccessor.getCustomFieldManager()

def users = []
def groups =  ["jira-administrators", "jira-service-desk-users"]

// If a user is in both groups, they would be added twice
for (group in groups) {
    users += groupManager.getUsersInGroup(group)
}

// Remove duplicate users
users = users.unique()

def approvers = []

for (user in users) {
    ApplicationUser approver = userManager.getUserByKey(user.getKey())
    approvers.add(approver)
}

// Customfield 10006 corresponds to a multiple user field which we are using for the approvers
if (!approvers.isEmpty()) {
    def approverField = customFieldManager.getCustomFieldObject("customfield_10006")
    // Commit changes to field
    approverField.updateValue(null, issue, new ModifiedValue(issue.getCustomFieldValue(approverField), approvers), changeHolder)
}

Getting Jira groups from Insight object attributes of the type Group

import com.atlassian.jira.component.ComponentAccessor

// Access Insight Java API
Class iqlFacadeClass = ComponentAccessor.getPluginAccessor().getClassLoader().findClass("com.riadalabs.jira.plugins.insight.channel.external.api.facade.IQLFacade")
def iqlFacade = ComponentAccessor.getOSGiComponentInstanceOfType(iqlFacadeClass)
Class objectFacadeClass = ComponentAccessor.getPluginAccessor().getClassLoader().findClass("com.riadalabs.jira.plugins.insight.channel.external.api.facade.ObjectFacade")
def objectFacade = ComponentAccessor.getOSGiComponentInstanceOfType(objectFacadeClass)

// Defining the Insight object information
def objectKey = "ST-12345"
def objectSchemaID = 1
def attributeName = "someGroupAttributeName"
def groups = []

// Get the matching objects from Insight. In this case we are looking for a single object by its unique object key
def insightObjects = iqlFacade.findObjectsByIQLAndSchema(objectSchemaID, "Key = \"" + objectKey + "\"")

for (object in insightObjects) {
    def groupAttribute = objectFacade.loadObjectAttributeBean(object.getId(), attributeName)

    if (groupAttribute != null) {
        def groupAttributeValues = groupAttribute.getObjectAttributeValueBeans()
        if (groupAttributeValues != null) {
            for (group in groupAttributeValues) {
                groups.add(group.getValue())
            }
        }
    }
}

Seting an issue comment with specific user context e.g. a system user

import com.atlassian.jira.component.ComponentAccessor

def commentManager = ComponentAccessor.getCommentManager()
def userManager = ComponentAccessor.getUserManager()
def customFieldManager = ComponentAccessor.getCustomFieldManager()

def user =  userManager.getUserByName("username")

// Set comment text e. g. with parsing a customfield value
def myField = customFieldManager.getCustomFieldObject("customfield_XXXXX")

def commentText = "Text pre " + issue.getCustomFieldValue(myField).toString() + " Text post"

// Commit comment to issue
commentManager.create(issue, user, commentText, true)

Further Groovy script use cases within Jira / Jira Service Management

You can cover quite a distance using this approach. Just to give you some inspiration, here is a couple of other interesting ScriptRunner and Insight Groovy script use cases:

  • Simple password generator within transition post function
  • Access databases to acquire external information and bring them into Jira
  • Call a REST interface from workflow transition post functions and handle its response
  • Using Insight objects linked to an issue or issuetype to:
    • provide texts, comparable to canned responses, in any textfield e.g. resolution text or processing hints without buying another app
    • run validity checks across multiple fields even bundled fields from Dynamic Forms/Extension for Jira Service Management
    • build dynamic forms using ScriptRunner behaviours and accessing Insight object attributes to manipulate the field behaviour on screens
    • control available workflow transitions by linked Insight objects

If you want more information just leave me a comment or get in touch.





Contact us