Let’s talk about Swagger and Wrappers

Hello!

In this article, I would like to talk about Swagger and Wrappers.

Swagger simplifies API development for users, teams, and enterprises with the Swagger open source and professional toolset. You can find more info here.

By wrappers, I mean libraries that manipulate data by Rest API in a more convenient way for developers.

For example, you can look at the following wrappers: jiraclient (a JavaScript Jira Rest API Wrapper) Python Jira Client.

Why do we need the wrappers at all?

I will be talking about wrappers for Jira, but the same idea can be applied to wrappers for any other application (Atlassian or not Atlassian).

Wrappers, let us quickly call Jira Rest API. For example, let’s have a look at the Jira client wrapper. We can find issues in Jira with the following code:

// With ES6
import JiraApi from 'jira-client';

// Initialize
var jira = new JiraApi({
  protocol: 'https',
  host: 'jira.somehost.com',
  username: 'username',
  password: 'password',
  apiVersion: '2',
  strictSSL: true
});

async function logIssueName() {
  try {
    const issue = await jira.findIssue(issueNumber);
    console.log(`Status: ${issue.fields.status.name}`);
  } catch (err) {
    console.error(err);
  }
}

First, we created a Jira object for our URL, username, and password, and then we called Jira.findIssue function instead of calling the issue Jira Rest API method directly. It lets us write less code and make our software less buggy, faster to develop, and more understandable.

The same with the Python library:

jira = Jira(
    url='http://localhost:8080',
    username='admin',
    password='admin')
JQL = 'project = DEMO AND status IN ("To Do", "In Progress") ORDER BY issuekey'
data = jira.jql(JQL)
print(data)

Again we initialized our Jira connection and then got issues by a JQL query with the Jira.JQL function. Straightforward and clean code.

I believe now you have an idea why to use the wrappers.

Then why Swagger?

And we had used wrappers before Swagger emerged. Swagger lets the developer of the Rest API define all Rest API in a file of a particular format, and then swagger clients can analyze this file and call Rest API functions based on the data provided by this file.

I will provide here a detailed example of how it works.

I developed a plugin called Jira-react-atlaskit in which I showed how to use React and AtlasKit in a Jira Server Plugin.

Now I will extend this plugin to get all projects from a Jira Cloud Server using the swagger-js library.

In an actual application, I would call Jira Cloud API from my Java code in the backend using the swagger-codegen library instead because it would make our application more secure.

But here, I will get all projects from my JavaScript code.

Change backend

As I said before, if we want to use a swagger client, we need REST API declaration in a file in a specific format. Atlassian did it for us, and we can get the REST API declaration for Jira Cloud from here.

You need to download this file and change the servers parameter:

"servers":[{"url":"https://your-domain.atlassian.com"}]

Change URL to the URL of your Jira Cloud.

Next, we need to make this file available for our Jira Server app; I put this file into backend/src/main/resources/swagger folder. The name of the file is swagger.v3.json.

And now I need to add a reference to this file into my web-resource descriptor in the atlassian-plugin.xml file:

<resource type="download" name="swagger/" location="/swagger"/>

And I will also pass the URL to this file as a parameter to my soy template. Later I can get this URL in my JS code.

backend/src/main/java/ru/matveev/alexey/atlas/jira/servlet/FormServlet.java

 map.put("pathtoswaggerjson", "/jira/download/resources/ru.matveev.alexey.atlas.jira.backend:jira-react-atlaskit-resources/swagger/swagger.v3.json");

Now I will read this parameter in my soy template.

        <input class="text" type="text" id="pathtoswaggerjson" name="pathtoswaggerjson" value="{$pathtoswaggerjson}">

Done! Now we are ready to change our frontend.

Change frontend

First, we need to add the dependency to the swagger-js library in frontend/package.json:

"swagger-client": "^3.11.0",

Then we need to add code to frontend/src/js/components/Form.js.

I imported the library:

import SwaggerClient from 'swagger-client';

And then, I added code to the constructor.

I get the URL to the swagger.v3.json file:

const pathtoswaggerjson = document.getElementById("pathtoswaggerjson").value

Now I can write code to get all projects:

new SwaggerClient(pathtoswaggerjson, {requestInterceptor: (req) => {
      req.headers.Authorization = 'Basic base64(user:token)';
      return req;
    }})
        .then(
          client => client.apis.Projects.getAllProjects()
        )
        .then(
          getAllProjectsResult => console.log(getAllProjectsResult.body),
        )

  }

That s it.

As I said in an actual application, this code must be executed in the backend, but I am running this code right from the browser. That is why I need to launch my browser with web security disabled.

open -n -a /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --args --user-data-dir="/tmp/chrome_dev_test" --disable-web-security

Now, if I open my Form, I will see the following message in the console:

I got all projects from my Jira Cloud instance, and we had to write just a couple of lines to make it work.

You can also see the whole list of available Rest API if you write a code like this:

new SwaggerClient(pathtoswaggerjson, {requestInterceptor: (req) => {
      req.headers.Authorization = 'Basic Basic base64(user:token)';
      return req;
    }})
        .then(
          client => console.log(client.apis)
        )

If you execute this code, you will see something like this:

It is a list of all available Rest API methods.

Conclusion

As you can see, we write about the same amount of code with Swagger and Wrappers. Then why use Swagger?

  1. Developers of products refresh their Rest API declaration fast, and developers of wrappers can not be as quickly as developers of the products. Or, someday, the developers of wrappers can refuse to support their wrapper. What for to waste time writing code if you can do the same without writing any wrapper code?
  2. If you use Swagger, you can use the same swagger client to work with any Rest API described in the Swagger notation. You do not need to look for wrappers for every product you are trying to access.

That is why the need for wrappers has become much less. Yes, we would still need wrappers if these wrappers provide a more efficient code than the code generated by swagger clients. But for the libraries I mentioned above, it is not the case for most wrappers.

You can find the final code here.