My custom scripts for Scriptrunner and beyond

Viewing page 2 of 2

44 comments

Comment

Log in or Sign up to comment
Volodymyr
Contributor
December 14, 2023

How to set a resolution depending on the issue type

String issueTypeId = issue.getIssueTypeId()
String issueResolutionID

switch (issueTypeId) {
    case '42':
        issueResolutionID = '11' // When the issue type is "Change Request", set the resolution to "Implemented"
        break
    case '6':
        issueResolutionID = '6' // When the issue type is "Epic", set the resolution to "Done"
        break
    case '17':
        issueResolutionID = '6' // When the issue type is "Feature", set the resolution to "Done"
        break
    case '106':
        issueResolutionID = '1' // When the issue type is "Service Request", set the resolution to "Fixed"
        break
    case '58':
        issueResolutionID = '1' // When the issue type is "Support Request", set the resolution to "Fixed"
        break
    default:
        issueResolutionID = '19' // When any other type of issue, set the resolution to "Expired"
        break
}

issue.setResolutionId(issueResolutionID)
Volodymyr
Contributor
December 14, 2023

How to set a select list custom field

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

// ID of the fields
Long CAUSE = 15200
Long CUSTOMER = 15109

// Current user
def currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()

// Managers
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def issueManager = ComponentAccessor.getIssueManager()
def optionsManager = ComponentAccessor.getOptionsManager()

// Custom fields
def cfCause = customFieldManager.getCustomFieldObject(CAUSE)
def cfCustomer = customFieldManager.getCustomFieldObject(CUSTOMER)

// Config(context) of the custom fields
def cfCauseConfig = cfCause.getRelevantConfig(issue)
def cfCustomerConfig = cfCustomer.getRelevantConfig(issue)

// OptionManager allows you to retrieve values for a specific config(context), for a specific field, for a specific issue
def cfCauseValue = optionsManager.getOptions(cfCauseConfig)?.find { it.toString() == 'Special case' }
def cfCustomerValue = optionsManager.getOptions(cfCustomerConfig)?.find { it.toString() == 'AEG Presents US [AEGW]' }

if (cfCauseValue) {
    issue.setCustomFieldValue(cfCause, cfCauseValue)
}

if (cfCustomerValue) {
    issue.setCustomFieldValue(cfCustomer, cfCustomerValue)
}

// UpdateIssue method needs to update the issue on UI
issueManager.updateIssue(currentUser, issue, EventDispatchOption.ISSUE_UPDATED, false)
Volodymyr
Contributor
December 14, 2023

How to set the default watcher/s

How to set a single user as a watcher:

import com.atlassian.jira.component.ComponentAccessor

def userName = 'user_name' // Name of the user
def userManager = ComponentAccessor.getUserManager()
def user = userManager.getUserByName(userName)

if (!user) {
    log.warn("User ${userName} not found")
    return
}

def watcherManager = ComponentAccessor.getWatcherManager()

// Apply when the issue is of "Change Request" type
if (issue.getIssueTypeId() == '42') {
    watcherManager.startWatching(user, issue)
}

How to set two users as watchers:

import com.atlassian.jira.component.ComponentAccessor

def firstUserName = 'user_name' // Name of the user
def secondUserName = 'user_name' // Name of the user

def userManager = ComponentAccessor.getUserManager()
def firstUser = userManager.getUserByName(firstUserName)
def secondUser = userManager.getUserByName(secondUserName)

if (!firstUser) {
    log.warn("User with name ${firstUserName} not found")
}

if (!secondUser) {
    log.warn("User with name ${secondUserName} not found")
}

def watcherManager = ComponentAccessor.getWatcherManager()

// Apply when the issue is of "Bug" type
if (issue.getIssueTypeId() == '1') {
    watcherManager.startWatching(firstUser, issue)
    watcherManager.startWatching(secondUser, issue)
}

How to set all the people from DL into watchers:

import com.atlassian.jira.component.ComponentAccessor

def DL = 'WFT Business Desk Atlassian Support' // Replace the name to your DL

def groupManager = ComponentAccessor.getGroupManager()
def watcherManager = ComponentAccessor.getWatcherManager()

def membersOfDL = groupManager.getUsersInGroup(DL, false) // without inactive users

if (membersOfDL) {
    // Use only one method and delete the others
    // Using enhanced for-loop
    for (user in membersOfDL) {
        watcherManager.startWatching(user, issue)
    }
    // Using FOR
    for (int i = 0; i < membersOfDL.size(); i++) {
        watcherManager.startWatching(membersOfDL[i], issue)
    }

    // Using EACH
    membersOfDL.each {
        watcherManager.startWatching(it, issue)
    }

    // Using FOREACH
    for (member in membersOfDL) {
        watcherManager.startWatching(member, issue)
    }
}
Volodymyr
Contributor
December 14, 2023

How to sum up two customfields of type "Number Field"

import com.atlassian.jira.component.ComponentAccessor

// Issuetype should be Bug(1), Task(3), or Story(7)
if (!['1', '3', '7'].contains(issue.getIssueTypeId())) {
    return
}

// Get customfields
def cfStoryPoints = ComponentAccessor.getCustomFieldManager().getCustomFieldObject(10004)
def cfQAEffort = ComponentAccessor.getCustomFieldManager().getCustomFieldObject(24600)

// Get the customfields for the current issue
def cfStoryPointsValue = issue.getCustomFieldValue(cfStoryPoints)
def cfQAEffortValue = issue.getCustomFieldValue(cfQAEffort)

/*
We need two options to work with:
1. "QA Effort" and "Story Points" are not empty/null.
2. "QA Effort" is not empty/null but "Story Points" is, so copy its value to "Story Points".
*/

if (cfQAEffortValue != null && cfStoryPointsValue != null) {
    issue.setCustomFieldValue(cfStoryPoints, cfStoryPointsValue.toDouble() + cfQAEffortValue.toDouble())
} else if (cfQAEffortValue != null && cfStoryPointsValue == null) {
    issue.setCustomFieldValue(cfStoryPoints, cfQAEffortValue.toDouble())
}
Volodymyr
Contributor
December 14, 2023

