Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

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)

Code Block
languagegroovy
// 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]) 
 }
 }
}

...

Code Block
languagegroovy
/*

Script to create issues of a particular issue type when
a new project of classic type is created in JIRA cloud

*/


//get the project key on creation of project
def projectKey = project.key;
logger.info("The key of the project is: " + projectKey);

//get call for project data based on project key
def getProjectStyle = get("/rest/api/3/project/" + projectKey) 
                        .header('Content-Type', 'application/json')
                        .asObject(Map)
     
//project style                        
def projectStyle = getProjectStyle.body.style;
logger.info("Project Style is: " + projectStyle);

//project id
def projectId = getProjectStyle.body.id;
logger.info("Project Id is: " + projectId);

//variable used to assign rest call, later in the code
def postCreateIssue;

//issue type id
def issueTypeId = "10002";

//account id of assignee
def accountIdOfAssignee = "5b4f068a3675c92cbbded6a2";

//project style classic
def projectStyleClassic = "classic";


//condition to create issues only if it is classic project
if(projectStyle == projectStyleClassic) {
    postCreateIssue = post("/rest/api/3/issue/bulk")
        .header("Content-Type", "application/json")
        .body(
        [
                issueUpdates:  [
                        [
                                fields: [
                                        summary    : "Create user profiles",
                                        description: [
                                                type: "doc",
                                                version: 1,
                                                content: [
                                                    [
                                                        type: "paragraph",
                                                        content: [
                                                                        [
                                                                            text: "Create profiles for students in directory",
                                                                            type: "text"
                                                                        ]
                                                                    ]
                                                    ]
                                                ]
                                        ],
                                        project    : [
                                                id: projectId
                                        ],
                                        issuetype  : [
                                                id: issueTypeId
                                        ],
                                        assignee: [
                                                id: accountIdOfAssignee
                                        ]
                                ]
                        ],
                        [
                                fields: [
                                        summary    : "Create logo",
                                        description: [
                                                type: "doc",
                                                version: 1,
                                                content: [
                                                    [
                                                        type: "paragraph",
                                                        content: [
                                                                        [
                                                                            text: "Create logos for the customer",
                                                                            type: "text"
                                                                        ]
                                                                    ]
                                                    ]
                                                ]
                                        ],
                                        project    : [
                                                id: projectId
                                        ],
                                        issuetype  : [
                                                id: issueTypeId
                                        ],
                                        assignee: [
                                                id: accountIdOfAssignee
                                        ]
                                ]
                        ],
                        [
                                fields: [
                                        summary    : "Create accounts",
                                        description: [
                                                type: "doc",
                                                version: 1,
                                                content: [
                                                    [
                                                        type: "paragraph",
                                                        content: [
                                                                        [
                                                                            text: "Create accounts for each student n staff",
                                                                            type: "text"
                                                                        ]
                                                                    ]
                                                    ]
                                                ]
                                        ],
                                        project    : [
                                                id: projectId
                                        ],
                                        issuetype  : [
                                                id: issueTypeId
                                        ],
                                        assignee: [
                                                id: accountIdOfAssignee
                                        ]
                                ]
                        ],
                        [
                                fields: [
                                        summary    : "Create properties",
                                        description: [
                                                type: "doc",
                                                version: 1,
                                                content: [
                                                    [
                                                        type: "paragraph",
                                                        content: [
                                                                        [
                                                                            text: "Create properties for students and staff",
                                                                            type: "text"
                                                                        ]
                                                                    ]
                                                    ]
                                                ]
                                        ],
                                        project    : [
                                                id: projectId
                                        ],
                                        issuetype  : [
                                                id: issueTypeId
                                        ],
                                        assignee: [
                                                id: accountIdOfAssignee
                                        ]
                                ]
                        ],
                        [
                                fields: [
                                        summary    : "Assign and implement",
                                        description: [
                                                type: "doc",
                                                version: 1,
                                                content: [
                                                    [
                                                        type: "paragraph",
                                                        content: [
                                                                        [
                                                                            text: "Assign and implement the case",
                                                                            type: "text"
                                                                        ]
                                                                    ]
                                                    ]
                                                ]
                                        ],
                                        project    : [
                                                id: projectId
                                        ],
                                        issuetype  : [
                                                id: issueTypeId
                                        ],
                                        assignee: [
                                                id: accountIdOfAssignee
                                        ]
                                ]
                        ]
                ]   
        ])
        .asString()
        
} else {
    logger.info("Project doesn't belong to classic style");
}

//success or failure of issue creation based on condition
if(projectStyle == projectStyleClassic && postCreateIssue.status == 201) {
    logger.info("Successfully added issues to project with key: ", projectKey);
} else {
    logger.error("Failed to bulk add issues to project with key: ", projectKey);
}

