Releases: mattpolzin/OpenAPIKit
Heading the Right Direction
Improved error reporting around decoding invalid specification extensions and allowed some Header Object properties that were previously disallowed by accident when specification extension support was added to that type.
Unsupported Support
This fix makes types that are not yet supported by OpenAPIKit (see the Specification Coverage) ignored silently instead of letting them cause decoding to fail.
The idea is, it's better to still be able to parse documentation even if not all of it has OpenAPIKit representation yet. OpenAPIKit is really close to supporting all OpenAPI types but not quite there yet. At least now the existence of those types in a document won't render the document invalid in the eyes of OpenAPIKit.
Fragmented References
Fixes #98 wherein an allOf Schema Object with a Reference Object would not parse and could not be represented in OpenAPIKit.
The fix involved adding the .reference
case to the JSONSchemaFragment
type. Although the surface area here is small, it is possible that this breaks code -- specifically, code that switches over the JSONSchemaFragment
enum.
Required Stability
JSONSchema
objects' requiredProperties
/optionalProperties
are now sorted so that their ordering is stable. These properties are dynamically created from the properties
dictionary and therefore the order has never been defined before.
Even now, the order is not publicly guaranteed, but now it will at least be stable which can help with diffing.
U.R.Love
URLs now encode as strings and decode from strings no matter what encoder/decoder you use. This is the treatment already given to URLs by popular encoders and decoders like Foundation's JSON options and Yams's YAML options but now OpenAPIKit takes the liberty of passing URLs to the encoder (and requesting them from the decoder) as strings to make sure that they always get this intuitive treatment.
True Forms
This is a big release that closes two longstanding gaps: An inability to dereference an OpenAPI Document and the lack of any canonical representation of the components of the API described by an OpenAPI Document.
Dereferencing
The OpenAPI specification supports the use of JSON References in a number of places throughout the Document. These references allow authors to reuse one component in multiple places or even just make the route definition section of the document more concise by storing some components in the Components Object.
When traversing an OpenAPI document, these references are often more of an annoyance than anything else; Every time you reach a part of the document where a reference is allowed, you must check whether you are working with a reference or not or maybe even reach out to the Components Object and look a reference up.
This release introduces the OpenAPI.Document
locallyDereferenced()
method. This method traverses the whole document resolving all references to components found in the Components Object. The result is a document that has replaced many types with dereferenced variants -- anywhere you would have seen Either<JSONReference<Thing>, Thing>
you will now just see Thing
(or a DereferencedThing
, anyway). The dereferenced variants will always expose the properties of the original OpenAPI
type.
Before:
let document: OpenAPI.Document = ...
let anOperation: OpenAPI.Operation = document
.paths["/hello/world"]!
.get!
let parametersOrReferences = anOperation.parameters
// print the name of all parameters that happen to be inlined:
for parameter in parametersOrReferences.compactMap({ $0.parameterValue }) {
print(parameter.name)
}
// resolve parameters in order to print the name of them all
for parameter in parametersOrReferences.compactMap(document.components.dereference) {
print(parameter.name)
}
Now:
let document: OpenAPI.Document = ...
let anOperation = try document
.locallyDereferenced() // new
.paths["/hello/world"]!
.get!
let parameters = anOperation.parameters
// loop over all parameters and print their names
for parameter in parameters {
print(parameter.name)
}
Resolving (to canonical representations)
The OpenAPI Specification leaves numerous opportunities (including JSON References) for authors to write the same documentation in different ways. Sometimes when analyzing an OpenAPI Document or using it to produce something new (a la code generation) you really just need to know what the API looks like, not how the author of the documentation chose to structure the OpenAPI document.
This release introduces the resolved()
method on the DereferencedDocument
(the result of the locallyDereferenced()
method on an OpenAPI.Document
). A ResolvedDocument
collects information from all over the OpenAPI.Document
to form canonical definitions of the routes and endpoints found within. In a resolved document, you work with ResolvedRoute
(the canonical counterpart to the OpenAPI.PathItem
) and ResolvedEndpoint
(the canonical counterpart to the OpenAPI.Operation
).
To show the power of a resolved type, let's look at the hypothetical need to look at all parameters for a particular endpoint. To achieve this without resolved types, we need to collect parameters on the Path Item Object and combine them with those on the Operation Object. Even worse, to really get this right we would need to let the parameters of the Operation override those of the Path Item if the parameter names & locations were the same.
Before:
let document: OpenAPI.Document = ...
let aPathItem = try document
.locallyDereferenced()
.paths["/hello/world"]!
let anOperation = aPathItem.get!
// going to oversimplify here and not worry
// about collisions between the Path Item and
// Operation parameters.
let parameters = aPathItem.parameters + anOperation.parameters
After:
let document: OpenAPI.Document = ...
let anEndpoint = try document
.locallyDereferenced()
.resolved() // new
.routesByPath["/hello/world"]! // new
.get!
// no oversimplification here, this is going to get
// us the totally correct comprehensive list of
// parameters for the endpoint.
let parameters = anEndpoint.parameters
Schema context accessors and all servers in Document
Additions
- Adds
allServers
onOpenAPI.Document
to retrieve all servers that are referenced anywhere in the document (whether in the rootserver
array, theservers
on a Path Item, or theservers
on an Operation. - Adds accessors that retrieve contexts on JSONSchemas, providing an alternative to destructuring when retrieving an optional context is more convenient.
Bug Fixes
- Breaking: Fixes return type on
Components.forceDereference()
to be non-optional. It was made optional whenforceDereference()
was introduced in v1.2.0 by accident even though it is not possible for that function to produce an optional value. I decided this was an isolated enough breakage to something introduced very recently and therefore am not waiting for a major version to fix the bug.
OpenAPI Document Extensible Validation
Additions
- Add
isInternal
andisExternal
to JSONReference. - Add
forceDereference()
methods to OpenAPI.Components to give a throwing alternative to the existingdereference()
methods. - Add validation, as described below.
Validation
It has always been a goal of OpenAPIKit to lean into Swift's type system and make it impossible to represent invalid OpenAPI documents as much as possible. However, there are some things Swift's type system still cannot guarantee. For those things, now there is explicit validation.
Given an OpenAPI.Document, you can call validate()
to check the following things (language pulled from the OpenAPI Specification):
- The Responses Object MUST contain at least one response code, and it SHOULD be the response for a successful operation call.
- Each tag name in the list [on the root Document Object] MUST be unique.
- The list [of parameters on a Path Item] MUST NOT include duplicated parameters.
- The list [of parameters on an Operation] MUST NOT include duplicated parameters.
- [Operation Ids] MUST be unique among all operations described in the API.
You can use the validation system to exercise additional control over what a "valid" document looks in your particular use-case. For more information on adding your own validation checks, see the new Validation Documentation.
Expanded Vendor Extension Support
Expand vendor extension (i.e. "specification extension") support to:
-
OpenAPI.Request
-
OpenAPI.Response
-
OpenAPI.SecurityScheme
-
OpenAPI.Server.Variable
-
Add
routes
accessor onOpenAPI.Document
to retrieve an array of pairings ofPath
andPathItem
.
Bump Yams version.
Looking over the Yams v3 release, I am inclined to say that v3 of Yams is fully source-compatible with v2 of Yams. There is a noted breaking change of now requiring Swift 4.1+ but OpenAPIKit only supports Swift 5.1+ to begin with.
The upshot of this major version bump is that even though OpenAPIKit only uses Yams for test targets, now downstream packages will not experience dependency conflicts when using OpenAPIKit and trying to use the latest versions of Yams (which include some important bug fixes).