SEND DATA
June 12, 2018

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

KK
Krisztian Kovacs 9 minute read

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.

1Krisz

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:

// Code box

copy

Copied!

 

payload = {
            "subject" : "Urgent Message",
            "body" : ""
        }


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.

// Code box

copy

Copied!

 

function submit() {
        var payload = {
            "subject" : "Urgent Message",
            "body" : ""
        };
        payload.body = document.getElementById("sr-dialog-textarea").value;
        var xhttp = new XMLHttpRequest();
        xhttp.open("POST", "https://MYJIRAINSTANCE.COW/rest/scriptrunner/latest/custom/xMattersRelay", false);
        xhttp.setRequestHeader("Content-type", "application/json");
        xhttp.send(JSON.stringify(payload));
        AJS.dialog2("#sr-dialog").hide();
    }


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.

// Code box

copy

Copied!

 

var textarea = $('textarea').val();
var linebreak = textarea.split('\n');
var length = linebreak.length;
var data = [];
for ( var i = 0 ; i<length ; i++){
    data.push({ 'line': i , 'content': linebreak[i] });
    console.log(data);
}


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:

// Code box

copy

Copied!

 

function submit() {
    var payload = {
        "subject" : "Urgent Message",
        "body" : ""
    };
    var textarea = document.getElementById("sr-dialog-textarea").value;
    var linebreak = textarea.split('\\n');
    var length = linebreak.length;
    var data = [];
    for ( var i = 0 ; i<length ; i++){
        data = data + " " + linebreak[i];
    }
    payload.body = data;
 
    var xhttp = new XMLHttpRequest();
    xhttp.open("POST", "https://tesc-dev501.adaptavist.cloud/rest/scriptrunner/latest/custom/xMattersRelay", false);
    xhttp.setRequestHeader("Content-type", "application/json");
    xhttp.send(JSON.stringify(payload));
    alert(xhttp.responseText);
    AJS.dialog2("#sr-dialog").hide();
}


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.

Kris1 image2018 4 19 10 22 44

First create a communication plan that will house all you need to create your rest end point.

KRIS2image2018 4 19 10 24 38

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.

image2018 4 19 10 56 58

 

image2018 5 29 13 23 40 

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.

// Code box

copy

Copied!

 

import com.atlassian.jira.component.ComponentAccessor

import com.atlassian.jira.issue.Issue

import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate

import groovy.transform.BaseScript

import javax.ws.rs.core.MediaType

import javax.ws.rs.core.MultivaluedMap

import javax.ws.rs.core.Response

 

@BaseScript CustomEndpointDelegate delegate

 

import groovy.json.JsonSlurper

import groovyx.net.http.HTTPBuilder

import groovyx.net.http.ContentType

import static groovyx.net.http.Method.*

 

import com.atlassian.jira.issue.fields.rest.json.beans.PriorityJsonBean

import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate

import groovy.transform.BaseScript

import org.codehaus.jackson.map.ObjectMapper

 

import javax.ws.rs.core.MultivaluedMap

import javax.ws.rs.core.Response

 

//-------------------------------------------------------------------------

// This script can be applied as a rest-end-point that shows the dialog

// when a user clicks on the 'send xmatters' button

// Currently known button placement for web fragments:

// - jira.issue.tools

//-------------------------------------------------------------------------

// Please change the following so the script would suit your needs:

List<String> CustomFieldIDs = [

        "customfield_10105",

        "customfield_10106"

]

//-------------------------------------------------------------------------

 

static trimIssueKey(String key) {

    key = key.replaceAll(/^\[|]$/, '')

    return key

}

 

static getJiraBaseUrl() {

    def baseUrl = ComponentAccessor.getApplicationProperties().getString("jira.baseurl")

    return baseUrl

}

 

static getCustomFieldData(String key, String customFieldID) {

    Issue issue = ComponentAccessor.issueManager.getIssueByCurrentKey(key)

    return issue.getCustomFieldValue(ComponentAccessor.getCustomFieldManager()

            .getCustomFieldObject(customFieldID))

}

 

static getTextArea(String issueKey, List<String> CustomFieldIDs) {

    return """

This is the actual message that will be sent to xMatters with all the variables we gather from custom fields.

It can be edited here before sent.

  

Custom Field A: ${getCustomFieldData(issueKey, CustomFieldIDs[0])}

Another Custom Field: ${getCustomFieldData(issueKey, CustomFieldIDs[1])}

  

Thank you for your attention.

  

Cheers,

The A Team"""

}

 

