Rewrite Scripts for Cloud Hints and Tips
Migration from Scriptrunner for Jira Server/Data Center to ScriptRunner for Jira Cloud (either manually or through the Atlassian Cloud Migration Assistant) will require your scripts to be rewritten. This is because the APIs and programming models differ significantly between Jira Server/Data Center and Jira Cloud.
Jira Server/Data Center relies on certain APIs and functionalities that are specific to on-premises environments, while Jira Cloud operates with a different set of APIs designed for cloud-based infrastructure. For example, certain API endpoints or methods available in Jira Server/Data Center might not exist in Jira Cloud, or they might function differently. Additionally, Jira Cloud offers new capabilities and constraints that require adjustments in how scripts are structured and executed. As a result, each migration is unique and requires careful assessment and adaptation of existing scripts to ensure they work seamlessly in the cloud environment.
Before you start
Platform differences between Server/Data Center and Cloud
Jira Cloud utilizes the Atlassian Forge framework, while Jira Server/Data Center relies on the Atlassian Plugins framework, also known as Plugins v2 (or P2). We have documented the notable differences between these two frameworks on our Differences between ScriptRunner for Jira Server/DC and Cloud page. It's important to understand these differences before you start rewriting your scripts.
The P2 framework in Jira Server/Data Center allows deep integration with the host application, providing direct access to the Java API and execution within Jira's Java Virtual Machine (JVM). In contrast, Jira Cloud uses the Atlassian Forge framework, which operates on a more decoupled model. Here, apps like ScriptRunner must rely on Jira's public REST APIs for operations.
This architectural difference means that in Jira Cloud, tasks such as retrieving, updating, or creating issues, managing projects, and other administrative functions are executed through API calls. As a result, script execution in Cloud is asynchronous. Therefore, when a script runs in Jira Cloud, users might experience page loading before the script has fully executed.
Understanding this shift in how operations are performed is crucial when adapting scripts from Jira Server/Data Center to Jira Cloud. Scripts will need to be rewritten to effectively utilize the available REST APIs and account for the asynchronous nature of Cloud operations.
The ScriptRunner Migration Suite is a suite of tools that helps you plan, analyse, convert and deploy scripts with confidence, significantly reducing the manual migration effort. It supports (not replaces) your expertise. The suite is made up of three tools:
- ScriptRunner Migration Analyse and Assess Tool: Use this tool to review your ScriptRunner Data Center scripts and configurations for risks and cloud readiness.
- The ScriptRunner Migration Agent: Use our specialised AI chat agent to create, convert, and optimise scripts, or you can use it to answer a variety of different questions about ScriptRunner.
- ScriptRunner Dev and Deployment Tool: Use this tool to organise and deploy ScriptRunner Cloud scripts. It is focused on making it easier and faster for consultants and developers to migrate, test, and deploy scripts from ScriptRunner DC to Cloud.
If you have any questions, need help, or would like to request access, the quickest way to get assistance is through our dedicated support portal.
Field availability
All standard ScriptRunner for Jira field types are available in Cloud. However it is important to note that Project Picker fields are only available as single-select fields. Cloud custom fields and advanced field types are listed in Atlassian's documentation.
ScriptRunner differences between Server/Data Center and Cloud
ScriptRunner for Jira Cloud differs from ScriptRunner for Jira Server/Data Center due to differences in the platform. You can review the main differences on the Feature Parity and Script Alternatives page. Some features available in ScriptRunner for Jira Server/Data Center are not available in ScriptRunner for Jira Cloud. We recommend that you review this page before starting to rewrite your scripts to see which features have full or partial parity. You can also use this page to explore the features and capabilities of ScriptRunner for Jira Cloud, such as built-in scripts, listeners, and the Script Console.
Simplify your scripts with HAPI
HAPI is an API you can use to write scripts in a simpler way in ScriptRunner. ScriptRunner Server/Data Center scripts that are simplified using HAPI will be easier to migrate when moving from Jira DC to Jira Cloud. Check out our ScriptRunner for Jira Cloud HAPI documentation for more details on HAPI.
HAPI in ScriptRunner for Jira Cloud differs from Data Center as not all methods are available in Cloud (see the Feature Parity and Script Alternatives page for more details). Scripts that use HAPI methods will likely require significantly fewer changes when migrating to ScriptRunner for Jira Cloud.
Don't have the time or capacity to write scripts in-house? Get your Server scripts translated to Cloud without writing a single line of code yourself. Check out our Scripting Service. |
Migration tools
During the migration process from Jira Server/Data Center to Jira Cloud, using the right tools can help streamline the transition, especially when dealing with API calls and script testing. Below are some essential tools that you can use.
| Tool type | Tool | Summary |
|---|---|---|
| API testing tools | Postman |
|
| cURL |
| |
| Version control systems (optional) | Git |
|
| Integrated development environments (IDEs) (optional) | IntelliJ IDEA |
|
| Visual Studio Code |
|
Preparation steps
Create a test environment
A test environment is crucial in the migration process from Jira Server/Data Center to Jira Cloud. It allows you to experiment, test, and refine your scripts in a controlled setting before deploying them to your production environment. Below we provide details on how to set-up and utilize a test environment.
Set up a Jira Cloud test instance
- Create a Jira Cloud account if you do not already have one. Tip: Atlassian offers free trials for Jira Cloud, allowing you to set up a test environment without incurring initial costs.
- Create a test project that mirrors your production environment as closely as possible. This includes setting up:
- Workflows
- Issue types
- Configurations where they are needed
Install ScriptRunner for Jira Cloud
Install ScriptRunner for Jira Cloud as described on our Installation page.
Utilize the ScriptRunner Script Console
ScriptRunner for Jira Cloud includes a Script Console, similar to the one available in ScriptRunner for Jira Server/Data Center. This feature allows you to write and test scripts in real-time and can be accessed from the ScriptRunner section within your Jira Cloud instance.
We recommend you utilize the script console to do the following:
- Write and test scripts: Write new scripts or adapt existing ones from your server environment. The console provides immediate feedback, allowing you to test script functionality and debug issues quickly.
- Experiment and iterate: The script console is ideal for experimentation. Try different approaches, test API calls, and iterate on your scripts until they perform as expected in the cloud environment. This iterative process helps ensure that your scripts are robust and reliable.
Analyze existing code in Server/Data Center
Before you start your migration of ScriptRunner code from Jira Server/Data Center (DC) to Jira Cloud, we recommend you conduct a thorough analysis of your existing codebase. This will help you streamline the migration process and ensure a smoother transition. Below we list the steps you should follow to analyze your existing code and prepare your scripts for migration.
Review and adapt scripts for Jira Cloud
Before you start migrating your scripts, it's crucial to understand how scripts work in Jira Cloud and the key differences from Server/Data Center. The following steps will guide you through understanding how scripts work in Jira Cloud, enabling you to effectively rewrite your scripts.
Step one: Review imports and dependencies
Examine your scripts for any imported external libraries. These libraries can introduce additional complexity when migrating to Jira Cloud. In addition you should identify plugins, for example Tempo, that the scripts rely on.
Determine if these libraries/plugins are supported or available in the cloud environment. If they are not available you should identify alternative approaches or built-in functionalities in Jira Cloud that can replace them.
Step two: Analyze API calls
Identify any API calls within your scripts, especially those that interact with Jira's internal APIs. Since Jira Cloud relies on REST APIs, you'll need to adapt these calls to use the appropriate cloud-based endpoints. Use the following tips to effectively analyze API calls:
For more details on APIs see the Commonly used Atlassian Java API endpoints and their Cloud equivalents section below.
Step three: Understand and apply basic operations
It's important to understand the basic operations in Jira Cloud's API before you migrate your scripts. This understanding will form the foundation for successfully adapting your existing scripts and ensuring they function correctly in the new environment. In this section, we explore field updates and issue updates.
Work with field updates
The following points outline key considerations for understanding and implementing field updates in Jira Cloud:
- Understand field types: Most fields in Jira Cloud can be updated using the string representation of their values. This eliminates the need to locate option IDs which is often required in ScriptRunner for Server/Data Center scripts. However, some field types (for example text, number, date, select lists, user pickers) may have different representations and constraints in Jira Cloud. For each field type, determine the correct JSON format for updates. The representation may vary based on the field type (for example, date fields may require specific formatting). Understanding these differences is crucial for correctly formatting API requests.
- Check field availability: Use the Jira Cloud REST API to verify that each field you need is available. You can retrieve a list of fields using the /rest/api/3/field endpoint, which provides metadata about all fields in your Jira Cloud instance. Tip: Pay special attention to custom fields, as their IDs and configurations might differ between Server/Data Center and Cloud.
- Supported operations: Ensure that the fields you need to update can be managed through the Jira Cloud REST API. Not all fields may be editable or support the same operations as in the server version.
- Field IDs: Note that in Jira Cloud, fields are often referenced by their unique IDs rather than names. Ensure you have the correct field IDs for use in API requests.
- Test updates: Experiment with updating fields using the
/rest/api/3/issue/{issueIdOrKey}endpoint, providing a JSON payload that specifies the fields and values to update.
Work with issue updates
Updating issues in Jira Cloud using the REST API requires a clear understanding of the JSON payload format, as this dictates how data is structured and transmitted to the server. Below is a detailed guide on how to approach this task.
We recommend you review the following steps to effectively handle JSON payloads and ensure successful interactions with the Atlassian Jira Cloud REST API:
- Review API documentation: Regularly consult the Atlassian Jira Cloud REST API documentation for examples and guidelines on JSON payload structures for different field types.
- Use the ScriptRunner console: If using ScriptRunner, leverage the Script Console to test small scripts that perform GET requests to fetch issue data. This helps you understand how fields are structured in JSON format.
- Experiment with test data: Use a test Jira Cloud instance and create sample issues with various field types. Use the API to retrieve these issues and examine the JSON format of the response. This provides insights into how fields are represented and should be updated.
- Develop your update scripts iteratively: Start with simple fields and gradually incorporating more complex or custom fields. This approach helps isolate and troubleshoot formatting issues.
- Implement error handling: Implement error handling in your scripts to capture and respond to common issues, such as incorrect field IDs, validation errors, or permission issues.
By thoroughly understanding the JSON payload format and field-specific requirements, you can effectively update issues in Jira Cloud, ensuring your scripts function as intended in the new environment.
Basic structure
The JSON payload for updating an issue is typically structured with a root fields object. Each field you wish to update is included as a key-value pair within this object. For example:
{
"fields": {
"summary": "Updated issue summary",
"description": "Updated issue description"
}
}Field-specific formatting
Different field types may require specific formatting in the JSON payload. Understanding these requirements is crucial for successful updates:
- Text fields: Straightforward, accepting plain text strings.
- Select lists: Require the option's ID or value to be specified.
- User picker fields: Require the account ID of the user, rather than their username or email.
- Date fields: Must be formatted in ISO-8601 format (for example
2025-03-04). - Multi-select fields: Accept an array of IDs or values.
Custom fields
Custom fields are identified by their unique IDs (for example customfield_10010). Ensure you use the correct ID in the JSON payload:
{
"fields": {
"customfield_10010": "Custom value"
}
}Complex fields
Fields like components or labels may require arrays of objects or strings:
{
"fields": {
"components": [
{ "id": "10001" }
],
"labels": ["label1", "label2"]
}
}For more details on implementation for basic operations see the Common operations section below.
Step four: Leverage Unirest in ScriptRunner for Jira Cloud
When working with ScriptRunner for Jira Cloud, the Unirest library is a powerful tool for making HTTP requests to interact with Jira's REST API and other web services. It's important to understand how to use Unirest effectively within ScriptRunner scripts to perform actions like retrieving, creating, or updating Jira issues. Below we describe how to use Unirest in ScriptRunner for Jira Cloud:
- Unirest library: Unirest is a lightweight HTTP library that simplifies making HTTP requests in Java and Groovy. We recommend you research the ScriptRunner for Jira Cloud style for writing HTTP calls using the Unirest library. This library is auto-imported into scripts, allowing you to directly use methods like
get(),put(), andpost()as documented in Unirest's documentation without additional import statements. See our documentation on the Unirest Library for more information. - Example scripts: Review the available Example Scripts on the ScriptRunner website and within the Script Console in your Jira Cloud instance. These examples provide insights into the style and code structure for common use cases.
By following these steps, you'll be well-prepared to rewrite your Server/Data Center scripts for Jira Cloud, ensuring a smooth transition and maintaining the desired functionality in the new environment.
Step five: Test your scripts, API calls and integrations
- Test real scenarios: Use the Test environment described above to simulate real scenarios and workflows that your scripts will encounter in production. This includes testing API calls, integrations with other applications, and handling various data inputs and outputs.
- Monitor performance and behavior: Pay attention to how your scripts perform in the test environment. Monitor for any performance bottlenecks, unexpected behaviors, or errors that need to be addressed before going live.
By creating a test environment you can identify and resolve issues early, reducing the likelihood of disruptions in your production environment.
Step six: Migrate to production
The final step is to deploy the scripts in the production environment.
This is a manual process, so each script must be copied manually from test environment to production environment (note that custom field IDs change per environment). Usually this process is scheduled outside working hours to reduce impact in service and needs to be heavily coordinated with the rest of the teams.
Best practices and tips for ScriptRunner for Jira Cloud
When migrating to or working with ScriptRunner for Jira Cloud, keep these best practices and tips in mind. They apply to various use cases and can help streamline your scripting process.
Understand REST API responses
- https://YourCloudURL.atlassian.net/rest/api/3/issue/YourIssueKey
Analyze the response to understand data structure and field representations.
Simplify scripts for Cloud
When converting scripts from ScriptRunner for Jira Server/Data Center to ScriptRunner for Jira Cloud, you may find that many complex objects can be omitted. Server/Data Center scripts often use complex Java methods. In Cloud, these can often be replaced by straightforward REST calls with structured body parameters.
User execution context
In Jira Cloud, scripts generally cannot be executed as another user, except for the ScriptRunner add-on user. While you can pass user account IDs as parameters to certain REST calls, the calls themselves will execute as either the initiating user or the ScriptRunner add-on user.
Authentication headers
You do not need to manually define REST request authentication headers in ScriptRunner for Jira Cloud scripts. These headers are automatically configured. Scripts will execute as the user who triggers the script or as the ScriptRunner add-on user. This execution context is easily controlled through a drop-down menu within the ScriptRunner script configuration UI.
Grouping scripts
In a Server/Data Center script, it is typical to call issue.update { setX(...) } many times to update several fields of an issue at a time. When updating multiple fields of an issue in ScriptRunner for Jira Cloud, it's crucial to group all updates into a single issue update POST call. This approach ensures that all changes are included in the same changeset and prevents multiple notifications from being sent. Here's an example comparing the Data Center (DC) version with the Cloud version:
//DC version
import com.atlassian.jira.component.ComponentAccessor
import java.sql.Timestamp
def versionManager = ComponentAccessor.versionManager
def projectComponentManager = ComponentAccessor.projectComponentManager
def customFieldManager = ComponentAccessor.customFieldManager
def userManager = ComponentAccessor.userManager
def issue = Issues.getByKey('SSPA-1')
def project = issue.getProjectObject()
def version = versionManager.getVersion(project.getId(), "Version 2.0")
def component = projectComponentManager.findByComponentName(project.getId(), "MyComponent")
def user = Users.getByName("admin")
log.warn("version = ${version}")
log.warn("component = ${component}")
if (version) {
issue.update{
setFixVersions(version)
}
}
if (component) {
issue.update{
setComponents(component)
}
}
issue.update {
// Text Field
setCustomFieldValue('CustomField', 'ABC')
// Date Field
setCustomFieldValue('CustomDatePicker', new Timestamp((new Date() + 7).time))
// User Field
setCustomFieldValue('UserPickerField', user)
// System Fields
setDescription('new description')
setDueDate(new Timestamp((new Date() + 1).time).toString())
}
// get custom fields
def fields = ComponentAccessor.customFieldManager.customFieldObjects
//CLOUD version
def issueToUpdate = Issues.getByKey(issue.key) // get the issue by its key
def tomorrowStr = (new Date() + 1).format("yyyy-MM-dd'T'HH:mm:ssZ", TimeZone.getTimeZone("UTC")) // date format in iso8601
issueToUpdate.update {
setFixVersions('1.1')
setComment('MyComponent')
setDescription('A generated description')
setCustomFieldValue('Custom Field 1', 'Some text value')
setCustomFieldValue('Custom Field 2', tomorrowStr)
setCustomFieldValue('Custom Field 3', 'admin')
}Key points to remember:
- The Cloud script is shorter and uses one API call for all updates. The documentation for the update is also much simpler.
- No managers or multiple API calls are needed in the Cloud version.
- Ensure
fixVersionand component exist, and fields are visible on screens.
assert resp.status == 200 (replace 200 with the appropriate response code). Successful APIs call may respond with 204, others with 200, 201 or 303.
Supporting technical information on APIs, listener events, and common operations
Commonly used Atlassian Java API endpoints and their Cloud equivalents
This table maps some of the most commonly used Atlassian Java APIs (in ScriptRunner for Jira Server/Data Center) to the closest Atlassian REST API endpoints (ScriptRunner for Jira Cloud) to guide you in script conversions.
- The latest version of the Jira Cloud platform REST API is version 3. Version 2 and 3 of the API offer the same collection of operations. However, version 3 provides support for the Atlassian Document Format (ADF) and is continuously being enhanced.
- The REST API may not provide direct equivalents for all Java API functionalities. In such cases, consider using a combination of available endpoints or re-evaluating the script's logic to fit within the cloud's constraints.
- Always refer to the official Jira Cloud REST API documentation for the most up-to-date information on available endpoints and their usage.
| Java API (Server) | REST API (Cloud) |
|---|---|
ApplicationProperties (get properties) | /rest/api/3/application-properties (Get application properties) |
AttachmentManager (get attachments) | To get an attachment use: GET/rest/api/3/attachment/content/{id} |
CommentManager (get, add comments) | To get or add comments to an issue use: GET/rest/api/3/issue/{issueIdOrKey}/comment |
ProjectComponentManager (get components) | To get components use: GET/rest/api/3/component |
CustomFieldManager (get custom fields) | To get fields use: GET/rest/api/3/field To create fields use: POST (create fields) /rest/api/3/field Do not use this endpoint to update values of fields, use the issues PUT endpoint. |
GroupManager | Create a group: POST/rest/api/3/group Delete a group: DELETE/rest/api/3/group Get members of a group GET/rest/api/3/group/member Add user to group POST/rest/api/3/group/user Remove user from group DELETE/rest/api/3/group/user |
IssueManager | To get issues use: GET/rest/api/3/issue/{issueIdOrKey} |
IssueSecurityLevelManager / IssueSecuritySchemeManager | You need the ID to get a security level so you have to follow this: |
IssueService (create, update, delete) | Getting issues: GET/rest/api/3/issue/{issueIdOrKey} Updating issues: PUT/rest/api/3/issue/{issueIdOrKey} Deleting issues: DELETE/rest/api/3/issue/{issueIdOrKey} |
issueService.newIssueInputParameters() | You don't need to do this in cloud, you just send a JSON structure of the fields you want to change in the PUT requests body parameters. For example: |
issueService.validateUpdate | You don't need to do validation like this prior to updating issues in Jira Cloud as the endpoint itself will return an error if what you try to do is invalid. |
IssueTypeManager | Get all issue types the executing user has permission to see GET/rest/api/3/issuetype |
JiraAuthenticationContext | Used by Server to get user who will run a Java function. This is not required in ScriptRunner for Jira Cloud as you choose to run scripts as the Current User or ScriptRunner Addon User only . You may need to pass user ID's in the REST API body parameters, but running a script as a user other than Current User or ScriptRunner Add-on User is not currently possible in Cloud. |
LabelManager (get labels) | /rest/api/3/label (no direct equivalent; manage labels via issue updates) |
LinkManager (issue linking) | /rest/api/3/issueLink (create, retrieve issue links) |
OptionsManager | Updating field options: Currently, these endpoints are all experimental for Jira Cloud so you may not be able to do exactly the same thing as in Jira Server.Refer to all the experimental endpoints here. |
PriorityManager | Get all Issue Priorities GET/rest/api/2/priority |
ProjectManager (get project details) | Get a project details by id or key: GET/rest/api/3/project/{projectIdOrKey} Get projects with a paginated search using project Key or Name: GET/rest/api/3/project/search Update a project by id or key: PUT/rest/api/3/project/{projectIdOrKey} Delete a project by id or key: DELETE/rest/api/3/project/{projectIdOrKey} |
ProjectRoleservice / ProjectRoleManager | Control role actors PUT/rest/api/3/project/{projectIdOrKey}/role/{id} Get a projects roles GET/rest/api/3/project/{projectIdOrKey}/role Delete project roles DELETE/rest/api/3/role/{id} |
SearchService (execute JQL queries) | Search with JQL using rest GET/rest/api/3/search/jql If the JQL is too large for a query param use POST/rest/api/3/search/jql |
UserManager (get user details) | Get user details GET/rest/api/3/users/search Get all users GET/rest/api/3/users/search Search users with query GET/rest/api/3/user/search User creation and deletion is in experimental state. |
VersionManager (get versions) | Get all versions for a project: GET/rest/api/3/project/{projectIdOrKey}/versions Create versions for a project: POST/rest/api/3/version Update versions within a project: PUT (Update versions) /rest/api/3/version/{id} Delete/Replace versions in a project: POST/rest/api/3/version/{id}/removeAndSwap |
WatcherManager | Get Watchers for an issue: GET/rest/api/3/issue/{issueIdOrKey}/watchers Add watchers to an issue: POST (add watchers) /rest/api/3/issue/{issueIdOrKey}/watchers Delete watchers from an issue: DELETE/rest/api/3/issue/{issueIdOrKey}/watchers |
WorklogManager (get add worklogs) | /rest/api/3/issue/{issueIdOrKey}/worklog (get, add worklogs to an issue) |
Common listener events availability
Below is a list of common listener events in ScriptRunner for Jira Server/Data Center and their availability in Cloud. More detail on what ScriptRunner can do with the supported Cloud events can be found on our Script Listeners page.
| Server/DC Event | Available? | Notes |
|---|---|---|
| Issue Created/Updated/Deleted | ||
| Issue Assigned | ||
| Issue Resolved | ||
| Issue Closed | ||
| Issue Reopened | ||
| Issue Moved | ||
| Issue Link Created/Deleted | ||
| Issue Watcher Added/Deleted | ||
| Issue Archived | Not a Cloud feature | |
| Comment Created/Updated/Deleted | ||
| Project Created/Updated/Deleted | ||
| Project Component events | ||
| Project Role events | ||
| Version Created/Updated/Deleted | ||
| Version Moved | ||
| Version Released/Unreleased | ||
| User Created/Deleted/Edited | User management events may not be directly available in Cloud. | |
| Group Created/Updated/Deleted | ||
| Worklog Created/Updated/Deleted | ||
| Custom Event | Custom events are not directly supported in Cloud but can be simulated using webhooks and automation. |
Common operations
Here we have listed some common operations required in ScriptRunner for Jira Cloud scripts. Switch between the tabs to show the Cloud or Server/Data Center scripts. These operations can be utilized for many use cases.
Get field name to ID map
Cloud
/*
For Cloud, you are unlikely to need a map of field names to IDs for custom fields, as you can simply update or create an issue using the custom field name instead of its ID by using HAPI methods
*/
def fieldNameToIdMap
def fields = get('/rest/api/2/field')
.header('Content-Type', 'application/json')
.asObject(List)
if (fields.status == 200){
fieldNameToIdMap = fields.body.collectEntries {
[(it.name): it.id]
}
} else {
return "Failed to generate fields map ${fields.status} ${fields.body}"
}Server/Data Center
/*
For Server/DC you are unlikely to need to get a map of field names to ID for custom fields as you can just
use this simple method to get a collection CustomField objects and then filter by name as shown in the next example.
*/
import com.atlassian.jira.component.ComponentAccessor
def fields = ComponentAccessor.customFieldManager.getCustomFieldObjects()Get a single field ID with its name
Cloud
/*
For Cloud, you are unlikely to need custom field ID, as you can simply update or create an issue using its name using HAPI methods
*/
final customFieldName = 'TextFieldA'
def fieldId
def fields = get('/rest/api/2/field')
.header('Content-Type', 'application/json')
.asObject(List)
if (fields.status == 200){
fieldId = fields.body.find {
it.name == customFieldName
}.id
} else {
return "Failed to generate fields map ${fields.status} ${fields.body}"
}Server/Data Center
import com.atlassian.jira.component.ComponentAccessor
final FIELD_NAME = 'TextFieldA'
def customFieldId = ComponentAccessor.customFieldManager.getCustomFieldObjectsByName(FIELD_NAME).find().idUpdate fields
Cloud
def issue = Issues.getByKey("TEST-2")
issue.update {
setSkipScreenCheck(true)
setCustomFieldValue("SelectListA", "BBB") //Single Select List
setCustomFieldValue("Multi Select", "BBBB", "CCCC") //Multi Select List
setCustomFieldValue("RadioButtonA", "Maybe") // Radio Buttons
setCustomFieldValue("CheckBoxA", "Maybe", "No") // Checkboxes
setCustomFieldValue("TextFieldA", "QWERTY") // Text Field
setCustomFieldValue("UserPickerA", "5b9a84022d389f762bd0bd23") // Single User Picker
setCustomFieldValue("MultiUserPickerA", "5b9a84022d389f762bd0bd23") // Multi User Picker
setCustomFieldValue("DateTimePickerA", "2021-10-16T15:41:00.000+0100") // Date Time Field
setCustomFieldValue("DatePickerA", "2021-10-11") // Date Field
setCustomFieldValue("ProjectPickerA", "TP") // Project Picker
setCustomFieldValue("LabelFieldA", "here", "test") // Labels field
setCustomFieldValue("VersionPickerA", "testv1") // Single Version Picker
setCustomFieldValue("MultiVersionPickerA", "testv1", "anotherv2") // Multi Version Picker
setCustomFieldValue("singleGroup", "jira-software-users-mattdevtest") // Single Group Picker
setCustomFieldValue("multiGroup", "jira-servicemanagement-users-mattdevtest", "jira-software-users-mattdevtest", "jira-workmanagement-users-mattdevtest") //Multi-Group Picker
}Server/Data Center (HAPI version)
// Example from https://www.scriptrunnerhq.com/help/example-scripts/basics-updating-customfields-onPrem
// the issue key to update
def issueKey = "Test-1"
Issues.getByKey(issueKey).update {
// set custom fields with options (select lists, checkboxes, radio buttons)
setCustomFieldValue('SelectListA', 'BBB')
setCustomFieldValue('MultiSelectA', 'BBB', 'CCC')
setCustomFieldValue('RadioButtons', 'Yes')
setCustomFieldValue('Checkboxes', 'Maybe', 'Yes')
// cascading select
setCustomFieldValue('CascadingSelect', 'BBB', 'B2')
// set text fields
setCustomFieldValue('TextFieldA', 'New Value')
// set user fields
setCustomFieldValue('UserPicker', 'bob')
setCustomFieldValue('MultiUserPickerA', 'bob', 'alice')
setCustomFieldValue('GroupPicker', 'jira-users')
setCustomFieldValue('MultiGroupPicker', 'jira-users', 'jira-administrators')
// set date, and date-time custom fields
setCustomFieldValue('First DateTime', '04/Feb/12 8:47 PM')
// setCustomFieldValue('Date', '04/Feb/12')
// a "project picker" custom field - provide a project key
setCustomFieldValue('ProjectPicker', 'SSPA')
// set custom field of type version
setCustomFieldValue('SingleVersionPicker', 'Version1')
}Server/Data Center (Jira API version)
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.issue.fields.CustomField
import groovy.transform.Field
// the issue key to update
@Field final String issueKey = "Test-1"
// the name of a 'single select list' custom field
final String selectList = "SelectListA"
// the name of a 'multi select list' custom field
final String multiSelectList = "MultiSelectA"
// name of a 'radio button' custom field
final String radioButtonField = "RadioButtons"
// name of a 'check box' custom field
final String checkboxField = "Checkboxes"
// the name of a 'text field' custom field
final String textField = "TextFieldA"
// the name of a 'user picker' custom field
final String userPicker = "UserPicker"
// the name of a 'multi user picker' custom field
final String multiUserPicker = "MultiUserPickerA"
// the name of a 'group picker' custom field
final String groupPicker = "GroupPicker"
// the name of a 'multi group picker' custom field
final String multiGroupPicker = "MultiGroupPicker"
// the name of a 'date and time' custom field
final String dateTimeField = "First DateTime"
// the name of a 'date' custom field
final String dateField = "Date"
// the name of a 'project picker' custom field
final String projectPickerField = "ProjectPicker"
// the name of a 'label' picker custom field
final String labelField = "LabelField"
// name of a 'single version picker' custom field
final String versionField = "VersionPicker"
// name of a 'multi version picker' custom field
final String multiVersionField = "VersionsPicker"
// change to 'true' if you want to send an email if the update is successful
final boolean sendMail = false
def issueService = ComponentAccessor.issueService
def loggedInUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def issue = ComponentAccessor.issueManager.getIssueByCurrentKey(issueKey)
assert issue: "Could not find issue with key $issueKey"
def issueInputParameters = issueService.newIssueInputParameters().with {
// set custom fields with options (select lists, checkboxes, radio buttons)
addCustomFieldValue(getSingleCustomFieldByName(selectList).id, *getOptionIdsForFieldByValue(selectList, "BBB"))
addCustomFieldValue(getSingleCustomFieldByName(multiSelectList).id, *getOptionIdsForFieldByValue(multiSelectList, "BBB", "CCC"))
addCustomFieldValue(getSingleCustomFieldByName(radioButtonField).id, *getOptionIdsForFieldByValue(radioButtonField, "Yes"))
addCustomFieldValue(getSingleCustomFieldByName(checkboxField).id, *getOptionIdsForFieldByValue(checkboxField, "Maybe", "Yes"))
// set text fields
addCustomFieldValue(getSingleCustomFieldByName(textField).id, "New Value")
// set user fields
addCustomFieldValue(getSingleCustomFieldByName(userPicker).id, "admin")
addCustomFieldValue(getSingleCustomFieldByName(multiUserPicker).id, "admin", "anuser")
// set group fields
addCustomFieldValue(getSingleCustomFieldByName(groupPicker).id, "jira-users")
addCustomFieldValue(getSingleCustomFieldByName(multiGroupPicker).id, "jira-users", "jira-administrators")
// set custom field of type date
addCustomFieldValue(getSingleCustomFieldByName(dateTimeField).id, "04/Feb/12 8:47 PM")
addCustomFieldValue(getSingleCustomFieldByName(dateField).id, "04/Feb/12")
}
// set project picker field
def project = ComponentAccessor.projectManager.getProjectObjByKey("SSPA")
assert project: "Could not find project"
issueInputParameters.addCustomFieldValue(getSingleCustomFieldByName(projectPickerField).id, project.id.toString())
// set custom field of type label
issueInputParameters.addCustomFieldValue(getSingleCustomFieldByName(labelField).id, "foo", "bar")
// set custom field of type version picker
def versionOne = ComponentAccessor.versionManager.getVersions(issue.projectObject).findByName("Version1")
assert versionOne: "Could not find version"
issueInputParameters.addCustomFieldValue(getSingleCustomFieldByName(versionField).id, versionOne.id.toString())
// set custom field of type multi-version picker
def versionTwo = ComponentAccessor.versionManager.getVersions(issue.projectObject).findByName("Version2")
assert versionTwo: "Could not find version"
issueInputParameters.addCustomFieldValue(getSingleCustomFieldByName(multiVersionField).id, versionOne.id.toString(), versionTwo.id.toString())
def updateValidationResult = issueService.validateUpdate(loggedInUser, issue.id, issueInputParameters)
assert updateValidationResult.valid: updateValidationResult.errorCollection
def issueUpdateResult = issueService.update(loggedInUser, updateValidationResult, EventDispatchOption.ISSUE_UPDATED, sendMail)
assert issueUpdateResult.valid: issueUpdateResult.errorCollection
/**
* Get a custom field given a custom field name.
* If there are than one custom fields with the same name under the same Context then return the first one.
* @param fieldName The name of the custom field
* @param issue The issue to look for that custom field
* @return the custom field, if that exists
*/
CustomField getSingleCustomFieldByName(String fieldName) {
def issue = ComponentAccessor.issueManager.getIssueByCurrentKey(issueKey)
def customField = ComponentAccessor.customFieldManager.getCustomFieldObjects(issue).findByName(fieldName)
assert customField: "Could not find custom field with name $fieldName"
customField
}
/**
* Given a custom field name and option values, retrieve their ids as String
* @param customFieldName The name of the custom field
* @param values The values in order to get their ids
* @return List < String > The ids of the given values
*/
List<String> getOptionIdsForFieldByValue(String customFieldName, String... values) {
def issue = ComponentAccessor.issueManager.getIssueByCurrentKey(issueKey)
def customField = getSingleCustomFieldByName(customFieldName)
ComponentAccessor.optionsManager.getOptions(customField.getRelevantConfig(issue)).findAll {
it.value in values.toList()
}*.optionId*.toString()
}Perform JQL searches
Cloud
def issues = Issues.search("project = TEST")
// print all the issue keys just to demonstrate what was found
issues*.keyServer/Data Center (HAPI version)
// Example from https://www.scriptrunnerhq.com/help/example-scripts/jql-search-onPrem
Issues.search('project = TEST').each { issue ->
// do something with `issue`
}Server/Data Center (Jira API version)
import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.search.SearchException
import com.atlassian.jira.web.bean.PagerFilter
import org.apache.log4j.Level
// Set log level to INFO
log.setLevel(Level.INFO)
// The JQL query you want to search with
final jqlSearch = "project = TEST"
// Some components
def user = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def searchService = ComponentAccessor.getComponentOfType(SearchService)
// Parse the query
def parseResult = searchService.parseQuery(user, jqlSearch)
if (!parseResult.valid) {
log.error('Invalid query')
return null
}
try {
// Perform the query to get the issues
def results = searchService.search(user, parseResult.query, PagerFilter.unlimitedFilter)
def issues = results.results
issues.each {
log.info(it.key)
}
issues*.key
} catch (SearchException e) {
e.printStackTrace()
null
}