We just released our integration between Jira and Stack Overflow for Teams. This integration will bring your Stack Overflow questions and answers closer to your Jira workflow by allowing you to find content from your Stack Overflow Team directly within Jira as well as give you unfurled previews of Jira issues right within your Stack Overflow Team.
In this post, we take a look behind the scenes and explore how we approached building our Jira app. We’ll discuss some of the challenges, pitfalls, and lessons we learned from building this integration and look at the inner workings of our Jira app.
Let’s briefly look at the features of our app to understand its functionality before discussing how we build these things:
- Show related questions within Jira - When you look at a Jira issue, we’ll search your Team on Stack Overflow for questions that might be relevant to the issue you’re looking at and display these questions in the right sidebar of your issue view:
Related questions shown within a Jira issue
- Unfurl Jira links in Stack Overflow for Teams - When posting a link to a Jira issue, we’ll show an unfurled preview of that issue right within your question or answer:
An unfurled preview of a link to a Jira issue in Stack Overflow for Teams
Getting the foundations right
When we started investigating how to build a Jira app, we found different approaches we could take:
- Use Jira’s REST API
- Use the Atlassian Connect framework
Jira’s REST API is pretty extensive and gets you quite far. For our link unfurling feature, reading from the REST API would have taken care of everything we needed.
However, we knew early on that we wanted to extend Jira’s user interface to show related questions from your Stack Overflow Team within Jira issues. Extending Jira’s user interface is something only Atlassian Connect supports, and so we decided to build our app on top of Atlassian Connect.
Atlassian Connect is a framework to extend the functionality of Jira and other Atlassian products. It solves some of the tricky challenges like authentication, user interface integration, communication with Jira’s REST API, and event-based collaboration.
Using Atlassian Connect, you can host your Jira application anywhere as long as Jira is able to send HTTP calls to it. It doesn’t matter if you build your Jira app as a dedicated app or as part of a bigger application. You can host it in a datacenter, on serverless functions, or on a bunch of Raspberry Pis glued to your desk.
We built the Jira integration as part of our core Stack Overflow application. This is the application powering Stack Overflow’s public Q&A as well as our private Stack Overflow for Teams offering.
At the foundation of every Atlassian Connect-based Jira app is the app descriptor, a JSON document defining things like:
- The name and description of your app
- The app’s permissions
- Webhook listeners
- User Interface extensions
This app descriptor needs to be hosted by your application so that Jira Cloud can find it. This is what Stack Overflow’s app descriptor looks like:
{
"name": "Stack Overflow for Teams",
"description": "Integrate with your Stack Overflow Teams",
"key": "com.stackoverflow.teams",
"baseUrl": "https://stackoverflow.com/",
"vendor": {
"name": "Stack Overflow, Inc.",
"url": "https://stackoverflow.com"
},
"authentication": {
"type": "jwt"
},
"lifecycle": {
"installed": "integrations/jira/installed",
"uninstalled": "integrations/jira/uninstalled"
},
"modules": {
"postInstallPage": {
"url": "integrations/embedded/jira/post-install",
"key": "post-install-page",
"name": {
"value": "Stack Overflow for Teams - Get started"
}
},
"jiraIssueGlances": [
{
"icon": {
"width": 48,
"height": 48,
"url": "https://cdn.sstatic.net/Sites/stackoverflow/img/icon-48.png?v=b7e36f88ff92"
},
"content": {
"type": "label",
"label": {
"value": "Similar questions"
}
},
"target": {
"type": "web_panel",
"url": "integrations/embedded/jira/search-glance"
},
"name": {
"value": "Stack Overflow for Teams"
},
"key": "so-issue-glance"
}
]
},
"scopes": [
"READ"
]
}
Whenever a user installs or uninstalls our application, Atlassian Connect sends a request to the respective callback URLs we define in our app descriptor. The payload of this request contains some information we need to identify your Jira instance and to ensure that future incoming requests are trustworthy.
Extending Jira’s user interface is a matter of declaring `modules` within the app descriptor. Each module includes a URL and a description of where the user interface element should be located within Jira. This way, Jira can simply include the HTML views rendered by the URLs we defined in an iframe to allow us to flexibly extend Jira’s user interface.
App installation
The first thing we built for our Jira app was the app’s installation process. As this is fundamental for everything our app does, this is a natural starting point. From building previous integrations, we’ve learned that spending a lot of attention and care on the installation process will pay off early on. Getting this foundation right makes building more features on top far easier.
Installing the Stack Overflow for Teams app in Jira is a two step process. First you need to install the app in Jira, then you need to connect the app to your Stack Overflow Team. After you’ve installed the app from Atlassian’s marketplace, Jira doesn’t know which Team on Stack Overflow it should connect to, and your Team doesn’t know about it’s association with Jira. So we had to build a way to establish this connection.
Whenever someone installs the Jira app, Jira will send us a notification via the `installed` webhook we registered in the app descriptor. This request will include the `SecurityContext`,an identifier for your Jira instance along with some other information used for secure communication later on.We take the `SecurityContext` and cache it in Redis for a while. After that, our Jira app will show you a prompt to Get Started with the Stack Overflow for Teams app.
On this screen, we explain that you’ll need to connect your Jira Cloud instance to a Team on Stack Overflow. We take you over to a connect page on stackoverflow.com and let Jira pass a valid and signed JWT to our site.
The connect page lets you connect Jira to one of your Stack Overflow Teams
Once you are on stackoverflow.com, we read the JWT and try to match it to one of the `SecurityContext` objects we cached in Redis. If we find a matching one, we can be certain that there was an installation initiated in your Jira instance and you can continue connecting one of your Stack Overflow Teams to your Jira instance.
Clicking the Connect button will take the Jira `SecurityContext` we had cached in Redis before and store it in your Team’s SQL Server database. This way, the information we need in order to talk securely between Jira and your Stack Overflow Team will be persistently stored within your Team’s database.
The sequence of installing the Stack Overflow Jira app
Whenever we receive an incoming request from Jira in the future, we’ll grab the JWT from the request’s `Authorization` header and verify that it has been signed with the shared secret we stored in your Team’s `SecurityContext`. If this is the case, we can be certain that the incoming request is trustworthy, and we can continue rendering our HTML views in Jira via an iframe. When we’re sending a request to Jira’s REST API, we will fetch your Team’s `SecurityContext` and generate a valid and signed JWT that we attach to the outgoing request. Jira will do the same verification process on their end. With this mechanism in place both parties can make sure to only ever respond to requests coming from a trusted source.
Search your Team from within Jira
To show related questions from your Stack Overflow Team within a Jira issue, we had to build an endpoint that receives a search query, searches your team’s instance, and returns an HTML view with questions from your team that match this query. We would then let Jira display this HTML view in an iframe as part of the issue Glance.
Building an endpoint that returns an HTML view is our bread and butter when building a web application like Stack Overflow. We could pass the search query via a query parameter and then run a search against your Team’s ElasticSearch index to find questions that match the given query.
Telling Jira to render our HTML view in the issue sidebar was as easy as providing the URL of our new view in the app descriptor. This would make Jira take the given URL and render it in an iframe at the desired user interface location.
There was one obstacle building this feature. When Jira is resolving our HTML view, we cannot make it pass any data as part of the request. The naive approach of letting Jira pass a search query as a query parameter wouldn’t work, unfortunately.
To bypass this limitation, we came up with a two-step approach. When Jira embeds our view in an iframe, we would render a placeholder HTML view that embeds some JavaScript. When this HTML view is rendered in an iframe in Jira’s issue view, the JavaScript will fetch additional information about the issue we’re looking at using Jira’s REST API, build a search query, and send an AJAX request back to Stack Overflow with that search query. When the AJAX request returns successfully, it will take the response body — a list of matching questions rendered as HTML — and insert it into the placeholder HTML view.
Unfurling Jira links
Another main feature of the Jira integration is that your Teams instance will show you an unfurled preview of your Jira issues. After you’ve activated the Jira integration for your Team, you can post a raw issue URL on a single line within a question or answer and Stack Overflow for Teams will unfurl this link for you and everyone who’s viewing this question.
To make unfurling work, we detect links on a single line when you’re writing a question or answer. We match links on a single line against a set of matchers to see if a given link is something we can unfurl. At the moment, we don’t support unfurling arbitrary links; only certain sources, like Jira or GitHub.
When you’ve installed the Jira integration for your Stack Overflow Team, we’ll save the base URL of your Jira instance as part of the installation process. Using this information we can detect if a given link is to an issue on your Jira instance and try to unfurl it.
We parse the link to your Jira instance and try to find an `issue key` within the link URL. An issue key is a unique identifier for issues within a Jira instance. You might have seen those, they usually look like this: STACK-247.
If we’ve found an issue key, we have everything we need to make the unfurling magic happen. We take the issue key and send it over to Jira’s REST APIto fetch some details for this issue. Once more, we’ll generate a proper JWT and attach it to the request to handle authentication properly.
Once we receive a response from Jira that includes the issue’s details, we can then generate an HTML representation of the unfurled link and show it as part of a question or answer.
Takeaway
Using Atlassian Connect, we were able to build a Jira integration that goes beyond the limits of interacting with a third-party service via a REST API. UI integration via iframes doesn’t come without headache as you’ll easily run into issues around cross-origin communication.
Once we had solved these issues, Atlassian Connect’s building blocks of extending Jira’s UI, consuming their REST API and listening to events based on webhooks allowed us to put together an integration that seamlessly integrates Jira and Stack Overflow for Teams.