How to transition an issue(Script console) or its parent(Post function)

// How to transition an issue
import com.atlassian.jira.issue.IssueInputParametersImpl
import com.atlassian.jira.component.ComponentAccessor

def currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
def issue = ComponentAccessor.getIssueManager().getIssueByCurrentKey('ROYALPITA-2')

def workflowManager = ComponentAccessor.getWorkflowManager()
def issueService = ComponentAccessor.getIssueService()

def workflow = workflowManager.getWorkflow(issue)

/*
You can find out all the following statuses from the current one by name or ID, to make it easier to identify the right one.
return workflow.getLinkedStep(issue.getStatus()).getActions()
Result: [Reminder, Resolve Issue (fieldscreen), Close Issue (fieldscreen), Stop Progress]
return workflow.getLinkedStep(issue.getStatus()).getActions().id
Result: [711, 5, 2, 301]
*/

// ID of the needed transition
int actionID = 301

// Checking the validity of the transition. Check the user's role, conditions, and validators of the transition.
def transitionValidationResult = issueService.validateTransition(currentUser, issue.id, actionID, new IssueInputParametersImpl())

// Transition the issue only if possible
issueService.transition(currentUser, transitionValidationResult)

 

// How to transition its parent
import com.atlassian.jira.issue.IssueInputParametersImpl
import com.atlassian.jira.component.ComponentAccessor

def currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
def parent = issue.getParentObject()

def workflowManager = ComponentAccessor.getWorkflowManager()
def issueService = ComponentAccessor.getIssueService()

def workflow = workflowManager.getWorkflow(parent)

if (workflow.getLinkedStep(parent.getStatus()).id == 2) {
    int actionID = 171
    def transitionValidationResult = issueService.validateTransition(currentUser, parent.id, actionID, new IssueInputParametersImpl())

    if (transitionValidationResult) {
        issueService.transition(currentUser, transitionValidationResult)
    }
}
Volodymyr
Contributor
December 14, 2023

How to transition an issue if all its linked issues are closed/resolved

/*
How it works:
1. For the current issue, we need to retrieve all the issues that refer to it.
That is, the current issue "blocks" another issue, and it refers to the current as "is blocked by".
2. If the retrieved issue is in BLOCKED status and refers to others that are already in statuses CLOSED or RESOLVED,
and the current issue moves to status CLOSED/RESOLVED last, then change the status of the retrieved issue to OPEN.
*/

import com.atlassian.jira.issue.IssueInputParametersImpl
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.component.ComponentAccessor

def currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()

// Get managers
def issueLinkManager = ComponentAccessor.getIssueLinkManager()
def workflowManager = ComponentAccessor.getWorkflowManager()
def issueService = ComponentAccessor.getIssueService()

// All outward issues with "Blocked" type which means we retrieve all issues that the current issue "Blocks"
def outwardIssues = issueLinkManager.getLinkCollection(issue, currentUser).getOutwardIssues('Blocked')

if (outwardIssues) {
    outwardIssues.each {
        def linkedIssue = it as MutableIssue
        def workflow = workflowManager.getWorkflow(linkedIssue)
        def allIssueLinks = issueLinkManager.getLinkCollection(linkedIssue, currentUser).getAllIssues()

        // If this issue IS in "Blocked" status and all its linked issues ARE in Closed/Resolved statuses, transition the issue to "Open" status
        if (linkedIssue.status.id == '10079' && allIssueLinks.every {itLink -> (itLink.status.id in ['5', '6'])}) {
            int actionID = 101
            def transitionValidationResult = issueService.validateTransition(currentUser, linkedIssue.id, actionID, new IssueInputParametersImpl())
            issueService.transition(currentUser, transitionValidationResult)
        }
    }
}

 From the real case:

image2022-10-3_15-58-59.png

  1. When 6_1 is resolved, 6_2 and 6_3 should get opened.
  2. When 6_2 and 6_3 are both resolved, 6_4 should get opened.
  3. When 6_4 is resolved, 6_5 should get opened.
Volodymyr
Contributor
December 14, 2023

"Epic Status" field should be equal to the status category

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

// Only applies to "Epic" issuetype
if (!issue.getIssueTypeId() == '6') {
    return
}

// Current user
def currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()

// Customfield "Epic Status", its Config(Context), and its options
def cfEpicStatus = ComponentAccessor.getCustomFieldManager().getCustomFieldObject(14502)
def cfEpicStatusConfig = cfEpicStatus.getRelevantConfig(issue)
def cfEpicStatusOptions = ComponentAccessor.getOptionsManager().getOptions(cfEpicStatusConfig)

def currentStatusCategory = issue.getStatus().statusCategory.name

/*
We need to set the value of the customfield "Epic Status" based on the current status.
The values of this field are very similar to the status categories,
so we use the status category to specify the field value.
StatusColor - StatusCategory: Black = New, Blue = "In Progress", Green = Complete
*/

switch(currentStatusCategory) {
    case 'New':
        issue.setCustomFieldValue(cfEpicStatus, cfEpicStatusOptions?.find { it.toString() == 'To Do' })
        break
    case 'In Progress':
        issue.setCustomFieldValue(cfEpicStatus, cfEpicStatusOptions?.find { it.toString() == 'In Progress' })
        break
    case 'Complete':
        issue.setCustomFieldValue(cfEpicStatus, cfEpicStatusOptions?.find { it.toString() == 'Done' })
        break
}

ComponentAccessor.getIssueManager().updateIssue(currentUser, issue, EventDispatchOption.ISSUE_UPDATED, false)
Volodymyr
Contributor
December 14, 2023

Summing "Story Points" of all sub-tasks and adding the value to the parent

import com.atlassian.jira.component.ComponentAccessor

// Works for Story issue type only
if (!issue.getIssueTypeId() == '7') {
    return
}

// Get customfield "Story Points" and creating a variable to sum up all values 
def cfStoryPoints = ComponentAccessor.getCustomFieldManager().getCustomFieldObject(10004)
Double StoryPointsSum = 0

