Script Runner: Sending an email to a scripted field

kitensei June 27, 2016

This is what I want to achieve:

Once an issue is opened we "Qualify" it and defined a team that will handle it, that team is identified by an email, and each team is a group in our JIRA system, eg.:

  • development [AT] acme.com
  • it [AT] acme.com
  • etc.

I want that team to be notified that an issue has been assigned to them.

I have written a scripted field that get the email address of the team based on the Tempo Team selected:

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.user.util.UserManager
import com.atlassian.jira.config.properties.APKeys
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.fields.CustomField
import org.apache.http.HttpRequestInterceptor
import org.apache.http.HttpRequest
import org.apache.http.HttpResponse
import org.apache.http.protocol.HttpContext
import groovyx.net.http.RESTClient
import com.atlassian.jira.user.ApplicationUser
/*
 * Initialize the API connection by getting the JIRA-SD base url
 * and building the Rest client with the fields needed in order
 * to register a worklog.
 */
String baseUrl = ComponentAccessor.getApplicationProperties().getString(APKeys.JIRA_BASEURL)
RESTClient http = new RESTClient(baseUrl)
HttpResponse response
def statusCode
String apiuser = 'admin'
String apipasswd = 'admin'
String auth = "${apiuser}:${apipasswd}".bytes.encodeBase64().toString()
// append the authentication header
http.client.addRequestInterceptor(new HttpRequestInterceptor() {
    void process(HttpRequest httpRequest, HttpContext httpContext) {
        httpRequest.addHeader('Authorization', 'Basic ' + auth)
        httpRequest.addHeader('X-Atlassian-Token', "no-check")
    }})

// get the issue Team identifier, and retrieve its name with the Tempo API
CustomFieldManager customFieldManager = (new ComponentAccessor()).getCustomFieldManager()
CustomField teamCustomField = customFieldManager.getCustomFieldObjectByName('Team')
String tempoTeamId
String tempoTeamName
ApplicationUser user
try {
    tempoTeamId = teamCustomField.getValue(issue)
    if (!tempoTeamId) throw new Exception()
    response = http.get(path:"/rest/tempo-teams/1/team/${tempoTeamId}")
    statusCode = response.getStatusLine().statusCode
    tempoTeamName = response.getData().name
    user = getUserByTempoTeam(tempoTeamName)
    if (!user) throw new Exception()
    return user
} catch (Exception ex) {
    return null
}

def getUserByTempoTeam(teamName) {
    UserManager userManager = ComponentAccessor.getUserManager()
    def users = [
        0:  [name: 'Accounting', username: 'compta [AT] acme.ch'],
        1:  [name: 'Direction', username: 'direction [AT] acme.ch'],
        2:  [name: 'Exploit-Dev', username: 'dev [AT] acme.ch'],
        3:  [name: 'Exploit-NOC', username: 'noc [AT] acme.ch'],
        4:  [name: 'Exploit-System', username: 'systeme [AT] acme.ch'],
        5:  [name: 'Exploit-VOIP', username: 'systeme [AT] acme.ch'],
        6:  [name: 'Infra-Dev', username: 'dev [AT] acme.ch'],
        7:  [name: 'Infra-NOC', username: 'noc [AT] acme.ch'],
        8:  [name: 'Infra-System', username: 'systeme [AT] acme.ch'],
        9:  [name: 'Sales', username: 'vente [AT] acme.ch'],
        10: [name: 'Security', username: 'security [AT] acme.ch'],
        11: [name: 'Support-N1', username: 'dev [AT] acme.ch']
    ]
    def match = users.find { it.value.name.toLowerCase() == teamName.toLowerCase() }
    if (!match) throw new Exception()
    return userManager.getUserByKey(match.value.username)
}

This ends up in filling the scripted field correctly:

image2016-6-27 16:40:4.png

 

But then, on a "Qualify Issue" event, I want to send an email to that user:

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.user.util.UserManager
import com.atlassian.jira.issue.watchers.WatcherManager
import com.atlassian.jira.config.properties.APKeys
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.fields.CustomField
import org.apache.http.HttpRequestInterceptor
import org.apache.http.HttpRequest
import org.apache.http.HttpResponse
import org.apache.http.protocol.HttpContext
import groovyx.net.http.RESTClient
import groovyx.net.http.HttpResponseException
import com.atlassian.jira.user.ApplicationUser
// get the issue Team identifier, and retrieve its name with the Tempo API
CustomFieldManager customFieldManager = (new ComponentAccessor()).getCustomFieldManager()
CustomField fld = customFieldManager.getCustomFieldObjectByName('Assigned-team')
String tempoTeamId
String tempoTeamName
ApplicationUser user
user = fld.getValue(event.issue)
log.error(user)

