Build a Jira plugin that can send SMS messages using Twilio

July 22, 2020
Written by
Phil Nash
Twilion
Rob Shaw
Twilion
Reviewed by
Diane Phan
Twilion

Build a Jira plugin that can send SMS messages with Twilio

As a developer, when you think of Atlassian's Jira you probably think of bug tracking or project management for software development, but many organisations use Jira for all sorts of project management and communication. We found this out recently when The Salvation Army in Australia reached out for help sending SMS messages from their Jira powered contact centre.

Opening up this communication channel led to great results for the Salvos. In this post we are going to see how to build your own Jira plugin that can send SMS messages within your Jira project.

What we're going to build

We're going to build a Jira plugin that adds a new view into your Jira project from which you can send SMS messages using the Twilio API. This will be similar to the first version of the plugin we built with the Salvos.

We need to be able to handle plugin installation, showing an HTML form that we'll use to capture the message, and sending the message. For this blog post we'll use Node.js and Twilio Functions, though you could use any back-end of your choosing.

What you'll need

To build this plugin you will need:

To get setup with your Atlassian Cloud environment, follow this guide. You can stop before the part titled "Build a basic app", but if you go through the full guide you will get a good overview of what we're going to do.

Once you've got all that, it's time to start building.

Working with the Twilio Serverless Toolkit

Let's create a new project using the Twilio Serverless Toolkit. If you already have Node.js installed, you can create a new project with the following command. Enter your Twilio Account SID and Auth Token (available in your Twilio console) when prompted.

npm init twilio-function twilio-jira-plugin --empty
cd twilio-jira-plugin

That command scaffolds a totally new project that you can run locally or deploy to Twilio Functions. The --empty flag means that the command won't create any template files.

You can run the project with npm start but there's nothing to run just yet.

Building an App Descriptor

The first thing we need in our application is an app descriptor, which is a JSON file that describes our plugin to Jira. It can be a static JSON file, but I find it best to return a dynamic result so that some settings can be set dynamically.

Create a new function in the functions directory called atlassian-connect.js. You can see the format and keys the app descriptor needs in the documentation. Ours will be relatively simple, we'll add a name, description, key, baseUrl, authentication and modules.

A key needs to be a unique string. In the code below we set the key using context.APP_KEY. The context object contains environment variables that we set for our application. To set the environment variable locally open .env and add APP_KEY= and a unique identifier for your application.

We set the baseUrl to context.DOMAIN_NAME. You don't need to set this as an environment variable as the Twilio Functions environment sets this for you.

We are not concerning ourselves with authentication for this example, so set that to "none".

Within the modules key, we define a page module which points at the HTML we want to render within the Jira interface and defines where in the UI we place a link to it.

Add the following code to functions/atlassian-connect.js:

exports.handler = function (context, event, callback) {
  callback(null, {
    name: "Twilio SMS Basic",
    description: "A way to send SMS messages from within your Jira application",
    key: context.APP_KEY,
    baseUrl: 'https://' + context.DOMAIN_NAME,
    authentication: {
      type: "none",
    },
    modules: {
      generalPages: [
        {
          url: "/form.html",
          key: "form",
          location: "system.top.navigation.bar",
          name: {
            value: "Twilio SMS",
          },
        },
      ],
    },
  });
};

In the function we call the callback function with the result of the function. Passing a JavaScript object to it will render it as JSON.

That's the app descriptor. We now need the HTML that we're going to render inside Jira and then a function to handle form submissions.

Building our page

We need to return some HTML for the page module we defined. For this app we will return a static HTML page, using Atlassian's reduced UI kit so that it fits in with the rest of the interface. We'll also include Atlassian's JavaScript SDK to allow Jira to control the page.