// Iterate over each sub-task and add its value to StoryPointsSum, if any
issue.getSubTaskObjects().each {
    def subtaskStoryPointsValue = it.getCustomFieldValue(cfStoryPoints)
    if (subtaskStoryPointsValue) {
        StoryPointsSum += subtaskStoryPointsValue?.toDouble()
    }
}

// If the parent has a value in the field "Story Points",
// sum it with the total value of all the sub-tasks;
// if not, add only the total value of the sub-tasks.
if (issue.getCustomFieldValue(cfStoryPoints)) {
    issue.setCustomFieldValue(cfStoryPoints, StoryPointsSum + issue.getCustomFieldValue(cfStoryPoints))
} else if (!issue.getCustomFieldValue(cfStoryPoints)) {
    issue.setCustomFieldValue(cfStoryPoints, StoryPointsSum)
}
Volodymyr
Contributor
December 14, 2023

How to find the first and last day of the month when creating an issue and specify these values in customfields of Date type

Thanks to Peter-Dave Sheehan for the advice and help with the script

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

def currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()

// The day and time when the issue is created
def today = new Date()

// Using the variable above, we can find the first and last days of the month
def firstDayOfCurrentMonth = today.toLocalDate().withDayOfMonth(1).toDate().toTimestamp()
def lastDayOfCurrentMonth = today.toLocalDate().plusMonths(1).withDayOfMonth(1).minusDays(1).toDate().toTimestamp()

// Customfields of type Date
def cfPlannedStart = ComponentAccessor.getCustomFieldManager()?.getCustomFieldObject(11700)
def cfPlannedEnd = ComponentAccessor.getCustomFieldManager()?.getCustomFieldObject(11701)

// Set the above values in these fields
issue.setCustomFieldValue(cfPlannedStart, firstDayOfCurrentMonth)
issue.setCustomFieldValue(cfPlannedEnd, lastDayOfCurrentMonth)

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

 

Yanick Schlatter January 12, 2024

Hi Volodymyr

I wanted to try your Code to see if it works for me. But sadly it doesnt work for me, maybe you can help me:

2024-01-12 14_58_54-Script Console - Jira - Brave.png

The result is: 

org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed: Script1.groovy: 1: unable to resolve class com.atlassian.jira.component.ComponentAccessor @ line 1, column 1. import com.atlassian.jira.component.ComponentAccessor ^ 1 error at com.adaptavist.sr.cloud.workflow.AbstractScript.parseScript(AbstractScript.groovy:50) at com.adaptavist.sr.cloud.workflow.AbstractScript.evaluate(AbstractScript.groovy:33) at com.adaptavist.sr.cloud.events.ScriptExecution.run(ScriptExecution.groovy:29) at ConsoleScriptExecution1_groovyProxy.run(Unknown Source)

Volodymyr
Contributor
January 12, 2024

Hi @Yanick Schlatter

I haven't encountered this error yet, so I need more information.
What platform and version of JIRA?
What is the version of Scriptrunner?

Has this error happened before?
Have you tried adding other imports and what was the result?

Thank you.

 

Edit:
Could you please send your version of the script or is the same as mine?

Volodymyr
Contributor
February 16, 2024

How to add a user who logs time to a multiple userpicker customfield (Listener)

/*
When the one event, WorklogCreatedEvent, occurs, the current listener runs.
When a worklog is created in an issue, its creator is added to the field "Contributors" (User Picker (multiple users)).
If there is an already deleted or inactive user in the field, such values are not deleted while updating.
*/

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

// Worklog, Issue, Worklog Creator
def worklog = event?.getWorklog()
def issue = ComponentAccessor.getIssueManager().getIssueObject(worklog?.getIssue()?.getId())
def userCreatedWorklog = worklog?.getAuthorObject()

// Customfield Contributors and its values for the issue
def cfContributors = ComponentAccessor.getCustomFieldManager().getCustomFieldObject(25902)
def cfContributorsValues = issue.getCustomFieldValue(cfContributors) as List<ApplicationUser>

// List to add the first user to the field
def firstContributor = [] as List<ApplicationUser>

// If the field is empty, we need to add the first user
// If the field already contains a user who logs time, do nothing
// If the field doesn't contain a user who logs time, add it to the existing values
if (cfContributorsValues.equals(null)) {
    firstContributor.add(userCreatedWorklog)

    issue.setCustomFieldValue(cfContributors, firstContributor)
    ComponentAccessor.getIssueManager().updateIssue(userCreatedWorklog, issue, EventDispatchOption.ISSUE_UPDATED, false)
} else if (cfContributorsValues.contains(userCreatedWorklog)) {
    return
} else {
    cfContributorsValues << userCreatedWorklog

    issue.setCustomFieldValue(cfContributors, cfContributorsValues)
    ComponentAccessor.getIssueManager().updateIssue(userCreatedWorklog, issue, EventDispatchOption.ISSUE_UPDATED, false)
}

 

Volodymyr
Contributor
March 14, 2024

How to display different values for the Resolution or Priority field (Behaviours)

The script needs to be added to Initialiser and it will display the values regardless of whether there is jira.field.resolution property or not.
1. The script also sets values independent of those available to the project.
2. The script can also be used to change the order of the values, which cannot be done through properties.

import static com.atlassian.jira.issue.IssueFieldConstants.RESOLUTION

switch (underlyingIssue.getIssueTypeId()) {
    // Bug
    // Sub-bug
    case '1':
    case '78':
        // You can use the names or IDs of entities
        // Fixed, Rejected, "Cannot Reproduce", Duplicate, "Won't Fix", "Functions as intended"
        getFieldById(RESOLUTION).setFieldOptions(['1', '9', '5', '3', '2', '14'])
        break
    default:
        // You can use the names or IDs of entities
        // Done, Canceled, Obsolete, "Out of Scope"
        getFieldById(RESOLUTION).setFieldOptions(['6', '20', '18', '22'])
        break
}
import static com.atlassian.jira.issue.IssueFieldConstants.PRIORITY

// Only when creating an issue
if (underlyingIssue) {
    return
}