And "User" is always empty, so no mail is sent.

Can someone help me with that ?

My other option would be to add the user to the watchers list instead of a scripted field, but he will be notified everytime a notification scheme send an email to all watchers, and I'd like to avoid that.

 

Thank you,

Cheers, Giulio

 

1 answer

0 votes
Vasiliy Zverev
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
June 27, 2016

Try at first to get class of returned value for custom field like this:

log.error(ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName('Assigned-team').getValue().getClass().getName() )

kitensei June 27, 2016

Returns an error:

2016-06-28 08:58:39,273 http-bio-8443-exec-56 WARN admin 538x99379x2 fkmhrg 10.0.105.52,192.168.15.13 /rest/scriptrunner/latest/user/exec/ [c.o.s.r.rest.common.UserScriptEndpoint] Script console script failed:
groovy.lang.GroovyRuntimeException: Ambiguous method overloading for method com.atlassian.jira.issue.fields.config.manager.FieldConfigSchemeManagerImpl#getRelevantConfigScheme.
Cannot resolve which method to invoke for [null, class com.atlassian.jira.issue.fields.CustomFieldImpl] due to overlapping prototypes between:
        [interface com.atlassian.jira.issue.context.IssueContext, interface com.atlassian.jira.issue.fields.ConfigurableField]
        [interface com.atlassian.jira.project.Project, interface com.atlassian.jira.issue.fields.ConfigurableField]
        at com.atlassian.jira.issue.fields.config.manager.FieldConfigSchemeManager$getRelevantConfigScheme$0.call(Unknown Source)
        at com.onresolve.scriptrunner.customfield.GroovyCustomField.getRelevantConfig(GroovyCustomField.groovy:322)
        at com.onresolve.scriptrunner.customfield.GroovyCustomField$getRelevantConfig$0.callCurrent(Unknown Source)
        at com.onresolve.scriptrunner.customfield.GroovyCustomField$getRelevantConfig$0.callCurrent(Unknown Source)
        at com.onresolve.scriptrunner.customfield.GroovyCustomField.getValueFromIssue(GroovyCustomField.groovy:159)
        at com.atlassian.jira.issue.fields.CustomFieldImpl.getValue(CustomFieldImpl.java:386)
        at Script427.run(Script427.groovy:25)
kitensei June 27, 2016

The weird thing is that the error log is displayed in the console (catalina.out) but not on the web console, is this normal ?

It seems like there is a kind of delay preventing the scripted field to work correctly.

customfield.png

Vasiliy Zverev
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
June 27, 2016

Add this to import header:

import com.atlassian.jira.component.ComponentAccessor

I also recommend you to use any IDE to write code to avoid such mistakes. I use Intellij IDEA

kitensei June 27, 2016

The import is present, I have no errors about imports.

I think my problem is a problem of "order of things done":

  1. Issue is qualified (transition)
  2. Event "Issue qualified" is raised
  3. (On event: Issue qualified) Field "Assigned-team" is filled (scripted field)
  4. (On event: Issue qualified) Send an email to "Assigned-team"

The problem is that the two event listeners are triggered parallelly, therefore the scripted field value isn't set yet.

 

How can I handle this ? Should I generate two events, one for qualification and one for sending the email to my team ? Or should I prefer a "watcher" approach (set the user as a watcher instead of a scripted field, and send an email to all watchers) but I'm not sure the problem will be any different.

 

 

Vasiliy Zverev
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
June 27, 2016

I would recommend to set custom field value not into event listener, but into a postfunction. This is more robust approach. 

 

kitensei June 28, 2016

Ok that worked great, but now no email is sent with the Issue Qualified event.

image2016-6-28 12:25:58.png

The field is set properly with an existing user (that's what the script is doing) then an event is raised and configured in the JIRA Events, but no email is sent, never, the event is raised because adding an Event Listener it get called.

 

Any ideas ?

Vasiliy Zverev
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
June 28, 2016

Are you sure than event is handeled by the listener?

Add any log messages to make it clear.

Suggest an answer

Log in or Sign up to answer