Create a file called form.html in the assets directory and enter:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Send SMS with Twilio</title>
    <link
      rel="stylesheet"
      href="https://unpkg.com/@atlaskit/css-reset@2.0.0/dist/bundle.css"
      media="all"
    />
    <link
      rel="stylesheet"
      href="https://unpkg.com/@atlaskit/reduced-ui-pack@12.0.7/dist/bundle.css"
      media="all"
    />
    <style>
      .container {
        padding: 16px;
      }
      .help {
        font-size: 0.857143em;
        font-style: inherit;
        line-height: 1.33333;
        font-weight: normal;
        color: rgb(107, 119, 140);
        margin-top: 4px;
        display: flex;
      }
    </style>
    <script src="https://connect-cdn.atl-paas.net/all.js"></script>
  </head>
  <body>
    <section class="container ac-content">
      <h1>Send SMS</h1>
      <form method="POST" action="/send-sms">
        <div class="ak-field-group">
          <label for="phone-number">Phone number</label>
          <input
            type="tel"
            class="ak-field-text"
            name="phone-number"
            id="phone-number"
            inputmode="tel"
            required
          />
          <p class="help">Please enter the number in e.164 format.</p>
        </div>
        <div class="ak-field-group">
          <label for="message">Message</label>
          <textarea
            type="text"
            name="message"
            id="message"
            class="ak-field-text"
            required
          ></textarea>
        </div>
        <div class="ak-field-group">
          <button
            type="submit"
            class="ak-button ak-button__appearance-primary"
            id="btn-submit"
          >
            Send message
          </button>
        </div>
      </form>
    </section>
  </body>
</html>

The HTML page is pretty much one big form that submits a phone number and message to the /send-sms endpoint in our application. To complete this app we will need to implement the /send-sms endpoint.

Sending the SMS message

Create a new file in the functions directory called send-sms.js. We need to get the submitted phone number and message from the form and use them along with your Twilio phone number to send an SMS message.

Add your Twilio number to your environment variables by opening .env and adding TWILIO_PHONE_NUMBER= and your Twilio number.

Open functions/send-sms.js and enter the following function.

exports.handler = async (context, event, callback) => {
  const to = event["phone-number"];
  const body = event.message;
  const client = context.getTwilioClient();
  try {
    await client.messages.create({
      to,
      body,
      from: context.TWILIO_PHONE_NUMBER,
    });
    const response = new Twilio.Response();
    response.setStatusCode(303);
    response.appendHeader("Location", "/form.html");
    callback(null, response);
  } catch (error) {
    callback(error);
  }
};

In this function we get the phone number and the message that are sent in from the HTML form from the event object.

We then create an authorised API client from the context object and then use the client, the message, the phone number and our Twilio number to send the SMS.

If the SMS is sent correctly, we return a response object that redirects back to the form and if there is an error sending the message, we return that error to the callback so we can debug it later.

The plugin is ready

With those two functions and one HTML page we are ready to test the plugin. Start your application with the command:

npm start -- --ngrok

Adding the argument --ngrok will start a tunnel using ngrok that we can use to access this application running on our local machine from the public internet. When the app starts, it will show a number of URLs, copy the one that ends in "atlassian-connect", we will need this to install the plugin in Jira.

Open your Jira apps management page (from the top navigation, Apps > Manage your apps). Click "upload app", enter your atlassian-connect URL and click "upload".

Enter the URL for the atlassian-connect path in the Upload app modal.

This will install the application. Once the process is complete, hitting the Apps navigation again will show a "Twilio SMS" link.Click that and you will be brought to your form where you can enter your own phone number and message.

Once the app is installed you can find it under User-Installed apps. You can reach the form by opening the Apps menu and clicking "Twilio SMS"

Click send and you will receive your text message.

The plugin makes a form that takes a phone number and message and sends them as an SMS message from within Jira.

Deploying your plugin

Once you are happy with the plugin you can deploy the application to the Twilio Functions infrastructure with the command:

npm run deploy

You will get a URL you can use to upload your app to Jira permanently.

Your first Jira and Twilio plugin

You've just built a Jira plugin that can send SMS messages using Twilio. You can check out the code on GitHub. There is also an example of a more involved plugin that builds on these ideas to add new capabilities to issues with SMS.

There is more that we can do with this application though. We should work towards securing the /send-sms endpoint so that not everyone who knows the URL can use it to send SMS messages. We could also tie the SMS into issues, rather than have it as a separate form.

Finally, we are only using outbound messages right now, we should consider whether we could integrate inbound messages too. We could even integrate Twilio Voice into Jira.

This is very similar to the first version of a Jira plugin that we built for The Salvation Army that helped improve their process, you can read about that here.

What other features would you add to Jira now you have seen how plugins work? Let me know in the comments or on Twitter at @philnash.