// Only for Bugs
if (getIssueContext().getIssueTypeId() == '1') {
    // You can use the names or IDs of entities
    // Trivial, Minor, Medium, Major, Critical, Blocker
    getFieldById(PRIORITY).setFieldOptions(['5', '4', '10305', '3', '2', '1'])
}

 

Volodymyr
Contributor
April 2, 2024

How to make a field required and contain a specific value (Behaviours)

import static com.atlassian.jira.issue.IssueFieldConstants.COMPONENTS
import com.onresolve.jira.groovy.user.FieldBehaviours
import org.apache.log4j.Logger
import org.apache.log4j.Level
import groovy.transform.BaseScript

@BaseScript FieldBehaviours fieldBehaviours
def log = Logger.getLogger(getClass())
log.setLevel(Level.DEBUG)

// Only works when creating an issue
if (underlyingIssue) {
return
}

def issueType = getIssueContext().getIssueType()

// Doesn't work for Sub-tasks or Test(105) issue type
if (issueType.isSubTask() || issueType.getId() == '105') {
return
}

// Component/s field and its value/s
def componentsField = getFieldById(COMPONENTS)
def componentsFieldValue = componentsField.getValue()

// The field is always required and has no description
componentsField.setRequired(true)
componentsField.setDescription('')

// Always show the following error if there isn't at least one component starting with "C_"
if (componentsFieldValue.any { it.getAt('name').toString().startsWith('C_') }) {
componentsField.clearError()
} else {
componentsField.setError('There should be at least one component that starts with "C_", so please add it.')
}

 

Volodymyr
Contributor
April 5, 2024

How to create a sub-task for a specific label from the list of labels
If the ticket contains one of the listed labels, create a sub-task with its name

import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.component.ComponentAccessor

// Only works for Story, Change request, and Improvement
if (!['7', '42', '4'].contains(issue.getIssueTypeId())) {
return
}

// All current labels
def issueLabels = issue.getLabels() as List
// All needed labels. A sub-task will be created for each of them.
List<String> requiredLabels = ['ba', 'design', 'qa', 'be', 'fe']

// Method that creates a sub-task
def createNewSubtask(String summary) {
def currentUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def parentIssue = issue

def issueManager = ComponentAccessor.issueManager
def subTaskManager = ComponentAccessor.subTaskManager

MutableIssue newSubtask = ComponentAccessor.issueFactory.getIssue()
newSubtask.setProjectObject(issue.getProjectObject())
newSubtask.setIssueTypeId('5')
newSubtask.setSummary(summary)
newSubtask.setPriority(issue.getPriority())
newSubtask.setReporter(issue.getReporter())

issueManager.createIssueObject(currentUser, newSubtask)
subTaskManager.createSubTaskIssueLink(parentIssue, newSubtask, currentUser)
}

// If there is one of the needed labels in the list, a corresponding sub-task will be created for it
issueLabels.each {
def labelLowerCase = it.toString().toLowerCase()

if (requiredLabels.contains(labelLowerCase)) {
switch (labelLowerCase) {
case 'ba':
createNewSubtask('BA: Elaboration')
break
case 'design':
createNewSubtask('Design: Implementation')
break
case 'qa':
createNewSubtask('QA: Validation')
createNewSubtask('QA: Test Cases')
break
case 'be':
createNewSubtask('BE: Implementation')
break
case 'fe':
createNewSubtask('FE: Implementation')
break
default:
break
}
}
}

 

Volodymyr
Contributor
April 16, 2024

Issue contains the same components as its epic, even when it changes (Listener)
An issue contains the same components as its epic, which is due to a different listener or post function when creating the issue.
This script works when updating an issue, namely updating "Epic Link" field. If the epic changes, the current components are replaced by the components of the new epic. If the epic is deleted, all components are deleted as well.

/*
The script is triggered when an issue is updated - Issue Updated,
Issue contains the same components as its epic, so changing the epic will change the components,
and deleting the epic will remove all components.
*/

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

// Current issue and User who updates it
def issue = event?.issue as MutableIssue
def currentUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser

// This manager contains a history of all changes to the issue
def changeHistoryManager = ComponentAccessor.getChangeHistoryManager()
def changeEpicLink = changeHistoryManager?.getChangeItemsForField(issue, 'Epic Link')

// Stop the script if there are no changes in the issue for "Epic Link" field
if (!changeEpicLink) {
return
}

def previousValue = changeEpicLink?.last()?.fromString
def newValue = changeEpicLink?.last()?.toString

def newEpic = ComponentAccessor.getIssueManager().getIssueObject(newValue)

// If "Epic Link" changes, Issue's components change, and when it's deleted, the components are deleted
if (newEpic) {
if (previousValue != newValue) {
issue.setComponent(newEpic.getComponents())
ComponentAccessor.issueManager.updateIssue(currentUser, issue, EventDispatchOption.ISSUE_UPDATED, false)
}
} else {
issue.setComponent([])
ComponentAccessor.issueManager.updateIssue(currentUser, issue, EventDispatchOption.ISSUE_UPDATED, false)
}

 

Volodymyr
Contributor
May 22, 2024

How to sum up worklogs from tickets (sub-tasks included) and set their sum to their linked Change Request (or other type that acts as the main one - not Epic) (Listener)

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.link.IssueLink

// ID of the issue type, linked tickets and sub-tasks will be retrieved from
final String issueTypeID = '42'

// Worklog to retrieve Issue, its parent, and all subtasks of the parent
def worklog = event?.getWorklog()
def issue = worklog?.getIssue()
def parent = issue?.getParentObject() as MutableIssue
def subtasksOfParent = parent?.getSubTaskObjects()

// Current user who will make changes
def currentUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser

// Managers
def issueManager = ComponentAccessor.getIssueManager()
def issueLinkManager = ComponentAccessor.getIssueLinkManager()

