Email Reporter, Watchers, Request Participants and Organizations of Linked Issues When Transitioning

This Post-Function script will allow you to trigger notifications users/customers (reporter, watchers, request participants and organizations) for a linked issue. This was written assuming this would be most helpful to notify users when the linked issues were being resolved, but could fit anywhere in the workflow.

Use Cases:

  1. Your helpdesk team is receiving issues related to a software bug. Your customers' organizations can be added to the helpdesk issue and then receive an email when the software team has closed their linked issue.

  2. Business users may have an IT issue linked as a blocker, but might have no interest in watching an IT team’s issues for updates due to the volume of comments/edit. If they would like to receieve a notification only when it is finished, this script can shield them from all the rest of the JIRA notifications.

Steps

1. Identify the appropriate Workflow

This post-function should live in the JIRA workflow that needs to relay the emails out to other projects. See the use cases above for more detail.

2. Edit the Workflow

You will want to edit the workflow and navigate to the specific transition where you want these emails to trigger. Keep in mind, you may have to update the verbiage of the email to account for different transitions or meanings. See below for more on that.

3. Add the script

Add the script to a custom script post-function.

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.mail.Email
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.servicedesk.api.organization.CustomerOrganization
import com.atlassian.servicedesk.api.organization.OrganizationService
import com.atlassian.servicedesk.api.util.paging.SimplePagedRequest
import com.onresolve.scriptrunner.runner.customisers.WithPlugin
@WithPlugin("com.atlassian.servicedesk")

final def LINK_NAME = "causes"
def issueLinks = ComponentAccessor.getIssueLinkManager().getOutwardLinks(issue.getId())
def causedByIssues = ComponentAccessor.getIssueLinkManager().getOutwardLinks(issue.getId())?.findAll {it.issueLinkType.outward == LINK_NAME}
if (! causedByIssues) {
    log.debug "Does not cause any issues"
    return
}

causedByIssues.each {
    def destinationIssue = it.destinationObject
    def adminUser = ComponentAccessor.userManager.getUserByKey("admin") // this should be an admin you wish to use inside the script OR currentUser
    def watcherManager = ComponentAccessor.watcherManager

    def strSubject = "An issue linked to ${destinationIssue.getKey()} has been resolved"
    def baseURL = ComponentAccessor.getApplicationProperties().getString("jira.baseurl")
    def strIssueURL = "<a href='${baseURL}/browse/${issue.getKey()}'>${issue.getKey()}</a>"
    def strDestinationURL = "<a href='${baseURL}/browse/${destinationIssue.getKey()}'>${destinationIssue.getKey()}</a>"
    def strBody = """${strIssueURL} has been resolved.
 This issue has a &quot;${LINK_NAME}&quot; issue link which points to ${strDestinationURL}.
 You received this notification because you are watching ${strDestinationURL}."""
    def emailAddressTo = destinationIssue.reporterUser ? destinationIssue.reporterUser.emailAddress : adminUser.emailAddress
    def emailAddressesCC = []

    emailAddressesCC = watcherManager.getWatchers(destinationIssue, Locale.ENGLISH)?.collect {it.emailAddress}
    emailAddressesCC.addAll(getOrganizationMembersEmailsInIssue(destinationIssue, adminUser))
    emailAddressesCC.addAll(getParticipantsEmailsInIssue(destinationIssue))
    emailAddressesCC = emailAddressesCC.unique()
    emailAddressesCC = emailAddressesCC.join(",")

    sendEmail(emailAddressTo, emailAddressesCC, strSubject, strBody)
}

def sendEmail(String to, String cc, String subject, String body) {

    log.debug "Attempting to send email..."
    def mailServer = ComponentAccessor.getMailServerManager().getDefaultSMTPMailServer()
    if (mailServer) {
        Email email = new Email(to)
        email.setCc(cc)
        email.setSubject(subject)
        email.setBody(body)
        email.setMimeType("text/html")
        mailServer.send(email)
        log.debug("Mail sent to (${to}) and cc'd (${cc})")
    } else {
        log.warn("Please make sure that a valid mailServer is configured")
    }
}

List <String> getOrganizationMembersEmailsInIssue(Issue issue, ApplicationUser adminUser) {
    def organisationService = ComponentAccessor.getOSGiComponentInstanceOfType(OrganizationService)
    def cf = ComponentAccessor.customFieldManager.getCustomFieldObjectByName("Organizations")
    def emailAddresses = []
    (issue.getCustomFieldValue(cf) as List <CustomerOrganization>)?.each {
        def pageRequest = new SimplePagedRequest(0, 50)
        def usersInOrganizationQuery = organisationService.newUsersInOrganizationQuery().pagedRequest(pageRequest).customerOrganization(it).build()
        // this is a paged response, it will return only the first 50 results, if you have more users in an organization
        // then you will need to iterate though all the page responses
        def pagedResponse = organisationService.getUsersInOrganization(adminUser, usersInOrganizationQuery)
        if (pagedResponse.isLeft()) {
            log.warn pagedResponse.left().get()
        }
        else {
            emailAddresses.addAll(pagedResponse.right().get().results.collect {it.emailAddress})
        }
    }

    emailAddresses
}

List <String> getParticipantsEmailsInIssue(Issue issue) {
    def cf = ComponentAccessor.customFieldManager.getCustomFieldObjectByName("Request participants")
    def cfVal = issue.getCustomFieldValue(cf) ?.collect {it.emailAddress}

    cfVal
}
You will need to edit this script

Variables to look to edit are…​

a) LINK_NAME - this determines which issue link relation you are looking for as the trigger for the emails. It currently holds a value of "causes." This means that the post-function looks to check if the current transitioning issue "causes" any other issues, then checks those other issues for watchers and notifies them.

b) adminUser - This should be a user that will persist with the ability to query Organizations and to receive the email if no reporter exists.

c) strSubject - This is a short summary of the email and may need to be adjusted if you use this post-function to continue into another unresolved status.

d) strBody - This is an in-depth description of what issues were affected and why the user is receiving the notification. It also references resolving an issue so definitely change this if you are not using a transition landing in a resolved state.

Also, keep in mind…​

a) This script only notifies for one issue link type. If you want it to notify on multiple, you would need to add a string array or another mechanism to this script or duplicate the script.

b) The more watchers/Request participants/Organization customers are on the linked issue, the longer the post-function will take to execute.

c) The script assumes that both issues are on the same instance of JIRA, meaning they will have the same URL.

d) The script CCs everyone at once to avoid the time consuming activity of sending out tons of email.

For how-to questions please ask on Atlassian Answers where there is a very active community. Adaptavist staff are also likely to respond there.

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