Last updatedOct 23, 2019

Creating your own branded customer portal

This guide will show you how to create your own branded customer portal. We'll take you on a guided tour of an example Node.js Express application, jira-servicedesk-branded-customer-portal, that implements this functionality and explain the key concepts. You won't be building an application yourself in this guide, but you can browse, download, and even run the source code for the example application:

Show me the code!

The code for the final output of this tutorial is here: https://bitbucket.org/atlassianlabs/jira-servicedesk-branded-customer-portal.

Client-side

These are the client side parts of the jira-servicedesk-branded-customer-portal application.

The contact form

For our simple contact form, we will be using the default template engine that ships with Express: Jade. The form will also make use of Bootstrap and jQuery. The code for the form is shown below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
doctype html
html(lang='en')
  head
    meta(charset='UTF-8')
    title Contact Us - MyCompany
    link(rel='stylesheet', href='//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css', media='all')
    script(src='//ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js')
    script(src='//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js')
  body.container
    .jumbotron
      h1 Welcome to MyCompany!
    .content.col-xs-6
      h4
        | Please use the form below to contact us.
      br
      form
        each field in fields
          .form-group
            label(for=field.fieldId)
              strong #{field.name}
            input(id=field.fieldId).form-control(type='text', name=field.fieldId)
        .form-group
          label(for='email')
            strong What is your email address?
          input#email.form-control(type='text', name='email')
        button#contact.btn.btn-primary
          | Send
    script(src='/javascripts/contact.js')

A few things to note in the above code:

  • The form is dynamically generated, based on fields that are configured against a certain Request Type in our Jira Service Desk project. We'll look at this in more detail later.
  • The form includes a page, contact.js. This page is responsible for making the call to our server to create a customer request.

The code for the contact.js page is shown below. The script builds a formFields object that contains all the form input fields, then makes a POST request to our server.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$(function () {
    $("#contact").click(function () {
        var formFields = {};
        $("form input").each(function() {
            formFields[this.name] = $(this).val();
        });

        $("form :input").prop("disabled", true);
        $("button").text("Please wait...");

        $.ajax("/contact", {
            type: "POST",
            contentType: "application/json",
            data: JSON.stringify(formFields)
        }).done(function (issueKey) {
            $(".content").html("<p>Thank you. We will get back to you ASAP. Your issue key is " + issueKey + "</p>")
        }).fail(function (jqXhr) {
            $(".content").html("Failed creating request: " + jqXhr.responseText + " (" + jqXhr.status + " - " + jqXhr.statusText + ")");
        });

        return false;
    });
});

Authentication

Each REST API call to Jira Service Desk needs to be authenticated. We'll be using basic authentication with the credentials specified in a config file. You probably wouldn't do this in a production instance, but this will make things simpler in this guide.

If you want to learn more about authentication, read the security overview.

Configuration

The configuration needed for the application to run is stored in a config file: config.json. The repository contains a sample config file, config.json.sample:

1
2
3
4
5
6
7
8
9
10
11
{
  "instance": {
    "url": "http://jira.example.com",
    "username": "your-username",
    "password": "your-password"
  },
  "serviceDesk": {
    "id": "x",
    "requestTypeId": "y"
  }
}

A few things to note about this file:

  • All HTTP requests will be authenticated using the username and password in this file. The user specified in this file will be used to raise requests on a customer's behalf, so the user will need to be an agent (or project administrator) of the service desk project chosen in the serviceDesk section.
  • A service desk ID (id) and request type ID (requestTypeID) must also be specified to create requests in a Jira Service Desk project.

To get the service desk ID and request type ID to populate the id and requestTypeID fields, you can do either of the following:

  • Use the Get service desks endpoint to get the id of the desired service desk and the Get request types endpoint to get the id of the desired request type.

  • In Jira Service Desk, do the following:

    • Navigate to desired customer portal and extract the service desk ID from the URL (e.g. the service desk id is 9 in this URL: http://your-jira.example.com/servicedesk/customer/portal/9),

    • In the service desk, select the desired request type and extract the request type ID from the URL (e.g. the request type ID is 36 in this URL: http://your-jira.example.com/servicedesk/customer/portal/9/create/36).

Note, if you are trying to run the example app, you'll need to edit config.json.sample, substitute the placeholder values in this file with your own values, then rename the file to config.json.

Server-side

These are the server side parts of the jira-servicedesk-branded-customer-portal application.

Serving the contact form

Since we need to know what fields are configured against the request type specified in our config file, we need to make a call to the Get request type fields endpoint to retrieve the fields before we serve the contact form. For simplicity, we only support string fields here.

Our example application uses the request library to call the Jira Service Desk REST API:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
const express = require("express");
const request = require("request");
const config = require("../config.json");

const router = express.Router();

const jsdRequest = request.defaults({
  baseUrl: config.instance.url + "/rest/servicedeskapi",
  auth: {
    user: config.instance.username,
    pass: config.instance.password
  },
  json: true
});

router.get("/", (req, res) => {
  jsdRequest.get(`/servicedesk/${config.serviceDesk.id}/requesttype/${config.serviceDesk.requestTypeId}/field`,
  (err, httpResponse, body) => {
    if (httpResponse.statusCode === 200) {
      const requestTypeFields = body.requestTypeFields;
      const fields = extractFields(requestTypeFields);
      res.render("contact", {fields: fields});
    } else {
      writeError(res, httpResponse, body);
    }
  });

  function extractFields(requestTypeFields) {
    const fields = [];
    requestTypeFields.forEach(field => {
      if (field.jiraSchema.type === "string") {
        fields.push(field);
      } else {
        // TODO: handle cases where field is not a string
      }
    });
    return fields;
  }
});

Creating a customer account and raising a request on their behalf

Before creating a request on behalf of the customer in our Jira Service Desk project, we have to create a new customer account. We use the Create customer endpoint to do that. We also handle the case where the username already exists.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
router.post("/contact", (req, res) => {
  const formFields = req.body;

  jsdRequest.post({
    url: "/customer",
    body: {
      email: formFields.email,
      fullName: formFields.email.replace(/@.*/, "")
    },
    headers: {
      "X-ExperimentalApi": true  // At the moment, the Create customer endpoint is experimental
    }
  }, (err, httpResponse, body) => {
    let username;
    if (httpResponse.statusCode === 201) {
      username = body.name;
    } else if (httpResponse.statusCode === 400 && body.errorMessage.indexOf("username already exists") > -1) {
      username = formFields.email;
    } else {
      writeError(res, httpResponse, body);
      return;
    }

    createRequest(res, username, formFields);
  });
  ....

Now that the customer account is created, we can raise a request on their behalf. The Create customer request endpoint is used to do this, as shown below. By default, Jira Service Desk will notify the customer that they have raised a request via an email.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  ....
  function createRequest(res, username, fields) {
    delete fields.email; // email is not a Jira field, delete it from fields

    jsdRequest.post({
      url: "/request",
      body: {
        serviceDeskId: config.serviceDesk.id,
        requestTypeId: config.serviceDesk.requestTypeId,
        raiseOnBehalfOf: username,
        requestFieldValues: fields
      }
    }, (err, httpResponse, body) => {
      if (httpResponse.statusCode < 200 || httpResponse.statusCode >= 300) {
        writeError(res, httpResponse, body);
        return;
      }
      res.statusCode = 201;
      res.end(body.issueKey);
    });
  }
});

Congratulations!

You now know how to build your own branded customer portal with Jira Service Desk!

Next steps

If you've finished this tutorial, check out more Jira Service Desk tutorials.