Skip to content

Releases: mattpolzin/OpenAPIKit

We're seeing double now

28 May 20:42
76edc1d
Compare
Choose a tag to compare
Pre-release

NOTE: The following will refer to both OpenAPIKit v3.0.0 (this library's upcoming version) and also OpenAPI v3.0/v3.1 (two versions of the specification this library implements). Notice the Kit suffix is the easiest way to tell if I am talking about this library. It can be confusing to keep these straight, because this library follows semantic versioning (wherein v3.0.0 is a major version bump associated with breaking changes) and the specification itself does not follow semantic versioning (the move from v3.0 to v3.1 of the specification is associated with breaking changes) -- and, what's worse, it is merely coincidental that the specification is on v3.1 and the library is on v3.0.

Here's a table that might help:

OpenAPIKit OpenAPI v3.0 OpenAPI v3.1
v1.x
v2.x
v3.x

Release Notes

Version 3.0.0 of OpenAPIKit primarily focuses on supporting the new v3.1 of the OpenAPI specification. It will, however, also make some small breaking changes to support for the OpenAPI v3.0 specification where those changes fix bugs, drastically clean things up, or support really beneficial new features.

The release notes for this first alpha of OpenAPIKit 3.0.0 will not go into details on the differences between OpenAPIKit's implementation of v3.0 and v3.1 of the OpenAPI specification. It will, however, describe the biggest difference going into this major version of the OpenAPIKit library:

Two Modules. The two public modules are OpenAPIKit30 (support for v3.0 of the specification) and OpenAPIKit (support for v3.1 of the specification).

v3.0.0 of OpenAPIKit splits support for OpenAPI v3.0 and OpenAPI v3.1 into separate modules because there are incompatibilities between the two versions that are most easily handled by either picking which version of the specification to use or explicitly referring to the version being used if both are needed.

Migrating from OpenAPIKit v2.x

V3.0 of the specification

To use the OpenAPI v3.0 specification much like it has been supported in past versions of this library, change your imports as follows:

// before:
import OpenAPKit

// after:
import OpenAPIKit30

If you are using Swift Package Manager, also change your dependency from the string "OpenAPIKit" or the product .product(name: "OpenAPIKit", package: "OpenAPIKit") to .product(name: "OpenAPIKit30", package: "OpenAPIKit").

Beyond that, the only change you currently need to look out for is that JSONReferences inside JSONSchema has gained a context so that it can support optionality. This means switches that match may need to change as follows:

// before:
case .reference(let ref) ...

// after:
case .reference(let ref, let context) ...

The new context contains the required boolean. This boolean defaults to true, but can be made false if a reference should be optional (which means it will be left out of its parent's required array on serialization to JSON/YAML).

You can also access this context with the new referenceContext accessor on JSONSchema and you can now reliably test whether a reference schema is required with the JSONSchema required accessor (boolean for whether or not any particular schema is required).

v3.1 of the specification

As noted above, the release notes for this alpha won't go into details on the differences between OpenAPIKit's support for v3.0 and v3.1 of the OpenAPI specification. Future alpha & beta releases will detail some of these differences; for now, this release serves as a way to test some of those differences for practicality.

To use the new version, you use the OpenAPIKit module (as opposed to the OpenAPIKit30 module that supports v3.0 of the specification).

Thanks

Thanks goes to @siemensikkema for implementing support of required/optional reference schemas and @mihaelamj for implementing numerous changes required by the OpenAPI v3.1 specification.

A Path To Validation

10 Jan 03:51
4986552
Compare
Choose a tag to compare
  • Add lookup() and unwrapAndLookup() validation helpers to make it easier to work with JSON references during validation (see Validation Documentation).
  • Add filteringPaths() method to OpenAPI.PathItem.Map and OpenAPI.Document to assist in quickly filtering the paths in the documentation down to a subset (producing a new path item map or a new document).

Reference cycles are reference cycles are reference...

28 Dec 23:15
4e7f675
Compare
Choose a tag to compare

JSON Schema (OpenAPI Schema Object) reference cycles were not previously handled by the OpenAPIKit dereferencing logic; this bug fix handles reference cycles by producing an error message instead of crashing after running out of stack space.

Serve Up Some Variables

26 Dec 07:20
7861ff0
Compare
Choose a tag to compare

The URLTemplate type used for the OpenAPI.Server urlTemplate property now exposes information on the variables contained within it in two ways and will fail if the URL used to create it has malformed variables.

You can access an array of variable names within the template via the variables property or an array of components (variables and constants) via the components property.

You can now use the optional serverVariablesAreDefined validation to assert that if a variable exists in the template for a Server Object's URL, then that same Server Object gives the variable a definition (See the OpenAPI Server Variable and the variables property on the OpenAPIKit Server type).

You can replace URL template variables with values to turn a template into a valid fully formed URL. The URLTemplate type exposes a replacing(_:) method for just this purpose. Once all variables have been replaced by constant values, you can use the url property of URLTemplate to attempt to retrieve a valid Foundation URL if possible.

Other Additions

  • ValidationErrorCollection now uses a newline separated list of its errors as its description (you can use String(describing: errors) to get this description).
  • The JSONSchema type has a series of properties to check if a given schema is of a particular type. isBoolean, isNumber, isInteger, etc.
  • A new all(_:) has been added to the validation helpers. This function accepts any number of Validations and produces a function of the type expected by a check clause of another Validation. See the Validation Documentation for more.

Everyone gets a default

20 Dec 06:59
96e85ad
Compare
Choose a tag to compare

Schema Objects were missing the JSON Schema default property that allows you to define a default for values. This release adds defaultValue to the CoreContext and the JSONSchema type.

The defaultValue property is an AnyCodable so that you are not tied to one Swift way of representing each JSON type when defining default values.

Call Me Back

03 Nov 21:38
02b8b78
Compare
Choose a tag to compare

Adds support for OpenAPI Callback Object.

The Callback Object can be used in Operation Objects and defined inline or in the Components Object.

Adds (optional) pathParametersAreDefined validation.

Use this new validation by creating a validator and tacking the new validation onto it. See the Validation Documentation for more information on OpenAPIKit's validation system.

let document: OpenAPI.Document = ...

// this will check all the default validations _and_ the one being added on with the following line.
let validator = Validator()
  .validating(.pathParametersAreDefined)

try document.validate(using: validator)

Errors, Not Exceptions

24 Oct 21:01
f534aa9
Compare
Choose a tag to compare

Fix a crash that occurred when a Response Object failed to decode from within the Components Object. There was an assumption in the code that Response Objects lived within Operation Objects. That assumption has been removed and now OpenAPIKit successfully produces errors for Response Object decoding failures across the board.

At the same time, a couple of error messages have been made a bit more specific and therefore easier to understand.

Tell Me More

01 Oct 00:48
55e8672
Compare
Choose a tag to compare

A minor patch to the message output when validation of .schemaComponentsAreDefined fails.

2.0.0 - Much Love for Schemas

28 Sep 01:36
dea99e8
Compare
Choose a tag to compare

Features & Additions

New schema validation

New optional schema validation (not included in validation by default): .schemaComponentsAreDefined. Validates that all schemas are at least minimally defined (i.e. none of the JSON Schema definitions are just an empty object).

LocallyDereferenceable

Most OpenAPI types are LocallyDereferenceable now, which means they offer a dereferenced(in:) method that takes an OpenAPI.Components and results in a new type that is guaranteed to not contain any JSONReferences. This is the same behavior exposed on OpenAPI.Document via its locallyDereferenced() method.

Schema simplification

DereferencedJSONSchema has gained the simplified() method which will attempt to simplify compound schemas. The same is exposed via JSONSchema's simplified(given:) method which takes OpenAPI.Components.

Simplification is in its early stages right now and does not support all possible schemas but it works well on all(of:) schemas already.

Bug Fixes

  • OpenAPI.Server's url used to not be able to handle server URLs with variables in them. This has been fixed in the new urlTemplate property (see breaking changes and the migration guide for more information).
  • JSONSchema can express a number of valid schemas it previously could not -- all of its cases support the core set of properties (see breaking changes and the migratio guide for more information).
  • OpenAPI.Content's schema property has become optional, as indicated by the OpenAPI Specification (under Media Type Object).

Breaking Changes

The following list enumerates the breaking changes but the migration guide serves as a better resource for identifying the changes needed in your codebase (if any).

  • OpenAPI.Server loses its url property in favor of a more correct urlTemplate property that can in turn be asked for a Foundation URL (but it will only produce one if the URL in question has no variables in it).
  • OpenAPI.Content's schema property has become optional to support valid OpenAPI documentation where the Media Type Object has no schema.
  • JSONSchemaFragment has been removed -- you can now use JSONSchema to represent all of the same schema fragments. Relatedly, JSONSchema's undefined case has been renamed fragment and it has gained a full CoreContext.
  • DereferencedJSONSchema's underlyingJSONSchema property has been renamed to jsonSchema.
  • The JSONSchema generalContext property and Context type have been renamed to coreContext and CoreContext. DereferencedJSONSchema has also renamed its Context to CoreContext.
  • JSONSchema's all(of:), any(of:), and one(of:) cases had their discriminator associated value replaced with a full CoreContext (which in turn has a discriminator).
  • JSONSchema's not case gained a CoreContext.
  • Some additional properties of the JSONSchema.CoreContext (and its other contexts) have become optional to support a broader range of valid schemas. In practice, this is mostly non-breaking because the accessors remain non-optional even where the underlying type has changed but the initializers now take optional input to support the new expressivity.
  • JSONSchema's dereferencedSchemaObject() and dereferencedSchemaObject(resolvingIn:) renamed to dereferenced() and dereferenced(in:).
  • OpenAPI.Components dereferencing and lookup methods have been renamed to better express the differences between "looking a resource up" and "dereferencing a resource." See the migration guide for more information.

Improvements in-depth

Dereferencing

In OpenAPIKit v1, the OpenAPI.Components type offered the methods dereference(_:) and forceDereference(_:) to perform lookup of components by their references. These were overloaded to allow looking up Either types representing either a reference to a component or the component itself.

In OpenAPIKit v1.4, true dereferencing was introduced. True dereferencing does not just turn a reference into the value it refers to, it removes references iteratively for all properties of the given value. That made the use of the word "dereference" in the OpenAPI.Components type's methods misleading -- these methods "looked up" values but did not "dereference" them.

OpenAPIKit v2 fixes this confusing naming by supporting component lookup via subscript (non-throwing) and lookup(_:) (throwing) methods and not offering any methods that truly dereference types. At the same time, OpenAPIKit v2 adds the dereferenced(in:) method to most OpenAPI types. This new method takes an OpenAPI.Components value and returns a fully dereferenced version of self. The dereferenced(in:) method offers the same iterative dereferencing behavior exposed by the OpenAPI.Document locallyDereferenced() method that was added in OpenAPIKit v1.4.

Simplifying all(of:) schemas

The JSONSchema all(of:) case is unique amongst the combination cases because all of the schema fragments under it can often effectively be merged into a new schema. This is what "simplifying" that schema means in OpenAPIKit. The result of simplifying an all(of:) schema is a DereferencedJSONSchema.

Simplification is performed by JSONSchema's simplified(given:) method (which takes an OpenAPI.Components value) or the DereferencedJSONSchema simplified() method.

let schemaData = """
{
    "allOf": [
        {
            "type": "object",
            "description": "A person",
            "required": [ "name" ],
            "properties": {
                "name": { "type": "string" }
            }
        },
        {
            "type": "object",
            "properties": {
                "favoriteColor": {
                    "type": "string",
                    "enum": [ "red", "green", "blue" ]
                }
            }
        }
    ]
}
""".data(using: .utf8)!
let personSchema = try JSONDecoder().decode(JSONSchema .self, from: schemaData)
    .simplified(given: .noComponents)

// results in simplified schema equivalent to:
let personSchemaInCode = JSONSchema.object(
    description: "A person",
    properties: [
        "name": .string,
        "favoriteColor": try JSONSchema.string(required: false, allowedValues: "red", "green", "blue")
    ]
)

Default schema optionality

In OpenAPIKit v1, schemas created in-code defaulted to required: true. While decoding, however, they would default to required: false and then if a JSON Schema .object had a "required" array containing a certain property, that property's required boolean would get flipped to true. This is somewhat intuitive at face value, but it has the unintuitive side effect of all root schema components (i.e. a JSON Schema that does not live within another .object) having required: false because there is no parent "required" array to cause OpenAPIKit to flip its required boolean.

Again, this has no effect on the accuracy of encoding/decoding because the required boolean of a JSONSchema value is only encoded as part of a parent schema's "required" array.

OpenAPIKit v2 swaps this decoding default to required: true and instead flips the boolean to false for all properties not found in a parent object's "required" array. This approach has the same effect upon encoding/decoding except for root schemas having required: true which both aligns better with the default in-code required boolean and also makes more intuitive sense.

let schemaData = """
{ 
    "type": "object",
    "required": [
        "test"
    ],
    "properties": {
        "test": {
          "type": "string"
        }
    }
}
""".data(using: .utf8)!

//
// OpenAPIKit v1
//
let schema1 = try JSONDecoder().decode(JSONSchema.self, from: schemaData)

// results in:
let schema1InCode = JSONSchema.object(
    required: false, // <-- note that this is unintuitive even though it has no effect on the correctness of the schema when encoded.
    properties: [
        "test": .string(required: true) // <-- note that this `required: true` could be omitted, it is the default for in-code construction.
    ]
)

//
// OpenAPIKit v2
//
let schema2 = try JSONDecoder().decode(JSONSchema.self, from: schemaData)

// results in:
let schema2InCode = JSONSchema.object(
    required: true, // <-- note that this `required: true` could be omitted, it is the default for in-code construction.
    properties: [
        "test": .string(required: true) // <-- note that this `required: true` could be omitted, it is the default for in-code construction.
    ]
)

Version 2.0.0 Release Candidate 1

22 Sep 03:15
7170489
Compare
Choose a tag to compare
Pre-release

There are no substantial changes since the last beta and this release candidate. Test coverage has improved, a few gaps in the API have been closed, and the migration guide has been completed.

The v2 release notes (on the upcoming release) will summarize changes and the migration guide will be a resource to help quickly identify what breaking changes require attention.