// We need 3 conditions to run the script:
// 1. The issue is a sub-task and its parent is of 'issueTypeID' type
// 2. The issue is not a sub-task and is linked to 'issueTypeID' type
// 3. The issue is a sub-task whose parent is linked to 'issueTypeID' type
def issueIsSubtask = issue?.isSubTask()
def issueHasParentIssueTypeID = !parent ?: parent?.getIssueTypeId() == issueTypeID
def issueLinkedToIssueTypeID = issueLinkManager.getLinkCollection(issue, currentUser, false)?.getAllIssues().any { it.issueTypeId == issueTypeID }
def issueHasParentLinkedToIssueTypeID = !parent ?: issueLinkManager.getLinkCollection(parent, currentUser, false)?.getAllIssues().any { it.issueTypeId == issueTypeID }

// Unless one of the conditions above - exit the script
if (!(issueIsSubtask && issueHasParentIssueTypeID ||
!issueIsSubtask && issueLinkedToIssueTypeID ||
issueIsSubtask && issueHasParentLinkedToIssueTypeID)) {
return
}

// Customfield "Progress"
def cfProgress = ComponentAccessor.getCustomFieldManager().getCustomFieldObject(35906)

// Temporary variable for the sum of worklogs
Long timeSpentTotal = 0

// Three conditions - three variables for each
def issueIsSubtaskAndHasParentIssueTypeID
def issueIsNotSubtaskAndLinkedToIssueTypeID
def issueIsSubtaskAndHasParentLinkedToIssueTypeID

/*
If the issue is a sub-task and its parent of the required type,
we retrieve its sub-tasks, all the linked tickets, and their sub-tasks.

If the issue is a sub-task, we need to retrieve its parent,
from which we find the required linked ticket type,
and from which we retrieve its sub-tasks,
all the linked tickets, and their sub-tasks.

If the issue is a standard issue, we need to retrieve the required linked ticket type,
and from which we retrieve its sub-tasks, all the linked tickets, and their sub-tasks.
*/

if (issueIsSubtask && issueHasParentIssueTypeID) {
issueIsSubtaskAndHasParentIssueTypeID = issueLinkManager?.getLinkCollection(parent, currentUser, false)?.getAllIssues()

issueIsSubtaskAndHasParentIssueTypeID.each { issueInIssueTypeID ->
def subtasksOfissueIsSubtaskAndHasParentIssueTypeID = issueInIssueTypeID.getSubTaskObjects()

subtasksOfissueIsSubtaskAndHasParentIssueTypeID.each { subtask ->
!subtask?.getTimeSpent() ?: (timeSpentTotal += subtask.getTimeSpent())
}

!issueInIssueTypeID?.getTimeSpent() ?: (timeSpentTotal += issueInIssueTypeID.getTimeSpent())
}

if (parent.getCustomFieldValue(cfProgress) != (timeSpentTotal/3600).toDouble()) {
parent.setCustomFieldValue(cfProgress, (timeSpentTotal/3600).toDouble())
issueManager.updateIssue(currentUser, parent, EventDispatchOption.ISSUE_UPDATED, false)
}

} else if (!issueIsSubtask && issueLinkedToIssueTypeID) {
def mainIssue
issueLinkManager.getLinkCollection(issue, currentUser, false)?.getAllIssues().any {
if (it.issueTypeId == issueTypeID) {
mainIssue = it as MutableIssue
}
}

issueIsNotSubtaskAndLinkedToIssueTypeID = issueLinkManager?.getLinkCollection(mainIssue, currentUser, false)?.getAllIssues()

issueIsNotSubtaskAndLinkedToIssueTypeID.each { issueInIssueTypeID ->
def subtasksOfissueIsNotSubtaskAndLinkedToIssueTypeID = issueInIssueTypeID.getSubTaskObjects()

subtasksOfissueIsNotSubtaskAndLinkedToIssueTypeID.each { subtask ->
!subtask?.getTimeSpent() ?: (timeSpentTotal += subtask.getTimeSpent())
}

!issueInIssueTypeID?.getTimeSpent() ?: (timeSpentTotal += issueInIssueTypeID.getTimeSpent())
}

if (mainIssue.getCustomFieldValue(cfProgress) != (timeSpentTotal/3600).toDouble()) {
mainIssue.setCustomFieldValue(cfProgress, (timeSpentTotal/3600).toDouble())
issueManager.updateIssue(currentUser, mainIssue, EventDispatchOption.ISSUE_UPDATED, false)
}
} else if (issueIsSubtask && issueHasParentLinkedToIssueTypeID) {
def mainIssueFromParent
issueLinkManager.getLinkCollection(parent, currentUser, false)?.getAllIssues().any {
if (it.issueTypeId == issueTypeID) {
mainIssueFromParent = it as MutableIssue
}
}

issueIsSubtaskAndHasParentLinkedToIssueTypeID = issueLinkManager?.getLinkCollection(mainIssueFromParent, currentUser, false)?.getAllIssues()

issueIsSubtaskAndHasParentLinkedToIssueTypeID.each { issueInIssueTypeID ->
def subtasksOfissueIsNotSubtaskAndLinkedToIssueTypeID = issueInIssueTypeID.getSubTaskObjects()

subtasksOfissueIsNotSubtaskAndLinkedToIssueTypeID.each { subtask ->
!subtask?.getTimeSpent() ?: (timeSpentTotal += subtask.getTimeSpent())
}

!issueInIssueTypeID?.getTimeSpent() ?: (timeSpentTotal += issueInIssueTypeID.getTimeSpent())
}

if (mainIssueFromParent.getCustomFieldValue(cfProgress) != (timeSpentTotal/3600).toDouble()) {
mainIssueFromParent.setCustomFieldValue(cfProgress, (timeSpentTotal/3600).toDouble())
issueManager.updateIssue(currentUser, mainIssueFromParent, EventDispatchOption.ISSUE_UPDATED, false)
}
}

 

Volodymyr
Contributor
June 27, 2024

How to sum up worklogs from issues and their sub-tasks in the Epic (including Epic itself) and set their sum to it (Listener)

/*
It runs when there are three events: WorlogCreatedEvent, WorklogUpdatedEvent, WorklogDeletedEvent.
If the time is logged in a ticket that has Epic Link, in a sub-task, which parent has Epic Link,
or in an epic itself, then JQL runs and retrieves all the tickets from the epic,
and sums up the logged time that is set in the epic.
*/