Learning:

  1. Knowledge on how script-runner listener works for both cloud and server

  2. Got to understand different scenarios for Jira based on which scripts were written

  3. Basic knowledge on groovy

  4. 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

  5. While setting the value for single select system fields we can directly give “values” instead of “id’s of values”

  6. script-runner behaviors

  7. 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

...

13. Script to deactivate users if they are inactive for long time and doesn't belong to “xyz“ group(s) (Server)

Code Block
languagegroovy
import com.atlassian.crowd.embedded.api.CrowdService
import com.atlassian.crowd.embedded.api.UserWithAttributes
import com.atlassian.crowd.embedded.impl.ImmutableUser
import com.atlassian.jira.bc.user.UserService
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.user.ApplicationUsers
import com.atlassian.jira.user.util.UserUtil
import com.atlassian.jira.security.groups.GroupManager
 
//Number of days the user has not logged in
final NUMBER_OF_DAYS  = 180;

//Counter to calculate amount of users deactivated starting 0
def COUNTER = 0;

//Set a date as a limit to validate against / the period in which the user is not validated against
Date dateLimit = (new Date()) - NUMBER_OF_DAYS;

final GROUP_ONE = "Watchers_Only";
final GROUP_TWO = "Essential Accounts";
 
UserUtil userUtil = ComponentAccessor.userUtil;
CrowdService crowdService = ComponentAccessor.crowdService;
UserService userService = ComponentAccessor.getComponent(UserService);
ApplicationUser updateUser;
UserService.UpdateUserValidationResult updateUserValidationResult;
def groupManager = ComponentAccessor.getComponent(GroupManager);
 
//Loop to check the condition and deactivate users from instance
userUtil.getUsers().findAll{it.isActive()}.each {
    
    ApplicationUser userInGroup = ComponentAccessor.getUserManager().getUserByName(it.getName());
    def isUserInGroup = groupManager.isUserInGroup(userInGroup, GROUP_ONE);
    def isUserInSecondGroup = groupManager.isUserInGroup(userInGroup, GROUP_TWO);
    
    if(isUserInGroup == true) {
        log.error "${it.getName()} NOT DEACTIVATED as they belong to ${GROUP_ONE}";
    } 
    else if(isUserInSecondGroup == true) {
	log.error "${it.getName()} NOT DEACTIVATED as they belong to ${GROUP_TWO}";
    }
    
    else {

    UserWithAttributes user = crowdService.getUserWithAttributes(it.getName());
    //Get the last logged in value of users in miliseconds / lastLoginMillis
    String lastLoginMillis = user.getValue('login.lastLoginMillis');
    
    	//Condition to check if last logged in value is a number or null 
        if (lastLoginMillis?.isNumber()) {
           //Converts the timestamp value to [ Day Month Date HH:MM:SS Timezone Year ] format
       	   Date convertTimeStampToDate = new Date(Long.parseLong(lastLoginMillis));
           if (convertTimeStampToDate.before(dateLimit)) {
                updateUser = ApplicationUsers.from(ImmutableUser.newUser(user).active(false).toUser());
                updateUserValidationResult = userService.validateUpdateUser(updateUser);
                if (updateUserValidationResult.isValid()) {
                    userService.updateUser(updateUserValidationResult);
                    log.error "${updateUser.name} DEACTIVATED as their last login is on ${convertTimeStampToDate}";
                    COUNTER++;
                } else {
                    log.error "Update of ${user.name} failed: ${updateUserValidationResult.getErrorCollection().getErrors().entrySet().join(',')}";
                }
            }
        }
     }
}
 
"${COUNTER} users deactivated.\n";

Learning:

  1. Knowledge on how script-runner listener works for both cloud and server

  2. Got to understand different scenarios for Jira based on which scripts were written

  3. Basic knowledge on groovy

  4. 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

  5. While setting the value for single select system fields we can directly give “values” instead of “id’s of values”

  6. script-runner behaviors

  7. 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

Code Block
languagegroovy
isUserMemberOfRole(“Administrators”)

Code Block
languagegroovy
user.getProjectRoles(project).map(p => p.name).includes("Administrators")

2

Adaptivist Scriptrunner for Jira

Transition must be blocked unless it returns a truthy value

Code Block
languagegroovy
import com.atlassian.jira.issue.Issue

issue.resolutionObject.name != null || issue.assignee != null 

Code Block
languagegroovy
issue.resolution.name != null || issue.assignee.displayName != null

Reference Links:

Jira Expression Types: https://developer.atlassian.com/cloud/jira/platform/jira-expressions-type-reference/#user

Scriptrunner Example Conditions: https://docs.adaptavist.com/sr4jc/latest/features/workflow-extensions/conditions/example-conditions