Skip to content

Commit

Permalink
New blog post about vendor extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
EricWittmann committed May 10, 2024
1 parent 0a00674 commit d391678
Show file tree
Hide file tree
Showing 3 changed files with 221 additions and 0 deletions.
221 changes: 221 additions & 0 deletions _posts/2024-05-10-studio-vendor-extensions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
---
layout: post
title: "Apicurio Studio - Now with OpenAPI Vendor Extensions"
date: 2024-05-10 16:00:00
author: eric
categories: studio openapi
---

The Apicurio Studio OpenAPI editor has been around for a long time, and over
time support for vendor extensions has evolved somewhat. It started with:

`"You can use the Source tab, good luck!"`

Eventually we added a Vendor Extension section in various places, which made
things a bit better. However, the UI was rather basic - just a list of the
extensions with values displayed as raw JSON. Editing the values was the same -
simply a source editor for the raw JSON value of the extension.

We've made another incremental improvement in this area, which is to use form
generation (via [Formly](https://formly.dev/)) to generate a UI form used when editing the value of a
vendor extension!

---

# What are OpenAPI Vendor Extensions?
When using OpenAPI to describe a REST API, the specification defines a set of semantics
for this purpose. Endpoints, data types, operations, inputs and outputs, etc are
all concepts defined by the specification. These first order concepts are often all
you need to fully define the shape of your API.

However, there may be aspects of your API that you want to define which go beyond the
common semantics defined by the [OpenAPI specification](https://www.openapis.org/).
In these cases, OpenAPI defines the concept of a Vendor Extension, which are open
ended property names that must begin with `x-`.

So imagine you want to define some API Management limits on your operations and you
have a REST API platform that can inspect the OpenAPI to find the configured limits
for any given operation. You might set rate limits for an operation directly in the
OpenAPI like this:

```yaml
openapi: 3.1.0
info:
title: Example API
version: 1.0
paths:
/widgets:
summary: Get a list of widgets.
get:
responses:
'200':
$ref: '#/components/responses/WidgetListResponse'
'500':
$ref: '#/components/responses/ServerError'
operationId: listWidgets
summary: List widgets
x-apimanagement-rateLimit:
rate: 100
period: 'second'
per: 'user'
```
Everything in the above example is standard OpenAPI semantics except for the
`x-apimanagement-rateLimit` API Management property, which is a vendor extension.
This example might indicate that the platform should apply a rate limit of 100
requests per second per user. Neat!


# What inspired this feature?
Apicurio Studio will be included as a component of a new Red Hat product called
[Connectivity Link](https://developers.redhat.com/products/red-hat-connectivity-link/overview).
This new product will have a lot of functionality in the network connectivity
and API management spaces, and one of the things it supports is enabling and
configuring its various features by adding some vendor extensions to the OpenAPI
document.

In particular, another component of Connectivity Link is [Kuadrant](https://kuadrant.io/),
which is an open source project that brings policy based application connectivity features
to Kubernetes. That project defines some
[OpenAPI kuadrant extensions](https://docs.kuadrant.io/0.7.0/kuadrantctl/doc/openapi-kuadrant-extensions/)
that can be used to configure things like routes and rate limits.

We thought it would be nice if users could use a form to enter the Kuadrant
vendor extension values, rather than having to copy/paste the correct JSON from
the Kuadrant documentation into the editor.


# Wait, is this ONLY for Kuadrant?
Of course, we could have simply added UI support for Kuadrant vendor extensions. But
that would only add support for this one specific type of extension. What we really
wanted was a way to easily configure Studio to support a wide range of vendor extensions,
without having to write code and release new versions. So what we've done is implement
a dynamic form generation approach that allows Studio to be configured with a list of
vendor extensions at deploy time. More details on that later.


# OK but show me what it looks like!
Right, let's show some stuff! Here's what things used to look like in the editor:

![Studio (before)](/images/posts/vendor-extensions/before.png)

Notice that you have to provide the full vendor extension property name as well as
the value in raw JSON format. I've seen worse, but it's not great.

Now here's what it looks like:

![Studio (after)](/images/posts/vendor-extensions/after.png)

Cool!


# How do I use this feature?
Unfortunately the feature isn't magic, which means there is some configuration
needed to leverage it. Let's dig into that in this final section.

## Creating a vendor-extensions configuration
What you need to do is create a `vendor-extensions.json` file with the correct
information (note: the file name does not matter). You'll need to create this
file and then eventually make it available to the Studio backend application
(see the next section).

The vendor extension configuration file is just a JSON file with an array of
supported vendor extensions. Each vendor extension item in the array should
have the following properties:

* `name` : the name of the vendor extension property, which should begin with `x-`
* `schema` : a JSON schema that describes the format of the vendor extension value
* `model` : a JSON object that is the default values for the vendor extension value
* `components` : an (optional) array of OpenAPI component names indicating valid placement of the vendor extension

Here is an example of this file (the one used in this blog post):

```json
[
{
"name": "x-address",
"schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"title": "Enter address",
"properties": {
"street": {
"title": "Street",
"type": "string"
},
"city": {
"title": "City",
"type": "string"
},
"state": {
"title": "State",
"type": "string",
"pattern": "^[A-Z]{2}$"
},
"zip": {
"title": "Zip",
"type": "string",
"pattern": "^[0-9]{5}$"
}
},
"required": [
"street",
"city",
"state",
"zip"
]
},
"model": {
"street": "",
"city": "",
"state": "",
"zip": ""
},
"components": [
"document"
]
}
]
```

The above example has only a single vendor extension defined, but you can specify
as many as you like.

Note that the `components` property allows you to specify where in the OpenAPI document
the vendor extension is allowed to be. In the above example it will only appear when
adding a vendor extension to the root of the document. Other allowed values for this
are:

* schema
* response
* parameter
* header
* requestBody
* securityScheme
* pathItem
* operation

You get the idea!

When defining your JSON Schema for a vendor extension, we recommend making sure you
have a `title` defined for each property. This will allow the form generator to create
appropriate labels in the UI. See the Formly documentation for more information.


## Telling Studio where the configuration is
Now that you have a vendor-extensions configuration file created, you need to tell
Studio where it lives. You can do this by setting the `APICURIO_UI_OPENAPI_VENDOR-EXTENSIONS_URL=`
environment variable when starting up Studio. For example:

docker run -it -p 8080:8080 --env APICURIO_UI_OPENAPI_VENDOR-EXTENSIONS_URL="https://raw.githubusercontent.com/Apicurio/apicurio-studio/0f81ea54bcf780daff59f5ea78c7990c6a4221f9/app/src/main/resources/extensions/address.json" apicurio/apicurio-studio:latest-snapshot

Any valid URL that can be resolved by the application should work. Just be careful
of using local file paths when running a docker image - if you want that to work then
you'll obviously need to mount the file into the container in some way.

# Conclusion
Thanks for reading. This feature is still in Beta and can do with some feedback and
iteration. If you feel so inspired, please give it a try and let us know how it can
be improved (oh let me count the ways).

Cheers!
Binary file added images/posts/vendor-extensions/after.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/posts/vendor-extensions/before.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit d391678

Please sign in to comment.