static getDialogText(String issueKey, List<String> CustomFieldIDs) {

    return """

<script>

    function submit() {

        var payload = {

            "subject" : "Urgent Message",

            "body" : "moo"

        };

        var textarea = document.getElementById("sr-dialog-textarea").value;

        var linebreak = textarea.split('\\n');

        var length = linebreak.length;

        var data = [];

        for ( var i = 0 ; i<length ; i++) {

            data = data + " " + linebreak[i];

        }

        payload.body = data;

  

        var xhttp = new XMLHttpRequest();

        xhttp.open("POST", "${getJiraBaseUrl()}/rest/scriptrunner/latest/custom/xMattersRelay", false);

        xhttp.setRequestHeader("Content-type", "application/json");

        xhttp.send(JSON.stringify(payload));

        alert(xhttp.responseText);

        AJS.dialog2("#sr-dialog").hide();

    }

    var el = document.getElementById("submit");

    if (el.addEventListener)

        el.addEventListener("click", submit, false);

    else if (el.attachEvent)

        el.attachEvent('onclick', submit);

</script>

<section role="dialog" id="sr-dialog"

    class="aui-layer aui-dialog2 aui-dialog2-medium" aria-hidden="true" data-aui-remove-on-hide="true">

<header class="aui-dialog2-header">

    <h2 class="aui-dialog2-header-main">xMatters Message</h2>

    <a class="aui-dialog2-header-close">

        <span class="aui-icon aui-icon-small aui-iconfont-close-dialog">Close</span>

    </a>

</header>

<div class="aui-dialog2-content">

    <p>Header of the dialog, some text about warnings and whatever....</p>

        <textarea id="sr-dialog-textarea" rows="15" cols="75">

            ${getTextArea(issueKey, CustomFieldIDs)}

        </textarea>

</div>

<footer class="aui-dialog2-footer">

    <div class="aui-dialog2-footer-actions">

        <button class="aui-button" id="submit">Send xMatters</button>

        <button id="dialog-close-button" class="aui-button aui-button-link">Close</button>

    </div>

    <div class="aui-dialog2-footer-hint">This is a footer message</div>

</footer>

</section>

"""

}

 

showXMattersDialog() {

    MultivaluedMap queryParams ->

        String issueKey = queryParams.get("issueKey")

        issueKey = trimIssueKey(issueKey)

        String dialog = getDialogText(issueKey, CustomFieldIDs)

        Response.ok().type(MediaType.TEXT_HTML).entity(dialog).build()

}

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.

// Code box

copy

Copied!

 

import groovy.transform.Field

 

import com.onresolve.scriptrunner.runner.util.UserMessageUtil

import groovy.json.JsonSlurper

import groovyx.net.http.HTTPBuilder

import groovyx.net.http.ContentType

 

import static groovyx.net.http.Method.*

 

@BaseScript CustomEndpointDelegate delegate

 

import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate

import groovy.transform.BaseScript

 

import javax.ws.rs.core.MultivaluedMap

import javax.ws.rs.core.Response

//-------------------------------------------------------------------------

// Don't forget to change the variables

//-------------------------------------------------------------------------

@Field String xMattersURL =

        "https://adaptavist-dev.xmatters.com/api/integration/1/functions/3eb43db8-e0a4-40ff-b1c8-452e89dd4691/triggers"

@Field String xMattersUser = "myaddress@email.com"

@Field String xMattersPassword = "myfancypassword"

//-------------------------------------------------------------------------

 

String issueKey = null

 

xMattersRelay(httpMethod: "POST") {

    MultivaluedMap queryParams,

    def payload ->

        def jsonSlurper = new JsonSlurper()

        def content = jsonSlurper.parseText(payload)

        issueKey = content.key

        def http = new HTTPBuilder(xMattersURL)

        http.request(POST) {

            requestContentType = ContentType.JSON

            headers.'Authorization' =

                    "Basic ${"${xMattersUser}:${xMattersPassword}".bytes.encodeBase64().toString()}"

            body = """

            {

              "properties": {

                "Subject" : "${content.subject}",

                "Body": "${content.body}"

              },

              "recipients": [

                "kkovacs|Work Email"

              ]

            }

            """

            response.success = { resp, JSON ->

                UserMessageUtil.success("xMatters sent. Weeeeee...")

                return JSON

            }

            response.failure = { resp ->

                UserMessageUtil.error("${resp.status}")

                return "Request failed with status ${resp.status}"

            }

        }

        Response.ok(payload).build()

 

}

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.