20.Groovy Scripts (Cloud and Server) & Learning
Listing out the groovy scripts for different scenarios below which might be useful in future requirements.
1. Script to transition sub-task to same status as parent task on update or create event (Cloud)
// Check if issue types are sub-tasks
if (issue.fields.issuetype.subtask) {
logger.info("This is a subtask")
return
}
else if (!issue.fields.issutype.subtask) {
logger.info("This is not a subtask")
// Retrieve the key
def parentKey = issue.key
logger.info("Parent Key: " + parentKey)
// Get the parent issue object
def result = get('/rest/api/2/issue/' + parentKey + "?expand=transitions")
.header('Content-Type', 'application/json')
.asObject(Map)
// Find the parent status
def parentStatus = result.body.fields.status.name
logger.info("Parent Status: " + parentStatus)
// Find the subtask status
def subTaskStatus = result.body.fields.subtasks.fields.status.name
logger.info("Sub task Status: " + subTaskStatus)
// Find the subtasks key
String[] subTaskKeyInArray = result.body.fields.subtasks.key
//def subTaskKeyString = subTaskKeyInArray.join(", ")
logger.info("Length of the array is:" + subTaskKeyInArray.length)
//Defining statuses
def open = "Open"
def qaInProg = "QA In Prog"
def pendingCTS = "Pending CTS"
def ctsClosed = "CTS Closed"
def investigating = "Investigating"
def investigatingComplete = "Investigating Complete"
def fixed = "Fixed"
def transition(transitionId, subTaskKey) {
def transitionIssue = post("/rest/api/2/issue/" + subTaskKey + "/transitions")
.header("Content-Type", "application/json")
.body([transition: [id: transitionId]])
.asObject(Map)
logger.info("Sub Task issue transitioned")
}
for(int i = 0; i < subTaskKeyInArray.length; i++) {
logger.info("Value of i " + i)
logger.info("Sub task:" + subTaskKeyInArray[i])
// Put conditions to set transition ids based on condition is true
if (subTaskStatus.contains(qaInProg) && parentStatus.contains(investigating)) {
logger.info("Transition id after entering loop: 41")
transition("41", subTaskKeyInArray[i])
}
if (subTaskStatus.contains(qaInProg) && parentStatus.contains(pendingCTS)) {
logger.info("Transition id after entering loop: 51")
transition("51", subTaskKeyInArray[i])
}
if (subTaskStatus.contains(qaInProg) && parentStatus.contains(ctsClosed)) {
logger.info("Transition id after entering loop: 61")
transition("61", subTaskKeyInArray[i])
}
if (subTaskStatus.contains(qaInProg) && parentStatus.contains(fixed)) {
logger.info("Transition id after entering loop: 121")
transition("121", subTaskKeyInArray[i])
}
if (subTaskStatus.contains(open) && parentStatus.contains(qaInProg)) {
logger.info("Transition id after entering loop: 11")
transition("11", subTaskKeyInArray[i])
}
if (subTaskStatus.contains(open) && parentStatus.contains(pendingCTS)) {
logger.info("Transition id after entering loop: 21")
transition("21", subTaskKeyInArray[i])
}
if (subTaskStatus.contains(open) && parentStatus.contains(ctsClosed)) {
logger.info("Transition id after entering loop: 61")
transition("61", subTaskKeyInArray[i])
}
if (subTaskStatus.contains(open) && parentStatus.contains(fixed)) {
logger.info("Transition id after entering loop: 121")
transition("121", subTaskKeyInArray[i])
}
if (subTaskStatus.contains(investigating) && parentStatus.contains(investigatingComplete)) {
logger.info("Transition id after entering loop: 71")
transition("71", subTaskKeyInArray[i])
}
if (subTaskStatus.contains(investigating) && parentStatus.contains(pendingCTS)) {
logger.info("Transition id after entering loop: 81")
transition("81", subTaskKeyInArray[i])
}
if (subTaskStatus.contains(investigating) && parentStatus.contains(ctsClosed)) {
logger.info("Transition id after entering loop: 61")
transition("61", subTaskKeyInArray[i])
}
if (subTaskStatus.contains(investigating) && parentStatus.contains(fixed)) {
logger.info("Transition id after entering loop: 121")
transition("121", subTaskKeyInArray[i])
}
if (subTaskStatus.contains(investigatingComplete) && parentStatus.contains(pendingCTS)) {
logger.info("Transition id after entering loop: 101")
transition("101", subTaskKeyInArray[i])
}
if (subTaskStatus.contains(investigatingComplete) && parentStatus.contains(ctsClosed)) {
logger.info("Transition id after entering loop: 161")
transition("161", subTaskKeyInArray[i])
}
if (subTaskStatus.contains(investigatingComplete) && parentStatus.contains(fixed)) {
logger.info("Transition id after entering loop: 121")
transition("121", subTaskKeyInArray[i])
}
if (subTaskStatus.contains(investigatingComplete) && parentStatus.contains(investigating)) {
logger.info("Transition id after entering loop: 111")
transition("111", subTaskKeyInArray[i])
}
if (subTaskStatus.contains(pendingCTS) && parentStatus.contains(ctsClosed)) {
logger.info("Transition id after entering loop: 141")
transition("141", subTaskKeyInArray[i])
}
if (subTaskStatus.contains(pendingCTS) && parentStatus.contains(fixed)) {
logger.info("Transition id after entering loop: 121")
transition("121", subTaskKeyInArray[i])
}
if (subTaskStatus.contains(pendingCTS) && parentStatus.contains(investigating)) {
logger.info("Transition id after entering loop: 131")
transition("131", subTaskKeyInArray[i])
}
if (subTaskStatus.contains(fixed) && parentStatus.contains(ctsClosed)) {
logger.info("Transition id after entering loop: 151")
transition("151", subTaskKeyInArray[i])
}
if (subTaskStatus.contains(fixed) && parentStatus.contains(investigating)) {
logger.info("Transition id after entering loop: 131")
transition("131", subTaskKeyInArray[i])
}
}
}
2. Script to prompt the user if they want to close the parent ticket when all sub-tasks under the ticket are done (Cloud)
// Check if issue types are sub-tasks
if (!issue.fields.issuetype.subtask) {
return
}
// Get the parent issue as a Map
def parent = (issue.fields as Map).parent as Map
// Retrieve all the subtasks of this issue's parent
def parentKey = parent.key
logger.info("Parent Key: " + parentKey)
// Specify the name of the version to extract from the issue
def closedStatus = "Done"
logger.info("Closed Status: " + closedStatus)
// Get the parent issue object
def result = get('/rest/api/2/issue/' + parentKey)
.header('Content-Type', 'application/json')
.asObject(Map)
//Find the assignee name
def assignee = result.body.fields.assignee.displayName
logger.info("Assignee name is :" + assignee)
// Find the subtasks status
String[] subTasks = result.body.fields.subtasks
logger.info("Subtasks length: " + subTasks.length)
def allSubTasksResolved;
for(int i = 0; i < subTasks.length; i++) {
def subTasksStatus = result.body.fields.subtasks[i].fields.status.name
logger.info("sub task status: " + subTasksStatus)
if(subTasks.findAll{subTasksStatus.contains(closedStatus)}){
allSubTasksResolved = true
}
// If all subtasks have the Done status set the flag to true
if(subTasks.findAll{!subTasksStatus.contains(closedStatus)}){
allSubTasksResolved = false
}
}
if(allSubTasksResolved == true){
logger.info("All sub tasks are in Done status")
def prompt = post("/rest/api/2/issue/" + parentKey + "/notify")
.header("Content-Type", "application/json")
.body([
subject: 'All sub-tasks transitioned to Done status',
textBody: "All the sub-tasks related to " + parentKey + " are transitioned to Done status",
htmlBody: "<p>Do you want to close the </p>" + parentKey + "<p> issue?</p>" ,
to: [
users: [[
name: assignee,
active: true
]]
]
])
.asString()
} else if (allSubTasksResolved == false){
logger.info("All sub tasks are not in Done status")
}
3. Script to change the fix Versions field of sub-tasks based on parent task on update event (Server)
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.project.version.Version
IssueManager issueManager = ComponentAccessor.getComponent(IssueManager.class)
Issue updatedIssue = event.getIssue()
log.error "Issue is: " + updatedIssue
Collection<Version> fixVersions = new ArrayList<Version>()
fixVersions = updatedIssue.getFixVersions()
log.error "Fix versions of parent are: " + fixVersions
Collection<Issue> subTasks = updatedIssue.getSubTaskObjects()
log.error "Sub tasks are: " + subTasks
subTasks.each {
log.error "Type : " + it.issueType.getName()
if(it.issueType.getName() == "Migration") {
if (it instanceof MutableIssue) {
((MutableIssue) it).setFixVersions(fixVersions)
issueManager.updateIssue(event.getUser(), it, EventDispatchOption.ISSUE_UPDATED, false)
log.error "Fix version of subbtask"
}
}
}
4. Script to set a default value for fix-version field (Server)
5. Adding Custom filed value while changing the status to the particular screen: post-function (Server)
6. Changing the status of particular issue (Server)
7. Getting the linked issues of particular issue (Server)
8. Script to set a default value for fix-version field for a particular issue type (Server)
9. Adding a comment to the linked issue and the main issue (Server)
10. Getting all Sub-Tasks and Issue Key of the Sub-Task (Server)
11. Script to hide the custom fields on create screen if the user is not a project admin (behaviors - Server)
12. Script to create issues of particular issue type when a classic project is created (Cloud)
13. Script to deactivate users if they are inactive for long time and doesn't belong to “xyz“ group(s) (Server)
Learning:
Knowledge on how script-runner listener works for both cloud and server
Got to understand different scenarios for Jira based on which scripts were written
Basic knowledge on groovy
While setting the value for multi-select system fields always set the values based on “id’s of values” and not “values itself“ in behaviors
While setting the value for single select system fields we can directly give “values” instead of “id’s of values”
script-runner behaviors
While setting the value for single select custom field we have to give “id’s of values” only (can also be directly configured in field setting as these are custom fields)
No | Date | Topics learnt |
1 | 12-03-2020 | Introduction to Groovy, how groovy works, static and dynamic compilation, optional typed, how to use script-runner to execute scripts in script console, how to look up to java doc for methods and classes |
2 | 13-03-2020 | Variables, rules, operators, built in scripts and scenarios in script-runner, listeners, exception handling, how to get all the details related to a specific issue (code) |
3 | 16-03-2020 | Conditional statements (if else, switch), looping (for, for in, while), String, string functions, interpolation, multi-lines, methods / functions, closures, how to get all the sub-tasks related to issues, fetch their data and manipulate their information (code) |
4 | 17-03-2020 | Worked on setting a default value to a fix version system field (includes reading and understanding the behaviors of script-runner, sample examples, and implementation of the code) https://empyrajira.atlassian.net/browse/AP-1115 |
5 | 18-03-2020 | Collections, lists, maps, ranges, types of ranges, iterations over collections, functions available for different collections, IO operations, how to set values based on system and custom fields (code) |
Use Cases:
No | Use Cases | Version |
---|---|---|
1 | As a developer, I have to transition the subtasks on the issue to the same status as it’s parent task when parent task is transitioned | Cloud |
2 | As a developer, I have to prompt (ask) the user if they want to close the parent ticket when all sub-tasks under the ticket are done | Cloud |
3 | As a developer, I have to change the fix version field of sub-tasks based on parent task’s fix version field value on update event | Server / Cloud |
4 | As a developer, I have to set a default value for fix version field | Server |
5 | As a developer, I have to add the custom field value while changing the status to the particular screen: post-function | Server |
6 | As a developer, I have to change the status of particular issue on update event | Server / Cloud |
7 | As a developer, I have to set a default value for fix version field for a particular issue type | Server |
8 | As a developer, I have to get the linked issues of particular issue | Server / Cloud |
9 | As a developer, I have to add a comment to the linked issue and the main issue | Server |
10 | As a developer, I have to get all the sub tasks on a issue and their issue keys | Server / Cloud |
11 | As a developer, I have to hide the custom fields on create screen if the user is not a project admin | Server - Behaviors |
12 | As a developer, I have to create issues of particular issue type when a classic project is created | Cloud |
13 | As a developer, I have to deactivate users if they are inactive for long time and doesn't belong to a group(s) | Server |
Post Migration Script changes in cloud:
Once the Jira or Confluence migration takes place from Server to Cloud we have to identify the scripts that were running on Server and convert them to be compatible for Cloud. The scripts can be in various different apps such as Scriptrunner, JSU, JMWE etc., The syntax might differ a bit but the core logic remains the same. As per the mentioned apps above, the Cloud syntax is not very different from the Server. Below are few examples on how the scripts can be converted from Server to Cloud. The scripts can be written for various scenarios such as listeners, workflow conditions, validators, post functions etc., but majorly it will be on workflows.
Examples:
No | App | Use Case | Server code | Cloud code |
---|---|---|---|---|
1 | Adaptivist Scriptrunner for Jira | Must not be able transition the issue unless condition is true or it returns a truthy value |
|
|
2 | Adaptivist Scriptrunner for Jira | Transition must be blocked unless it returns a truthy value |
|
|
Reference Links:
Jira Expression Types: Jira expressions types
Scriptrunner Example Conditions: https://docs.adaptavist.com/sr4jc/latest/features/workflow-extensions/conditions/example-conditions