Use the Dynamic Forms feature to simplify the process of adding variables to your ScriptRunner Groovy scripts. Dynamic Forms allows you to create complex scripts with flexible variables that can be shared with multiple users, allowing one script to be used for various use cases.

Inline scripts are often copied and pasted, with minor changes made for different use cases. This approach requires maintenance for each usage of the script. Using Dynamic Forms, create flexible scripts with annotated variables that can be stored as files, reducing maintenance requirements while allowing for script customisation.

Annotate your variables to have them appear as form fields when a user edits a script. These annotations allow variable values within a script to be changed easily by those with limited code familiarity.

Dynamic forms annotations are available anywhere you can enter code in ScriptRunner, including the Script Console, Conditions, Validators, Post Functions, Behaviours, and many more.

For example, as a power user, you have created a script to delete issues within a project with a specific assignee. This script is used across multiple locations, varying by one variable (User) for each use case. Instead of hardcoding the User variable, you can now use a dynamic form annotation, meaning the user can be changed without needing to re-write the script. As well as being able to edit the script quickly, you can maintain the code in one centralised location. The dynamic form annotation shows the User variable as a form field:

100%

The above example uses the following code:

import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.search.SearchProvider
import com.atlassian.jira.jql.parser.JqlQueryParser
import com.atlassian.jira.web.bean.PagerFilter
import com.onresolve.scriptrunner.parameters.annotation.*

@UserPicker(label = "Assignee", description = "Issues with this assignee will be permanently deleted")
ApplicationUser user

// issues returned from that JQL will get deleted
final String searchQuery = "assignee = $user.name"

def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser)
def searchProvider = ComponentAccessor.getComponent(SearchProvider)
def loggedInUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def issueService = ComponentAccessor.issueService
def query = jqlQueryParser.parseQuery(searchQuery)
def searchResults = searchProvider.search(query, loggedInUser, PagerFilter.unlimitedFilter)

searchResults.getIssues().each { issue ->
    def deleteValidationResult = issueService.validateDelete(loggedInUser, issue.id)
    assert deleteValidationResult.isValid() : deleteValidationResult.errorCollection

    def deleteResult = issueService.delete(loggedInUser, deleteValidationResult)
    assert deleteResult
}

The following dynamic form field types are available:

Name Description Target Type

User Picker

Field allowing user selection.

com.atlassian.jira.user.ApplicationUser

Field Picker

Field to select any system or custom field.

com.atlassian.jira.issue.fields.Field

Short Text

Field allowing a short text input.

String

Select List

Single-select list field.

String

Checkbox

A checkbox field.

Boolean

Project Picker

Field allowing project selection.

com.atlassian.jira.project.Project

We are in the process of adding more dynamic form field types.

Create a Dynamic Form

  1. Click the Cog in the top ribbon, and open the Add-ons page.

  2. Select Script Console under ScriptRunner.

  3. Write your new script in the Script field, annotating the variables you want users to provide (see the Examples section below). Annotating variables allows them to be edited depending on requirements when running the script. These variables show as editable fields above the Script Console.

Transforming an Existing Inline Script

To enable sharing of annotated scripts, all inline scripts must be saved as files.

  1. Navigate to your existing inline script, and add in required annotations.

  2. Copy the script.

  3. Use the Script Editor to open your Scripts Root folder.

  4. Select the folder in which you want to save the script, and click the Create New File icon.

  5. Enter a file name in the Add New Groovy File/Add New Groovy Folder window.

  6. Click Add.

  7. Paste your inline script into the file, and click Save. This script is now available as a file and can be shared with multiple Jira users on the same instance.

Annotations

User Picker

Add a username picker field into your script.

import com.atlassian.jira.user.ApplicationUser
import com.onresolve.scriptrunner.parameters.annotation.*

@UserPicker(label = "Select a User", description = "Select a user on the UserPicker")
ApplicationUser userSelected

Field Picker

Add a field picker into your script. The field picker lets you pick from any fields (system or custom).

import com.onresolve.scriptrunner.parameters.annotation.*
import com.atlassian.jira.issue.fields.Field

@FieldPicker(description = "Select a field on the FieldPicker", label = "Select a field")
Field field

Short Text

Add a short text field to a script.

import com.onresolve.scriptrunner.parameters.annotation.*

@ShortTextInput(description = "Text Input Description", label = "Text input label")
String shortTextInputValue

Select List

Add a single-select list with configurable options.

import com.onresolve.scriptrunner.parameters.annotation.*
import com.onresolve.scriptrunner.parameters.annotation.meta.Option

@Select(
    description = "Select description",
    label = Select label",
    options =
        [
            @Option(label = "Option 1 Label", value = "option1Value"),
            @Option(label = "Option 2 Label", value = "option2Value")
        ]
)
String selectOptionSelected

Checkbox

Add a checkbox to a script.

import com.onresolve.scriptrunner.parameters.annotation.*

@Checkbox(description = "Check the checkbox to true or unselect to false", label = "Checkbox label")
boolean isChecked

All Fields

Use several annotators to create a dynamic form.

import com.atlassian.jira.issue.fields.Field
import com.atlassian.jira.user.ApplicationUser
import com.onresolve.scriptrunner.parameters.annotation.Checkbox
import com.onresolve.scriptrunner.parameters.annotation.FieldPicker
import com.onresolve.scriptrunner.parameters.annotation.ShortTextInput
import com.onresolve.scriptrunner.parameters.annotation.UserPicker
import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import groovy.json.JsonBuilder
import groovy.transform.BaseScript

