Skip to main content
Sending data from Jira to XMatters (Gotta Script'em All 4/6)
Share on socials
An illustration of a Jira ticket
Krisztian Kovacs
Krisztian Kovacs
12th June, 2018
Jira
Data center icon
Cloud icon
Server icon

Sending data from Jira to XMatters (Gotta Script'em All 4/6)

In the fourth instalment of the Gotta Script-em All series, we will use a dialog box to send data to XMatters.
Welcome back to the fourth post in the Gotta Script-em All series where we explore the capabilities of Jira through a step-by-step guide to creating seamless integration links between Jira and other tools. Make sure you check out the previous three posts in this series if you haven't yet.
In part 1 we explored how to create a custom button and rest endpoint on your Jira user interface. In part 2 we go back to basics with a guide to writing clean code. In part 3 we resume our quest for achieving custom user interface (UI) goodness using ScriptRunner and get deeper into the nitty-gritty of manipulating custom fields in Jira.
In this post (part 4), now that we have created a dialog box we will use it to send data to XMatters.
First, we need to build a simple code that creates a Javascript Object Notation (JSON) structure that can be sent to xMatters as a message or in technical terms a payload. Then we teach xMatters how to read the JSON payload and sit back with a feeling of achievement.
xMatters Message
We have a happy dialog now. It's editable but other than closing the dialog, there isn't much we can achieve with it. Unless we somehow figure out how to send the text field to xMatters.
The plan is simple:
  1. Get information from text area when clicking on "Send xMatters"
  2. Send information to xMatters using RestAPI
However, executing our plan is a bit complicated.

The challenge

The challenge is that until now we've been using JavaScript in the dialog. And as you can move data from Javascript back to ScriptRunner, you know the saying: "once you go JavaScript, you never go back." When the user clicks on the "Send xMatters" button, it's entirely JavaScript.
So we need to use JavaScript to send the data using RestAPI. Atlassian have already thought of that. It's a big security no-no. You can't just go and send data out aimlessly to the universe.
New plan:
  1. Get information from text area when clicking on "Send xMatters"
  2. Send information to a relay script using RestAPI
  3. Collect the data in the relay script and send it to xMatters
It's just one more extra step. How hard can it be?

What's JSON?

JSON and FREDY first featured in the Hollywood blockbuster... uhm, hang on. My mind has just gone somewhere else... So back to the matter in hand, JSON is a text format (is short for JavaScript Object Notation). Just like an essay has a certain structure, JSON has its own.
However it's standardised throughout the industry and you can use it in many places. Basically it relates to how pieces of software communicate via RestAPI.
In our case the payload looks like this:
1payload = {
2            "subject" : "Urgent Message",
3            "body" : ""
4        }
It's pretty simple. You get to name the variable (e.g. "subject") and the value it contains (e.g. "urgent message").
When you send it to the proper URL, the piece of software on the other, that's listening for incoming data, will try to 'decipher' it. This process is called parsing. It's a fancy name of understanding data in a predetermined form.
In our case, xMatters will expect a "subject" and a "body" as incoming objects and will accept whatever data is presented after the colon.

The tricky part

First let's look at the JavaScript from the previous chapter, now complete with the RestAPI call.
1function submit() {
2        var payload = {
3            "subject" : "Urgent Message",
4            "body" : ""
5        };
6        payload.body = document.getElementById("sr-dialog-textarea").value;
7        var xhttp = new XMLHttpRequest();
8        xhttp.open("POST", "https://MYJIRAINSTANCE.COW/rest/scriptrunner/latest/custom/xMattersRelay", false);
9        xhttp.setRequestHeader("Content-type", "application/json");
10        xhttp.send(JSON.stringify(payload));
11        AJS.dialog2("#sr-dialog").hide();
12    }

So we created a 'payload' with properties such as 'subject' and 'body' and the plan is to take all the data from the text area and put it into the 'body, then send the whole thing to our relay script that will send it to xMatters.
There is one issue we'll have to overcome though. JSON doesn't play well with multi-line strings, and since we want to put our text area into a string (an element within the JSON) that will cause a problem. I know I pretty much seem like an oracle, being aware of the future but since I've been through this already, trust me. #justimetravelthings
Luckily this is where Google comes to the rescue.
1var textarea = $('textarea').val();
2var linebreak = textarea.split('\n');
3var length = linebreak.length;
4var data = [];
5for ( var i = 0 ; i<length ; i++){
6    data.push({ 'line': i , 'content': linebreak[i] });
7    console.log(data);
8}

Now it's just a matter of putting some or all of this Javascript code into the already existing code.
However creating more JSON is not the goal here. Let's just remove the '\n' characters and create one long line instead:
1function submit() {
2    var payload = {
3        "subject" : "Urgent Message",
4        "body" : ""
5    };
6    var textarea = document.getElementById("sr-dialog-textarea").value;
7    var linebreak = textarea.split('\\n');
8    var length = linebreak.length;
9    var data = [];
10    for ( var i = 0 ; i<length ; i++){
11        data = data + " " + linebreak[i];
12    }
13    payload.body = data;
14 
15    var xhttp = new XMLHttpRequest();
16    xhttp.open("POST", "https://tesc-dev501.adaptavist.cloud/rest/scriptrunner/latest/custom/xMattersRelay", false);
17    xhttp.setRequestHeader("Content-type", "application/json");
18    xhttp.send(JSON.stringify(payload));
19    alert(xhttp.responseText);
20    AJS.dialog2("#sr-dialog").hide();
21}

Configuring xMatters

The plan is to create a communication plan in xMatters, configure a message, create a rest end point and put the URL (defined by xMatters) into the relay script.
Communication Plans
First create a communication plan that will house all you need to create your rest end point.
Simple xMatters Message
Then set up a simple message with subject and a body. These two pieces of data will be send by the xMatters preview dialog in Jira.
Set up the integration with basic authentication (username/password), select the simple message form you created in the previous step and then copy the URL. You'll need that URL to paste it into the code of the xMatters relay.
Simple xMatters Integration
Simple Message

The full xMatters dialog script

Always remember to replace the custom field IDs as the script will crash because it won't be able to handle non-existent custom fields.
1import com.atlassian.jira.component.ComponentAccessor
2
3import com.atlassian.jira.issue.Issue
4
5import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
6
7import groovy.transform.BaseScript
8
9import javax.ws.rs.core.MediaType
10
11import javax.ws.rs.core.MultivaluedMap
12
13import javax.ws.rs.core.Response
14
15 
16
17@BaseScript CustomEndpointDelegate delegate
18
19 
20
21import groovy.json.JsonSlurper
22
23import groovyx.net.http.HTTPBuilder
24
25import groovyx.net.http.ContentType
26
27import static groovyx.net.http.Method.*
28
29 
30
31import com.atlassian.jira.issue.fields.rest.json.beans.PriorityJsonBean
32
33import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
34
35import groovy.transform.BaseScript
36
37import org.codehaus.jackson.map.ObjectMapper
38
39 
40
41import javax.ws.rs.core.MultivaluedMap
42
43import javax.ws.rs.core.Response
44
45 
46
47//-------------------------------------------------------------------------
48
49// This script can be applied as a rest-end-point that shows the dialog
50
51// when a user clicks on the 'send xmatters' button
52
53// Currently known button placement for web fragments:
54
55// - jira.issue.tools
56
57//-------------------------------------------------------------------------
58
59// Please change the following so the script would suit your needs:
60
61List<String> CustomFieldIDs = [
62
63        "customfield_10105",
64
65        "customfield_10106"
66
67]
68
69//-------------------------------------------------------------------------
70
71 
72
73static trimIssueKey(String key) {
74
75    key = key.replaceAll(/^\[|]$/, '')
76
77    return key
78
79}
80
81 
82
83static getJiraBaseUrl() {
84
85    def baseUrl = ComponentAccessor.getApplicationProperties().getString("jira.baseurl")
86
87    return baseUrl
88
89}
90
91 
92
93static getCustomFieldData(String key, String customFieldID) {
94
95    Issue issue = ComponentAccessor.issueManager.getIssueByCurrentKey(key)
96
97    return issue.getCustomFieldValue(ComponentAccessor.getCustomFieldManager()
98
99            .getCustomFieldObject(customFieldID))
100
101}
102
103 
104
105static getTextArea(String issueKey, List<String> CustomFieldIDs) {
106
107    return """
108
109This is the actual message that will be sent to xMatters with all the variables we gather from custom fields.
110
111It can be edited here before sent.
112
113  
114
115Custom Field A: ${getCustomFieldData(issueKey, CustomFieldIDs[0])}
116
117Another Custom Field: ${getCustomFieldData(issueKey, CustomFieldIDs[1])}
118
119  
120
121Thank you for your attention.
122
123  
124
125Cheers,
126
127The A Team"""
128
129}
130
131 
132
133static getDialogText(String issueKey, List<String> CustomFieldIDs) {
134
135    return """
136
137<script>
138
139    function submit() {
140
141        var payload = {
142
143            "subject" : "Urgent Message",
144
145            "body" : "moo"
146
147        };
148
149        var textarea = document.getElementById("sr-dialog-textarea").value;
150
151        var linebreak = textarea.split('\\n');
152
153        var length = linebreak.length;
154
155        var data = [];
156
157        for ( var i = 0 ; i<length ; i++) {
158
159            data = data + " " + linebreak[i];
160
161        }
162
163        payload.body = data;
164
165  
166
167        var xhttp = new XMLHttpRequest();
168
169        xhttp.open("POST", "${getJiraBaseUrl()}/rest/scriptrunner/latest/custom/xMattersRelay", false);
170
171        xhttp.setRequestHeader("Content-type", "application/json");
172
173        xhttp.send(JSON.stringify(payload));
174
175        alert(xhttp.responseText);
176
177        AJS.dialog2("#sr-dialog").hide();
178
179    }
180
181    var el = document.getElementById("submit");
182
183    if (el.addEventListener)
184
185        el.addEventListener("click", submit, false);
186
187    else if (el.attachEvent)
188
189        el.attachEvent('onclick', submit);
190
191</script>
192
193<section role="dialog" id="sr-dialog"
194
195    class="aui-layer aui-dialog2 aui-dialog2-medium" aria-hidden="true" data-aui-remove-on-hide="true">
196
197<header class="aui-dialog2-header">
198
199    <h2 class="aui-dialog2-header-main">xMatters Message</h2>
200
201    <a class="aui-dialog2-header-close">
202
203        <span class="aui-icon aui-icon-small aui-iconfont-close-dialog">Close</span>
204
205    </a>
206
207</header>
208
209<div class="aui-dialog2-content">
210
211    <p>Header of the dialog, some text about warnings and whatever....</p>
212
213        <textarea id="sr-dialog-textarea" rows="15" cols="75">
214
215            ${getTextArea(issueKey, CustomFieldIDs)}
216
217        </textarea>
218
219</div>
220
221<footer class="aui-dialog2-footer">
222
223    <div class="aui-dialog2-footer-actions">
224
225        <button class="aui-button" id="submit">Send xMatters</button>
226
227        <button id="dialog-close-button" class="aui-button aui-button-link">Close</button>
228
229    </div>
230
231    <div class="aui-dialog2-footer-hint">This is a footer message</div>
232
233</footer>
234
235</section>
236
237"""
238
239}
240
241 
242
243showXMattersDialog() {
244
245    MultivaluedMap queryParams ->
246
247        String issueKey = queryParams.get("issueKey")
248
249        issueKey = trimIssueKey(issueKey)
250
251        String dialog = getDialogText(issueKey, CustomFieldIDs)
252
253        Response.ok().type(MediaType.TEXT_HTML).entity(dialog).build()
254
255}

The full xMatters relay script

You should also replace the xMatters URL and the username and password. Without that the xMatters rest endpoint will not accept your payload.
1import groovy.transform.Field
2
3 
4
5import com.onresolve.scriptrunner.runner.util.UserMessageUtil
6
7import groovy.json.JsonSlurper
8
9import groovyx.net.http.HTTPBuilder
10
11import groovyx.net.http.ContentType
12
13 
14
15import static groovyx.net.http.Method.*
16
17 
18
19@BaseScript CustomEndpointDelegate delegate
20
21 
22
23import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
24
25import groovy.transform.BaseScript
26
27 
28
29import javax.ws.rs.core.MultivaluedMap
30
31import javax.ws.rs.core.Response
32
33//-------------------------------------------------------------------------
34
35// Don't forget to change the variables
36
37//-------------------------------------------------------------------------
38
39@Field String xMattersURL =
40
41        "https://adaptavist-dev.xmatters.com/api/integration/1/functions/3eb43db8-e0a4-40ff-b1c8-452e89dd4691/triggers"
42
43@Field String xMattersUser = "myaddress@email.com"
44
45@Field String xMattersPassword = "myfancypassword"
46
47//-------------------------------------------------------------------------
48
49 
50
51String issueKey = null
52
53 
54
55xMattersRelay(httpMethod: "POST") {
56
57    MultivaluedMap queryParams,
58
59    def payload ->
60
61        def jsonSlurper = new JsonSlurper()
62
63        def content = jsonSlurper.parseText(payload)
64
65        issueKey = content.key
66
67        def http = new HTTPBuilder(xMattersURL)
68
69        http.request(POST) {
70
71            requestContentType = ContentType.JSON
72
73            headers.'Authorization' =
74
75                    "Basic ${"${xMattersUser}:${xMattersPassword}".bytes.encodeBase64().toString()}"
76
77            body = """
78
79            {
80
81              "properties": {
82
83                "Subject" : "${content.subject}",
84
85                "Body": "${content.body}"
86
87              },
88
89              "recipients": [
90
91                "kkovacs|Work Email"
92
93              ]
94
95            }
96
97            """
98
99            response.success = { resp, JSON ->
100
101                UserMessageUtil.success("xMatters sent. Weeeeee...")
102
103                return JSON
104
105            }
106
107            response.failure = { resp ->
108
109                UserMessageUtil.error("${resp.status}")
110
111                return "Request failed with status ${resp.status}"
112
113            }
114
115        }
116
117        Response.ok(payload).build()
118
119 
120
121}
Another critical thing to keep in mind is that xMatters authentication uses your email address, not your username (shown in your profile).
Join us again for part 5, where now that we have created a script to relay data to xMatters, modified our dialog script and configured xMatters to receive data, we need to convert our 'simple' script to send more complex data to XMatters.
To do this, we need to dig deeper into the world of code and find out what makes RestAPI's tick. While it's accurate to say that our solution is already "good enough" we are, however, aiming for excellence.