import com.atlassian.jira.web.bean.PagerFilter
import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.jql.parser.JqlQueryParser
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.component.ComponentAccessor

// Worklog to retrieve Issue, its parent, and customfield Epic Link
def worklog = event?.getWorklog()
def issue = worklog?.getIssue()
def parent = issue?.getParentObject() as MutableIssue
def cfEpicLink = ComponentAccessor.customFieldManager.getCustomFieldObject(14500)

def issueIsEpic = issue?.getIssueTypeId() == '6'

// Conditions under which the script should be run
def issueIsSubtask = issue?.isSubTask()
def issueIsSubtakAndItsParentHasEpicLink = parent ? (parent?.getCustomFieldValue(cfEpicLink) != null) : false
def issueIsNotSubtaskAndHasEpicLink = issue?.getCustomFieldValue(cfEpicLink) != null

if (!((issueIsSubtask && issueIsSubtakAndItsParentHasEpicLink) ||
issueIsNotSubtaskAndHasEpicLink ||
issueIsEpic)) {
return
}

// If a sub-task - Epic Link from its parent
// If an Issue - its Epic Link
// If an Epic - Epic itself
def epicOfIssue

if (issueIsSubtask) {
epicOfIssue = parent?.getCustomFieldValue(cfEpicLink) as MutableIssue
} else if (issueIsNotSubtaskAndHasEpicLink) {
epicOfIssue = issue?.getCustomFieldValue(cfEpicLink) as MutableIssue
} else if (issueIsEpic) {
epicOfIssue = issue as MutableIssue
}

// "Progress" field, and current user
def cfProgress = ComponentAccessor.customFieldManager.getCustomFieldObject(35906)
def currentUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser

// Managers
def issueManager = ComponentAccessor.issueManager

// Temporary variable for the sum of worklogs
Long timeSpentTotal = 0

// Managers for JQL search
def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser)
def searchService = ComponentAccessor.getComponent(SearchService)

// JQL query and Search to retrieve all issues in Epic
def query = jqlQueryParser.parseQuery("issueFunction in issuesInEpics(\'key = ${epicOfIssue?.key}\') OR key = ${epicOfIssue?.key}")
def search = searchService.search(currentUser, query, PagerFilter.getUnlimitedFilter())
def searchResults = search.getResults().findAll()

searchResults?.each { issueInJQL ->
!issueInJQL?.getTimeSpent() ?: (timeSpentTotal += issueInJQL.getTimeSpent())

issueInJQL?.getSubTaskObjects().each { subtaskOfIssueInEpic ->
!subtaskOfIssueInEpic?.getTimeSpent() ?: (timeSpentTotal += subtaskOfIssueInEpic.getTimeSpent())
}
}

if (epicOfIssue.getCustomFieldValue(cfProgress) != (timeSpentTotal/3600).toDouble()) {
epicOfIssue.setCustomFieldValue(cfProgress, (timeSpentTotal/3600).toDouble())
issueManager.updateIssue(currentUser, epicOfIssue, EventDispatchOption.ISSUE_UPDATED, false)
}

For Console to recalculate the time in already closed or old tickets

import com.atlassian.jira.web.bean.PagerFilter
import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.jql.parser.JqlQueryParser
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.component.ComponentAccessor

String yourProjectKey = 'EPMLITE'

// Epic of the issue or its parent, "Progress" field, and current user
def cfProgress = ComponentAccessor.customFieldManager.getCustomFieldObject(35906)
def currentUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def cfEpicLink = ComponentAccessor.customFieldManager.getCustomFieldObject(14500)

// Managers
def issueManager = ComponentAccessor.issueManager

// Managers for JQL search
def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser)
def searchService = ComponentAccessor.getComponent(SearchService)

// JQL query and Search to retrieve all issues in Epic
def query = jqlQueryParser.parseQuery("project = ${yourProjectKey} AND type = epic")
def search = searchService.search(currentUser, query, PagerFilter.getUnlimitedFilter())
def searchResults = search.getResults().findAll()

// Temporary variable for the sum of worklogs
Long timeSpentTotal

searchResults?.each {
def issueInJQL = issueManager.getIssueObject(it.key)

timeSpentTotal = 0

!issueInJQL?.getTimeSpent() ?: (timeSpentTotal += issueInJQL.getTimeSpent())

// JQL query and Search to retrieve all issues in Epic
def queryInEpic = jqlQueryParser.parseQuery("issueFunction in issuesInEpics(\"key = ${issueInJQL.key}\")")
def searchInEpic = searchService.search(currentUser, queryInEpic, PagerFilter.getUnlimitedFilter())
def searchResultsInEpic = searchInEpic.getResults().findAll()

searchResultsInEpic?.each { issueInEpic ->
!issueInEpic?.getTimeSpent() ?: (timeSpentTotal += issueInEpic.getTimeSpent())

issueInEpic?.getSubTaskObjects().each { subtaskOfIssueInEpic ->
!subtaskOfIssueInEpic?.getTimeSpent() ?: (timeSpentTotal += subtaskOfIssueInEpic.getTimeSpent())
}
}

if (issueInJQL.getCustomFieldValue(cfProgress) != (timeSpentTotal/3600).toDouble()) {
issueInJQL.setCustomFieldValue(cfProgress, (timeSpentTotal/3600).toDouble())
issueManager.updateIssue(currentUser, issueInJQL, EventDispatchOption.ISSUE_UPDATED, false)
}
}
Volodymyr
Contributor
July 3, 2024

How to send an email (Jobs)

import com.atlassian.jira.mail.Email
import com.atlassian.jira.web.bean.PagerFilter
import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.jql.parser.JqlQueryParser
import com.atlassian.jira.component.ComponentAccessor

def currentUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser

// Managers for JQL search
def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser)
def searchService = ComponentAccessor.getComponent(SearchService)

// JQL query and Search
def query = jqlQueryParser.parseQuery('project = EPMGDLT AND type = Intake AND (status = "On Hold" AND due <= now() OR status = "In Progress" AND updatedDate <= -10d)')
def search = searchService.search(currentUser, query, PagerFilter.getUnlimitedFilter())
def searchResults = search.getResults().findAll()

