One of the best things about working with SwaggerHub is the exposure to APIs. They’re everywhere! We use them ourselves to write the application that documents the very APIs we’re using! I’m a front-end developer for SwaggerHub, and this means my exposure to APIs comes primarily as a consumer – I help sculpt the result of our APIs by being a first-party user.
Now as a front-end developer, my IDE of choice is Visual Studio Code. It’s fast, it’s neat, and as a JavaScript developer, it’s also very handy that it’s written in JavaScript. If I’m unsure about something, I can poke around at the open source innards and see what’s making things tick.
That’s all preamble to the core conceit of this blog: VS Code and SwaggerHub. You see, being a front-end developer on SwaggerHub who consumes SwaggerHub APIs, my natural inclination is to read, use, and work with those APIs in VS Code. But that’s always been a tedious affair in the past.
If I don’t want to lose context, I must download the YAML, open it in the editor and quickly find myself out of sync with any changes my fellow devs had made. It didn’t take long before I abandoned this tack altogether and stuck to leaving SwaggerHub open on one screen, VS Code in another.
Despite this being functional, I always thought there could be a better way. So, while brainstorming ideas for one of our yearly hackathons, I thought why not combine the two and write an extension for VS Code that would fully integrate with SwaggerHub.
We already have a public API for anyone with a SwaggerHub account, so why not bring it onboard and into the IDE? At the time, the VS Code API seemed a bit much to get my head around for the sake of a hackathon (as it turned out later, I was right), so the idea didn’t come to light during that event. But it kept percolating.
It's rare enough that we have down periods in SwaggerHub – there are always new features to build – but one sprint we were given time to work on personal projects for a few days, a sort of mini-hackathon. Most of us have little passion projects or improvements we want to make to SwaggerHub, but rarely get the time. So when the opportunity came along, I grabbed it with both hands and got to work.
Thanks to our public API, a lot of the boundaries to functionality are known and I can only build what’s available in the API. From these restrictions, it quickly became clear the sort of functionality the extension could provide, and the first thing I needed to tackle was how we would display the core of SwaggerHub: Organizations, APIs, and Domains.
TreeViews
The VS Code UI is rigid and uniform. This rigidity is one of the best things about it, as extensions function in predictable and reliable ways. UX is always tricky, but the established language of extension design in VS Code means that if you place something logically, the end user will likely find it as they get to lean on the pre-existing design language.
On the left we have the TreeView container. This is the bar with all the primary activation elements for extensions. Then we have the TreeView itself, where ancillary extension functionality lives. And finally, we have the main editor. The logical place to provide our list of Organizations, APIs, and Domains was in the TreeView, in a style that mimics the tree structure of the file browser.
The VS Code API is initially intimidating, but gradually opens itself up after some time. Depending on the feature you’re working with, the docs can vary wildly in clarity. The API itself is clear, but the description of how things work can be vague, and much of it is made of pre-existing examples – which, while trying to be concise, blend in a lot of functionality from across the API. You can end up implementing things you thought were important, but really weren’t.
For our purposes, it was clear we needed a hierarchical list – folders, documents, and versions, split out between an “APIs” section and a “Domains” section.
Initializing the TreeView is straightforward:
We create new TreeViews, and bind their providers to global objects so they can be referred to throughout the application. The Providers themselves are the class wrappers around the core functionality. We have a distinction between APIs and Domains, and as they are our only distinctions, we pass a Boolean to the constructor for simplicity’s sake.
An important thing to see here is the onDidChangeTreeData event. This is the event we can fire to refresh the TreeView when we want to pull more data down.
Next, we populate the TreeView with our data. The orgs whose APIs we’ll be using are defined in our settings as an array of strings, but we have a small piece of messaging wrapped around that which requires an async action.
Generating our API listing is straightforward. We grab ourselves the orgs, pull the definition metadata and spin our way through the dataset, constructing our tree.
The sequential construction of leaves for the tree branch is naturally fast and easy. And returning flattened results gives the TreeView the full dataset it needs to show us all the APIs and Domains we have available.
Contextual Menus
Working with VS Code means always finding new things to play with. Just about every aspect of the tool can have some sort of extension functionality either grafted on or utilized. When it came time to include some status-update functionality – changing visibility, published/unpublished, etc. – the initial approach was simply to utilize the command palette.
This is the traditional home for most extension functionality that doesn’t have a direct 1:1 UI, as it’s a command line for VS Code, basically. But I reasoned there had to be a better UX to this functionality than the command palette. A lot of the work in building an extension in VS Code is figuring out just where everything should live, and why it should live there. I see the command palette as a point of last resort, and very much an “it’ll do for now” type situation.
A thread I’d been tracking for a good while was around the addition of submenus to the right-click menu in the editor. A fly-out submenu would be a perfect, contextual place to place the elements, if only the API was available. The first thread on the open source VS Code repo to talk about this comes from 2016: https://github.com/microsoft/vscode/issues/9827. It was clearly useful and needed functionality, and it would get us out of this UX pickle, but obviously things hadn’t moved very far.
Cut to the September 2020, 1.50.0 release, and the submenu API is finally available – 4 years after the initial request. What perfect timing for the SwaggerHub extension!
Implementing the update was simple, as the new API was straightforward.
First you need to establish a submenu section with a unique ID:
Then create a relevant section for that ID within the menu’s contribution point:
Add some logic to indicate when the menu should show – we don’t want it showing on anything but YAML, for example – and group the items by sections. With items in their own sections, a separator is automatically added to make everything more readable.
Then simply bind the menu into the right contribution point, in this case the editor/context:
Something I like to do in these occasions is prevent the options from appearing in the command palette to reduce noise. This is as simple as adding your command to the commandPalette contribution point with a “when” value of “false”:
Restricting actions from appearing in the command palette has its pros and cons. With the functionality in the UI, there’s a clear user path to follow. But with the command in the command palette, there is a certain ‘power gamer’ vibe where you can do everything with key combinations.
I’m sure we’ll iterate on this in the future as people start playing with things. One of the most exciting parts about software development is when people try to pull your software in directions you didn’t expect. I’m sure this extension will have a lot of that going forward!
Preview
One half of development of an API is writing the API – the other is exploring how it’ll work as a consumer, and how it will be read and distributed. In SwaggerHub, our friends at Swagger Open Source have developed the wonderful Swagger UI project: https://github.com/swagger-api/swagger-ui/
In SwaggerHub we use this project and the associated Swagger Editor to power our API development platform. They’re incredibly powerful tools and the validation and display engines are top of their class, so we’re delighted to incorporate them. I wanted to bring a touch of that excellence to our extension, too, and take the development of APIs to a higher level by giving users the power of Swagger UI right in VS Code.
My first port of call was to use the WebView API in VS Code. This is effectively a way to show HTML content within a panel, giving you access to the outside world and custom UX. There’s a host of possibilities here, and it looked like a great way in for us to render Swagger UI. The idea of natively bringing in the Swagger UI npm library, and making it work inside VS Code in some magical way crossed my mind. But the path of least resistance seemed to be the best course, and besides, Swagger UI has a standalone version for situations just like this.
Initially daunted by the range of possibilities, the very excellent documentation around establishing a WebView container put me on the right path: https://code.visualstudio.com/api/extension-guides/webview
From the outside I realized I would need to create an HTML wrapper page we could statically load, then pull in the Swagger UI JS files externally. Following the Swagger UI docs on a third-party install, my first approach was to use unpkg: https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/installation.md
While this worked easily enough, the delay when opening the window to pulling the remote files became a problem. Timing issues would occur when, the first time a WebView instance was created and loaded, we could get to a point where the converted spec JSON was being piped to the WebView before the libraries had loaded. I experimented with timing changes and adjustments to wait for loading messages etc. But in the end, I decided on a statically loaded method which would increase initial extension size but would remove any performance or timing issues.
Firstly, we initialize the preview panel:
Next, we need to generate VS Code compatible URI’s for the JavaScript files we have bundled into our static folder:
VS Code extensions are delivered in .VSIX files – pretty much just fancy zip files – and only have a fraction of the codebase with them. We use webpack to bundle our core extension up into a single file, but static resources are housed in a static folder. As the location of these folders differs from machine to machine, we need to generate a VS Code specific URI for each asset. These URIs can then be used in any script or style calls further down the line.
Once we’ve generated our URIs, we need to read the statically stored index file and replace the embedded bundle strings (easily noted in {{ }}) with the new VS Code URIs. We then pipe the resulting modified index into the webview.html, and now Swagger UI is free to be loaded instantly from disk.
Getting a spec to the preview window is also very simple, as the VS Code API for posting messages to and from the WebView is naïve, but functional.
With the previewWindow handle and the content stored as global objects for access elsewhere, we can just use postMessage:
We must first convert the YAML to JSON using the excellent js-yaml library – something our entire industry relies on extensively – and then pipe that through.
From the other direction, our WebView panel needs to be set up to receive our message. This is as simple as wiring up an event listener:
The setState code is a follow-on from the other state management code in the index that allows us to retain the last spec seen, even as the preview window moves into the background.
Once we’ve received the message, we just send it on into the standalone Swagger UI:
We can then trigger this method whenever the text document changes, allowing for live updates of Swagger UI from the editor:
It’s important that we only update the document we want. So we make sure the text change messages are coming from the right place, grab the document text, and update. Simple!
There is more to the extension – much more! But these are a few of the little things that were interesting along the way. Developing this extension has been a great experience, both in terms of just building in VS Code, but also in terms of using our own API. I’m a big believer in ‘drinking your own champagne’.
It’s easy to lose sight of the end-user experience when developing things, as you work to a variety of inputs, be they tickets, requests, strategies, etc. The distance from ideation to deployment can be long, and without really living the life of your user, it’s very easy to overlook things.
We’ve found that while working with our API aggressively from a third-party perspective, we’ve taken extra actions to improve it and iron out some of the nuances along the way. The VS Code extension has a sister project. A CLI tool that allows a similar amount of interaction with the SwaggerHub API but through a command line interface – very useful for CI integrations! https://github.com/SmartBear/swaggerhub-cli
Together, we’ve been putting the APIs through their paces and we hope that this encourages more people to use our APIs and get SwaggerHub deeper into their systems. There’s so much potential here we’re just itching to explore, and we already have more features in active development ready to roll out when they’re done.
Creating this extension has been a labor of love, and we’re super excited about sharing it with the world and getting feedback. It’s the first step on a long road and I’m really looking forward to seeing how people use the extension, and how much it changes their existing workflows.
To learn more about the new SwaggerHub for VS Code extension or download a copy, go to the Visual Studio Marketplace.