import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response

String result = ""

@FieldPicker(description = "FieldPickerDescription", label = "FieldPickerLabel")
Field field
result += field.getName() + " -- " + field.getId() + " -- " + field.getNameKey()

@ShortTextInput(description = "Dynamic Text Input Description", label = "Dynamic text input label")
String shortTextInput
result += shortTextInput

@Checkbox(description = "Check the checkbox to true or unselect to false", label = "Dynamic Checkbox label")
boolean selected

@UserPicker(label = "Select a User", description = "Select a user on the UserPicker")
ApplicationUser user

log.warn result
@BaseScript CustomEndpointDelegate delegate
doSomething(httpMethod: "GET", groups: ["jira-administrators"]) { MultivaluedMap queryParams, String body ->
    return Response.ok(new JsonBuilder([user: user.name, fieldPicker: field.getName(), shortTextInput: shortTextInput]).toString()).build()
}

Examples

Create an Issue

The following example shows the use of dynamic form annotations in a script which creates a new issue. A short text annotator is used for Project Key and Summary, select list annotator for Issue Type and Priority, and user picker for User.

import com.atlassian.jira.component.ComponentAccessor
import com.onresolve.scriptrunner.parameters.annotation.*
import com.onresolve.scriptrunner.parameters.annotation.meta.Option
import com.atlassian.jira.user.ApplicationUser

@ShortTextInput(description = "Project key", label = "Project")
String projectKey

@Select(
    description = "The issue type for the new issue",
    label = "Issue type",
    options =
        [
            @Option(label = "Bug", value = "Bug"),
            @Option(label = "Story", value = "Story")
        ]
)
String issueTypeName

@Select(
    description = "The priority of the new issue",
    label = "Priority",
    options =
        [
            @Option(label = "Major", value = "Major"),
            @Option(label = "Minor", value = "Minor")
        ]
)
String priorityName

@UserPicker(label = "User", description = "User with that user key will be the reporter of the issue")
ApplicationUser user
def reporterKey = user.name

@ShortTextInput(description = "The summary for the new issue", label = "Summary")
String summary

def issueService = ComponentAccessor.issueService
def constantsManager = ComponentAccessor.constantsManager
def loggedInUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def project = ComponentAccessor.projectManager.getProjectObjByKey(projectKey)
assert project: "Could not find project with key  $projectKey"

def issueType = constantsManager.allIssueTypeObjects.findByName(issueTypeName)
assert issueType: "Could not find issue type with name  $issueTypeName"

// if we cannot find user with the specified key or this is null, then set as a  reporter the logged in user
def reporter = ComponentAccessor.userManager.getUserByKey(reporterKey) ?: loggedInUser

// if we cannot find the priority with the given name or if this is null, then set the default priority
def priority = constantsManager.priorities.findByName(priorityName) ?: constantsManager.defaultPriority
def issueInputParameters = issueService.newIssueInputParameters().with {
    setProjectId(project.id)
    setIssueTypeId(issueType.id)
    setReporterId(reporter.key)
    setSummary(summary)
    setPriorityId(priority.id)
}
def validationResult = issueService.validateCreate(loggedInUser, issueInputParameters)
assert validationResult.isValid(): validationResult.errorCollection

def result = issueService.create(loggedInUser, validationResult)
assert result.isValid(): result.errorCollection

"New issue key: $result.issue.key"
create an issue example

Change Issue Priority

The following example shows the use of dynamic form annotations in a script that changes the priority of an issue. A short text annotator is used for the Issue Key field, select list annotator for Priority, and checkbox annotator for Send Email.

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.event.type.EventDispatchOption
import com.onresolve.scriptrunner.parameters.annotation.*
import com.onresolve.scriptrunner.parameters.annotation.meta.Option

@ShortTextInput(description = "The key of the issue to be updated", label = "Issue Key")
String issueKey

@Select(
    description = "The name of the priority to set",
    label = "Priority",
    options =
        [
            @Option(label = "High", value = "High"),
            @Option(label = "Medium", value = "Medium"),
            @Option(label = "Low", value = "Low")
        ]
)
String priorityName

@Checkbox(description = "Check to send notification email", label = "Send Email")
boolean sendMail

def issueService = ComponentAccessor.issueService
def loggedInUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def issue = ComponentAccessor.issueManager.getIssueByCurrentKey(issueKey)
def availablePriorities = ComponentAccessor.constantsManager.priorities
def priority = availablePriorities.find { it.name == priorityName }
def issueInputParams = issueService.newIssueInputParameters()
issueInputParams.with {
    priorityId = priority.id
}
def updateValidationResult = issueService.validateUpdate(loggedInUser, issue?.id, issueInputParams)
assert updateValidationResult.isValid() : updateValidationResult.errorCollection

def updateResult = issueService.update(loggedInUser, updateValidationResult, EventDispatchOption.ISSUE_UPDATED, sendMail)
"Issue priority changed to: $updateResult.issue.priority.name"
change priority example

Have questions? Visit the Atlassian Community to connect, share, and learn with other Atlassian users and experts, including Adaptavist staff.

Ask a question about ScriptRunner for JIRA, Bitbucket Server, or Confluence.

Want to learn more? Check out courses on Adaptavist Learn, an online platform to onboard and train new users for Atlassian solutions.