// Only works if the query returns 1 issue or more
if (searchResults.size() != 0) {
def mailServer = ComponentAccessor.mailServerManager.getDefaultSMTPMailServer()
def usernameOfMailServer = mailServer?.username

searchResults.each { issueFromJQLQuery ->
// Emails are set on tickets that have Assignee
if (issueFromJQLQuery.getAssignee()) {
def displayName = issueFromJQLQuery?.getAssignee().displayName
def emailAddress = issueFromJQLQuery?.getAssignee().emailAddress

// \n means a new line
String subject = 'Your attention is needed!'
String body ="Dear ${displayName},\nyour Intake https://jira.epam.com/jira/browse/${issueFromJQLQuery?.key} is awaiting your update.\nPlease, follow the link and leave the actual status in the comment.\nThank You!"

def email = new Email(emailAddress)
email.setFrom(usernameOfMailServer)
email.setSubject(subject)
email.setBody(body)
mailServer.send(email)
}
}
}

 
If you want to test it in the Console

import com.atlassian.jira.mail.Email
import com.atlassian.jira.web.bean.PagerFilter
import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.jql.parser.JqlQueryParser
import com.atlassian.jira.component.ComponentAccessor

def currentUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser

// Managers for JQL search
def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser)
def searchService = ComponentAccessor.getComponent(SearchService)

// JQL query and Search to retrieve all the tickets that the emails will be sent to
def query = jqlQueryParser.parseQuery('project = ROYALPITA AND reporter = "Volodymyr_Kushnir@epam.com" AND type = sub-bug')
def search = searchService.search(currentUser, query, PagerFilter.getUnlimitedFilter())
def searchResults = search.getResults().findAll()

// Only works if the query returns 1 issue or more
if (searchResults.size() != 0) {
def mailServer = ComponentAccessor.mailServerManager.getDefaultSMTPMailServer()
def usernameOfMailServer = mailServer?.username

searchResults.each { issueFromJQLQuery ->
// Emails are set on tickets that have Assignee
if (issueFromJQLQuery.getAssignee()) {
def displayName = issueFromJQLQuery?.getAssignee().displayName
def emailAddress = issueFromJQLQuery?.getAssignee().emailAddress

// \n means a new line
String subject = 'Your attention is needed!'
String body ="Dear ${displayName},\nyour Intake https://jira.epam.com/jira/browse/${issueFromJQLQuery?.key} is awaiting your update.\nPlease, follow the link and leave the actual status in the comment.\nThank You!"

def email = new Email(emailAddress)
email.setFrom(usernameOfMailServer)
email.setSubject(subject)
email.setBody(body)
mailServer.send(email)
}
}
}
Volodymyr
Contributor
July 10, 2024

All tickets in the fixVersion/s should be closed (green color) to move to the next status (Validators)

import com.opensymphony.workflow.InvalidInputException
import com.atlassian.jira.web.bean.PagerFilter
import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.jql.parser.JqlQueryParser
import com.atlassian.jira.component.ComponentAccessor

def currentUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser

// Works only for Build and if all the tickets were closed in its Fix Version/s
if(issue.issueTypeId == '33') {
issue.getFixVersions().each { fixVersion ->
// Managers for JQL search
def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser)
def searchService = ComponentAccessor.getComponent(SearchService)

// JQL query and Search to retrieve all issues in fixVersion
def query = jqlQueryParser.parseQuery("project = '${issue.projectObject?.key}' AND fixVersion = '${fixVersion?.name}'")
def search = searchService.search(currentUser, query, PagerFilter.getUnlimitedFilter())
def searchResults = search.getResults().findAll { it.key != issue.key }

if (!searchResults.every { it.status.statusCategory.id == 3 }) {
throw new InvalidInputException('There are tickets in Fix Version/s that have not been closed. Please move them to final status and repeat the action.')
}
}
}

All tickets in the fixVersion/s should be closed (green color) to move to the next status and release fixVersion (Validators)

import com.opensymphony.workflow.InvalidInputException
import com.atlassian.jira.web.bean.PagerFilter
import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.jql.parser.JqlQueryParser
import com.atlassian.jira.component.ComponentAccessor

def currentUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser

// Works only for Build and if all the tickets were closed in its Fix Version/s
if(issue.issueTypeId == '33') {
issue.getFixVersions().each { fixVersion ->
// Managers for JQL search
def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser)
def searchService = ComponentAccessor.getComponent(SearchService)

// JQL query and Search to retrieve all issues in fixVersion
def query = jqlQueryParser.parseQuery("project = '${issue.projectObject?.key}' AND fixVersion = '${fixVersion?.name}'")
def search = searchService.search(currentUser, query, PagerFilter.getUnlimitedFilter())
def searchResults = search.getResults().findAll { it.key != issue.key }

if (searchResults.every { it.status.statusCategory.id == 3}) {
// If all tickets in a fixVersion are closed(comleted), release the version
ComponentAccessor.versionManager?.releaseVersion(fixVersion, true)
} else {
throw new InvalidInputException('One of the fixVersion/s cannot be released because it doesn\'t have all the tickets closed. Please move them to final status and repeat the action.')
}
}
}
Volodymyr
Contributor
September 4, 2024

How to add a component that is mapped to a customfield value (Listener)

/*
It runs when there are two events: Issue Created, Issue Updated.
If the ticket is Epic, the script continues to run. If it's not, it stops.
Depending on the value in the customfield (select list (single choice)), there should be a mapped value in the components.
If the customfield is empty or contains an unmapped value, the component is not added.
*/

/*
Customfield = Component/s:
[Controllership - 42939] = [C_Finance-LC - 97123]
[Corporate Accounting - 42941] = [C_Finance-AcSS - 97120]
[External reporting - 42942] = [C_Finance-ER - 97117]
[FP&A - 42943] = [C_Finance-FP&A - 97124]
[OTC Business Partnering - 42944] = [C_Finance-BPS - 97118]
[Payroll - 42945] = [C_Finance-AcSS - 97120]
[Treasury - 42947] = [C_Finance-Treasury - 97546]
[Tax Accounting - 42946] = [C_Finance-AcSS - 97120]
*/

import com.atlassian.jira.component.ComponentAccessor

// Issue
def issue = event?.issue

// Works only for Epic
if (issue.getIssueTypeId() != '6') {
return
}

// Current user, and IDs of components to be added/removed depending on the value in the customfield
def currentUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def componentsIdsForMapping = [97123, 97117, 97124, 97118, 97120, 97546]

// Components' value IDs
def componentsValuesId = issue.getComponents()*.id

// Customfield "Business Unit" and its value ID for the issue
def cfBusinessUnit = ComponentAccessor.customFieldManager?.getCustomFieldObject(31903)
def cfBusinessUnitValueId = issue.getCustomFieldValue(cfBusinessUnit)?.optionId

// Managers
def projectComponentManager = ComponentAccessor.projectComponentManager

// Depending on the value in the customfield, we add a specific component
if (issue.getCustomFieldValue(cfBusinessUnit)) {
Long componentIdToSet
// Where Issued = Component/s
switch (cfBusinessUnitValueId) {
// Controllership = C_Finance-LC
case 42939:
componentIdToSet = 97123
break
// Corporate Accounting = C_Finance-AcSS
case 42941:
componentIdToSet = 97120
break
// External reporting = C_Finance-ER
case 42942:
componentIdToSet = 97117
break
// FP&A = C_Finance-FP&A
case 42943:
componentIdToSet = 97124
break
// OTC Business Partnering = C_Finance-BPS
case 42944:
componentIdToSet = 97118
break
// Payroll = C_Finance-AcSS
case 42945:
componentIdToSet = 97120
break
// Treasury = C_Finance-Treasury
case 42947:
componentIdToSet = 97546
break
// Tax Accounting = C_Finance-AcSS
case 42946:
componentIdToSet = 97120
break
default:
// The field is empty or unmapped value
componentIdToSet = 0
break
}

// If Component/s field is empty, set the mapped value
if (issue.getComponents().isEmpty()) {
projectComponentManager.updateIssueProjectComponents(issue, [projectComponentManager?.find(componentIdToSet)])
} else {
// If there is no mapped value among the components, find and add it to the list
if (componentsValuesId.every { it != componentIdToSet}) {
// To delete a previously mapped component if the customfield has a not mapped value
if (componentIdToSet == 0) {
def issueCurrentComponentsValuesId = componentsValuesId as Collection
List<Long> componentsValuesIdToDelete = []

componentsValuesId.each { componentIdToDelete ->
if (componentsIdsForMapping.contains(componentIdToDelete.intValue())) {
componentsValuesIdToDelete << componentIdToDelete
}
}

issueCurrentComponentsValuesId.removeAll(componentsValuesIdToDelete)

def newComponentsToSet = []
issueCurrentComponentsValuesId.each {
newComponentsToSet << projectComponentManager?.find(it)
}

projectComponentManager.updateIssueProjectComponents(issue, newComponentsToSet)
} else {
def issueCurrentComponentsValuesId = componentsValuesId as Collection
List<Long> componentsValuesIdToDelete = []

componentsValuesId.each { componentIdToDelete ->
if (componentsIdsForMapping.contains(componentIdToDelete.intValue())) {
componentsValuesIdToDelete << componentIdToDelete
}
}

issueCurrentComponentsValuesId.removeAll(componentsValuesIdToDelete)

def newComponentsToSet = []
issueCurrentComponentsValuesId.each {
newComponentsToSet << projectComponentManager?.find(it)
}

newComponentsToSet << projectComponentManager?.find(componentIdToSet)

projectComponentManager.updateIssueProjectComponents(issue, newComponentsToSet)
}
}
}
} else {
// If the customfield is empty, the mapped value is removed
if (!issue?.getComponents()?.isEmpty()) {
def issueCurrentComponentsValuesId = componentsValuesId as Collection
List<Long> componentsValuesIdToDelete = []

componentsValuesId.each { componentIdToDelete ->
if (componentsIdsForMapping.contains(componentIdToDelete.intValue())) {
componentsValuesIdToDelete << componentIdToDelete
}
}

issueCurrentComponentsValuesId.removeAll(componentsValuesIdToDelete)

def newComponentsToSet = []
issueCurrentComponentsValuesId.each {
newComponentsToSet << projectComponentManager?.find(it)
}

projectComponentManager.updateIssueProjectComponents(issue, newComponentsToSet)
}
}

 

Volodymyr
Contributor
December 30, 2024

How to add a specific component when a specific epic is chosen (Listener)

/*
When the specified epic is selected in a ticket (when creating/updating the issue),
the specified component is added to the ticket.
The component cannot be deleted while the epic is present, and if the epic is deleted,
the component is not automatically deleted.
*/

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

// The epic that adds the component when selected
String specificEpicToChoose = 'EPMTUBE-13614'
Long specificComponentToAdd = 98258 // C_BSS_OP-Security

def issue = event?.issue as MutableIssue
def cfEpicLink = ComponentAccessor.customFieldManager.getCustomFieldObject(14500)
def cfEpicLinkValue = issue?.getCustomFieldValue(cfEpicLink)
def loggedInUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser

// If the ticket has an epic but a different one - do nothing
if (cfEpicLinkValue?.key != specificEpicToChoose) {
return
}

def requiredComponentIsSet = issue?.getComponents().any { it.id == specificComponentToAdd }

// If the ticket has the specified epic and the specified component - do nothing
if (requiredComponentIsSet) {
return
}

// If the first and second checks (ifs) did not stop the script,
// it means that the specified epic was selected and we should add the component.
def issueExistingComponents = issue?.components
issueExistingComponents << ComponentAccessor.projectComponentManager?.getProjectComponent(specificComponentToAdd)

issue.setComponent(issueExistingComponents)
ComponentAccessor.issueManager.updateIssue(loggedInUser, issue, EventDispatchOption.ISSUE_UPDATED, false)
TAGS
AUG Leaders

Atlassian Community Events