[Discussion] OpenAPIKit

Codable implementation of OpenAPI Spec v3.x

Package Description

A library containing Swift types that encode to- and decode from OpenAPI Documents and their components.

Package name OpenAPIKit
Proposed Maturity Level Sandbox
License MIT
Dependencies Poly (MIT), OrderedDictionary (MIT), AnyCodable (MIT) [2020-03-20] Moved dependencies into OpenAPIKit.
Test-only Dependencies Yams (MIT), FineJSON (MIT)

Introduction

OpenAPIKit provides types that parse and serialize OpenAPI documentation using Swift's Codable protocols. It aims to stay structurally close enough to the Spec to be easy to understand given familiarity with OpenAPI, but it also takes advantage of Swift's type system to provide a "Swifty" interface to an OpenAPI AST and it additionally enforces the OpenAPI spec by making illegal things largely impossible to represent. The Project Status can be viewed as a glossary of OpenAPI terminology that references the corresponding OpenAPIKit types.

Motivation

OpenAPI is a broadly used specification for writing API documentation. OpenAPI documents can be used to generate interactive documentation , automate testing, generate code , or just provide a solid source of truth and a contract between a client and a server.

As linked above, a lot of great tooling already exists around the OpenAPI specification. The aforementioned code generator even supports Swift with improvements being actively discussed.

OpenAPIKit fits into the existing ecosystem as a relatively low level library, with the intention of supporting other libraries and tools on top of it. It currently captures nearly all of the specification in Swift Codable types. Thanks to Swift's type system, OpenAPIKit validates OpenAPI documentation simply by decoding it and it guarantees that OpenAPI documentation it encodes meets the spec as well.

In short, OpenAPIKit is a foundation for any Swift code that aims to read or write OpenAPI documentation. My hope is that this spec implementation saves time for others interested in writing tooling or frameworks with a higher level of abstraction.

There is a more compelling answer to "why?" though: Any Swift developer can harness the substantial power of an OpenAPI document without leaving the comfort and familiarity of the Swift language. This is the benefit I personally take away from it on a regular basis, having authored a handful of tools and libraries using OpenAPIKit already. With the parsing, validating, and serializing out of the way, someone authoring a library or implementing a dev tool or integrating OpenAPI into their SAAS app can jump straight to working with the AST.

Proposed Solution

Codable

OpenAPIKit takes advantage of Codable at every turn to create a single implementation that easily parses from- and serializes to JSON or YAML and takes advantage of independent advances by any Encoders or Decoders available.

AST with a Swifty Interface

"Swifty" is an admittedly vague descriptor. OpenAPIKit uses structs and enums to build out a nested structure of types that both encourages discoverability and ensures validity.

One of the primary goals was to produce a structure that was as easy to use declaratively as it was to traverse post-creation. OpenAPIKit almost reads like YAML (almost).

Writing OpenAPI documentation in Swift

// OpenAPI Info Object
// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#info-object
let info = OpenAPI.Document.Info(title: "Demo API", version: "1.0")

// OpenAPI Server Object
// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#server-object
let server = OpenAPI.Server(url: URL(string: "https://demo.server.com")!)

// OpenAPI Components Object
// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#components-object
let components = OpenAPI.Components(
    schemas: [
        "hello_string": .string(allowedValues: "hello")
    ]
)

// OpenAPI Response Object
// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#response-object
let successfulHelloResponse = OpenAPI.Response(
    description: "Hello",
    content: [
        .txt: .init(schemaReference: .component(named: "hello_string"))
    ]
)

// OpenAPI Document Object (and several other nested types)
// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#openapi-object
let _ = OpenAPI.Document(
    info: info,
    servers: [server],
    paths: [
        "/hello": .init(
            get: OpenAPI.PathItem.Operation(
                tags: ["Greetings"],
                summary: "Get a greeting",
                description: "An endpoint that says hello to you.",
                responses: [
                    200: .init(successfulHelloResponse)
                ]
            )
        )
    ],
    components: components
)

More examples can be found in the ease of use tests.

Traversing OpenAPI documentation in Swift

let data = ...
let doc = try decoder.decode(OpenAPI.Document.self, from: data)

// Loop over defined routes and print the path and operations defined for each path.
for (route, pathDefinition) in doc.paths {
    print(route.rawValue)
    // map GET, PUT, POST, PATCH, etc. to the operations defined for each
    let operations = OpenAPI.HttpVerb.allCases.compactMap { verb in
        pathDefinition.for(verb).map { (verb, $0) }
    }

    for (verb, operation) in operations {
        print("\(verb) -> \(operation.summary ?? "unknown description")")
    }
}

// Results similar to:
// ------------------
// test/api/endpoint/{param}
// get -> Get Test
// post -> Post Test

JSON References [Section updated 03-15-2020 for v0.24.0]

OpenAPI supports the use of JSON References in many locations. Anywhere this is allowed, OpenAPIKit exposes an Either property with one option being a reference and the other option being to directly specify the component in question. In many cases, Either has been extended with convenience static constructors allowing you to create either the reference or the component in an ergonomic and concise way.

As an example, OpenAPI.Response.Map (named the Responses Object in the Spec) maps response status codes to either responses or references to responses. The following code defines a status 200 response inline and then refers to a status 404 response that lives at #/components/responses/notFound.

let responses: OpenAPI.Response.Map = [
  200: .response(
    description: "Success!",
    content: [
      .txt: .init(schema: .string)
    ]
  ),
  404: .response(reference: try components.reference(named: "notFound", ofType: OpenAPI.Response.self))
]

Improvements to support for references are on the roadmap (#17). Currently, references to components living within the Components Object can be created or used to retrieve components.

let components: OpenAPI.Components = .init(
  schemas: [
    "example": .string
  ]
)
// creating references
let schemaReference: JSONReference<JSONSchema>
schemaReference = try components.reference(named: "example", ofType: JSONSchema.self)

// using references to grab components
let schema = components[schemaReference]

Validation through type-safety

The vast majority of the OpenAPI specification can be represented in the Swift type system. This means that we can create structs and enums that are incapable of representing ill-formed OpenAPI documentation. This means the structure of OpenAPIKit diverges slightly from that of the OpenAPI specification as documented, but the diversions are generally small and documented (currently part of the "glossary" found in the Project Status).

Human-readable Errors

Swift.DecodingError packs a lot of information, but it is not always easy to print that information in a very human-readable way. Additionally, in the context of a specification like OpenAPI we can sometimes offer error text that does a better job of pointing to a solution instead of just calling out a generic problem.

Although there's always room for improvement, I have done some work to set OpenAPIKit up for legible error messages.

When you catch an error coming out of OpenAPIKit, you can create an OpenAPI.Error from it. The reason OpenAPIKit does not just produce an OpenAPI.Error in the first place is because when you ask a Decoder to decode something it is going to wrap the result in a DecodingError anyway requiring someone to do the work of unwrapping it and that is what OpenAPI.Error is all about.

Example use:

let openAPIDoc: OpenAPI.Document
do {
  try openAPIDoc = JSONDecoder().decode(OpenAPI.Document.self, from: ...)
} catch {
  let prettyError = OpenAPI.Error(from: error)
  print(prettyError.localizedDescription)
  print(prettyError.codingPathString)
}

One important note is that the error is not actually localized at the moment. I see this as an area for improvement because these error messages are otherwise a great fit for passing on to the end user of whatever tool is failing to parse the OpenAPI document.

You can see some examples of the error text produced here.

Dependencies

OpenAPIKit currently has a number of dependencies I will mention here because all of them could be brought into the library to reduce the size of the dependency graph but it is worth discussing their utility in the first place and then deciding if the dependency graph is unwieldy as-is.

[2020-03-20] Moved dependencies into OpenAPIKit. I will leave the following sections in place nevertheless, as descriptions of- and justifications for the types.

Library

Poly OpenAPIKit.Either

Poly is an alternative to full type-erasure that I use on a regular basis. It can represent one of a number of types. It can erase the type with its .value accessor, but you can also write a switch statement over the possible types and it will decode any of its types with a fallback strategy and retain information on each decoding failure (not just tell you "none of the types were decoded").

All of that said, it is a very lightweight library and in fact OpenAPIKit only needs a subset of its functionality at that. OpenAPIKit uses Poly for its Poly2 type (referred to by its more intuitive typealias Either).

The OpenAPI Spec often defines things as one of two options and because encoding, decoding, and error handling logic are always going to be the same for these situations, an Either type made a lot of sense.

I would be willing to re-implement (mostly copy) support for Either from Poly into OpenAPIKit if removing Poly as a dependency is seen as more beneficial than reducing the footprint of the OpenAPIKit codebase.

OrderedDictionary OpenAPIKit.OrderedDictionary

The OrderedDictionary library just offers up one type and I bet you can guess what that is. Ordering of hashes is important to OpenAPIKit for two reasons:

  1. Users of OpenAPI may attribute meaning to the ordering of things like Path Items.
  2. When fed into a UI like Redoc or SwaggerUI, the output of OpenAPIKit should produce a stable view of documentation. It is disconcerting at best and confusing/frustrating at worst if the information you read yesterday is in a different part of the documentation today for no reason other than non-determinism.

I would be willing to being OrderedDictionary into OpenAPIKit to reduce the number of dependencies. Another alternative that I find less appealing is to refactor OpenAPIKit so it uses a new existential type for hashes and let the code integrating with OpenAPIKit decide whether to use the standard Foundation Dictionary or a type like OrderedDictionary.

AnyCodable OpenAPIKit.AnyCodable

OpenAPIKit uses AnyCodable anywhere that OpenAPI offers no structural restrictions on the JSON/YAML. This occurs most often where examples are allowed. Examples can be anything from a simple String value to a large nested structure containing Dictionarys and Arrays.

This is yet another dependency I would be willing to pull into OpenAPIKit -- I know it is not uncommon to roll-your-own support for this anyway.

Test-only

Yams and FineJSON are not used by any targets that are not test targets. FineJSON and Yams both support testing of ordered encoding/decoding. Yams is additionally used to run OpenAPIKit against third party OpenAPI Documents in the Compatibility Suite test target.

Example and Prototype Uses

Largely out of a combination of curiosity and utility at my "real job," I've developed a handful of libraries or showcases of OpenAPIKit integrations into other libraries and tools. I hope that this serves to show the breadth of applications of such a library even though this is by no means a comprehensive list of uses.

Next Steps

I am largely done adding to the surface area of the implementation for now. There are holes in what is supported, but I would like to motivate filling them with requests from the community because the remaining holes are increasingly remote corners of the specification.

I do plan to work on the following in the foreseeable future:

  1. Adding support for Specification Extensions to more types (#24).
  2. Improving support for $refs (#3, #17).
  3. Improving the flexibility of decoding (#23).
  4. Improving the ergonomics of using OpenAPIKit types, including adding accessors that retrieve "canonical" information on an API (suggestions appreciated on this one, see original pitch for more on this topic).

Maturity Justification

This package meets the following criteria according to the SSWG Incubation Process :

  • Follows semantic versioning
  • Uses SwiftPM
  • Code Style is up to date
  • Errors implemented
  • Unit testing for Mac OS and Linux in addition to a compatibility suite of tests.
  • Swift Code of Conduct

It notably does not meet the requirement of having a team of 2+ developers, although I believe that rule has been a bit flexible in the past. I do have a track record for maintaining open source libraries once they reach a level of maturity where others can begin taking advantage of them (e.g. JSONAPI, Poly).

Alternatives Considered

Existing Solutions

  • SwaggerParser (OpenAPI 2.0 support)
  • SwagGen (OpenAPI 3.0 parsing built-in but primarily a code gen. tool, no serializing of OpenAPI specs)
  • Kitura-OpenAPI (OpenAPI 2.0 serializing support)
  • Swiftgger (OpenAPI 3.0 serializing, no parsing)
4 Likes

Thanks for the detailed proposal! ++++1 to this overall.

I've only been able to review the code so far. I'm hoping to get a chance soon to dig into this further and try building some stuff with it. As is though, I think the idea and execution of this package is strong and should move forward.

One general tip I'd recommend for any package going through the proposal process (no specific cause for concern here) is to do a public API audit. Basically search everywhere the public keyword exists in code and consider whether that is essential API. Keeping the API surface as small as possible makes it easier to change and improve the package once it's released.

Below are some comments based on code review:


The JSONReference stuff is a bit hard for me to understand from glancing at the code. I'm not quite sure what these things do:

RefDict<Root: ReferenceRoot, Name: RefName, RefType: Equatable & Codable>: ReferenceDict, Equatable 
public protocol RefName {
    static var refName: String { get }
}

public protocol ReferenceRoot: RefName {}

public protocol AbstractReferenceDict: RefName {
    func contains(_ key: String) -> Bool
}

public protocol ReferenceDict: AbstractReferenceDict {
    associatedtype Value
}
Either<JSONReference<OpenAPI.Components, OpenAPI.Request>, OpenAPI.Request>

Some explanation about the reasoning behind these types and what they are doing in the proposal would be great. Any way to make the code more obvious would be awesome as well, though I understand OpenAPI is quite complex so that may not be possible.


@_exported should be avoided (Vapor 4 is bad at this and Vapor 5 will attempt to improve it).

@_exported import AnyCodable

This code prevents OpenAPIKit from moving to a new major version of AnyCodable without breaking. (This will be irrelevant if you end up removing the AnyCodable dependency of course).


I've noticed lots of abbreviations in type names, e.g., AbstractReferenceDict. Swift style recommends not using abbreviations.

Avoid abbreviations. Abbreviations, especially non-standard ones, are effectively terms-of-art, because understanding depends on correctly translating them into their non-abbreviated forms.


Some responses to specific parts of the proposal:

I'm +1 to pulling the min required elements of these into OpenAPIKit itself. Given that this is a very low level package that will be brought into potentially many libraries, keeping this as simple as possible will be a huge win.


Could any usages of OrderedDictionary be replaced with arrays or arrays of tuples? Performance isn't critical for this package so I don't think array lookups (O(n) vs. dictionary O(1)) would be a huge issue. We could always add caching if we needed to improve performance. I'd be interested for the opinions of others here.

Thanks @tanner0101!

This is a great idea. Absolutely will do.

These types support using references like (yaml):

$ref: #/components/schemas/hello_string

That reference would be parsable as a JSONReference<OpenAPI.Components, JSONSchema> and you would create the same reference in Swift with

.internal(\.schemas, named: "hello_string")

I'll add a section on JSONReference in the proposal. [EDIT] Added JSON References section.

I should spend some time with that code, too. The intention was to support arbitrarily deep JSON references. These references can be internal to the current file or external and I wanted to support both as far as parsing and serializing. I wanted to use KeyPaths for internal references obvious reasons, but because these paths need to serialize all the intermediate steps, I needed a more substantial backing than just a KeyPath.

Anyway, the short version of the story is that I might back off on that a bit before a 1.0 release and switch to only supporting internal JSON References to the Components Object which would simplify the types involved and entirely remove one of JSONReference's generic parameters.

I should note, these things parse and serialize just fine as is and usability is not terrible given an example to go off of, but it's probably worth redesigning that a bit more narrowly to save on the complexity of the types involved.

I'm convinced. I'll start work on removing all 3 dependencies (AnyCodable, OrderedDictionary, and Poly).

Easy enough to remedy and no complaints from me. Thanks for pointing out a breach of the style-guide!

OrderedDictionary currently offers 3 features:

  1. It has stable ordering.
  2. It offers subscript access by key.
  3. It encodes to- and decodes from a keyed (hash) structure for any LosslessStringConvertible or String RawRepresentable Key type, not just strictly String keys.

Compared to alternatives:
Dictionary: [1], [2], [3]

Array: [1], [2], [3]

It is possible to work around some of these limitations with other approaches, but you find yourself wanting a nice Swift type to encapsulate that encoding/decoding logic and attach that subscript access to. OrderedDictionary actually does the smallest amount it can do without losing those key benefits (Dictionary-like access, "extended" support for key/value encoding and decoding, Array-like order stability).

As for replacing some but not all uses of OrderedDictionary, if that is what you are suggesting, I don't see the benefit. Ordering is beneficial throughout (so Dictionary won't do) and I would be hard pressed to rewrite the encoding/decoding logic of OrderedDictionary in one or more utility functions to allow storage in an Array and encoding/decoding a hash structure without having totally removed the need for OrderedDictionary.

I see this requirement by the SSWG as fundamentally at odds with improving the Swift development community, even with a server-side focus. The dependencies mentioned for this library aren't vanity dependencies but instead fundamental building blocks that Swift doesn't currently offer. Pulling them into the the library does a disservice to the original library (as it doesn't benefit from any improvements), the community in general (I can't reuse the functionality from this library because it's not exposed as a dependency, especially once the code starts diverging from the original dependency), and the library itself (as it can no longer benefit from improvements the community makes to the dependency). To me, this limits the impact the SSWG can have and generally makes contributing libraries more difficult. If the SSWG wants to meet the goals of "reduce duplication of effort, increase compatibility and promote best practices", it needs to not only provide server components, but the components that undergird those. In the end, I don't think pulling separate copies of previously open source libraries into every SSWG library does meets those goals.

2 Likes

@Jon_Shier I agree in principle, but the problem that we're finding today is with the undisciplined nature of the Swift Package ecosystem - its causing several dependency graph resolution errors.

Such a low-level library could potentially be a blocker for a wide range of libraries being unable to update a basic composable library.

Yes, I can see that being an issue, and is why we avoid dependencies in Alamofire (sometimes to the detriment of the overall community). So perhaps I'm just saying the SSWG should consider incorporating libraries that serve a more basic purpose than "feature" libraries that can have guaranteed versioning and release support. Things like AnyCodable or OrderedDictionary are gaping holes in the overall Swift ecosystem which would be well served by reliable dependencies. So perhaps this is more of a missed opportunity than a disservice. I know Alamofire could use OrderedDictionary and AnyCodable, along with things like a URLEncodedFormEncoder (Vapor's is too tied to Vapor/NIO), TopLevelEncoder/Decoder, an Empty type, and better JSON parsers.

In the long run, I'm not sure this is a scalable approach to building an ecosystem. Apple can't replicate every library needed with their own version, especially when so much of their work can't evolve separately from their platforms. So empowering the community, not just by blessing every dependency, but by building out the tooling need to find high quality dependencies that can form the basis of a reliable ecosystem.

2 Likes

On the dependency discussion - there are also other implementations in the community. Worth evaluating them if the route of sticking with dependencies is taken over rolling them in.

I should clarify that this is not an SSWG requirement, it's my opinion. The only thing the incubation process process requires regarding dependencies is that their licenses are listed in the proposal.

I agree that being DRY and not reinventing the wheel everywhere is very important. But I think being overly DRY is a problem as well. NPM's ecosystem is the obvious example here with packages like is_integer that are literally one line.

In order to use these 3 methods node_modules needs 1826 files. And that’s just 4 of mentioned 976 installed packages.

From: https://hackernoon.com/whats-really-wrong-with-node-modules-and-why-this-is-your-fault-8ac9fa893823

NPM's package bloat has caused big technical and security problems.

That being said, I don't know where AnyCodable, OrderedDictionary, or Poly fit on this spectrum. Are they complex enough that they merit their own package and we should be re-using them? Does not re-using them create real usability problems for consumers of OpenAPIKit? Are we only using a very small subset that would make more sense for direct inclusion? Does adding this dependency create unnecessary bloat?

Those are great questions and worth discussing with evidence in this proposal. My initial argument was simply "I don't know" and to err on the side of being conservative here. That is not a requirement of the SSWG or even what the other SSWG member's opinions may be.

1 Like

On the topic of Dependencies:

I like the idea of the SSWG supporting, nay encouraging adoption of certain implementations of types offering functionality that is needed commonly and perhaps even more so in server-side development than elsewhere.

I also agree that the group should not simply endorse dependencies based on the first time they show up in another library that just happens to be up for proposal. There very well might be better implementations out there and those other implementations may have been missed precisely because it can be difficult to find or decide on third party options outside of the recommendations made by the WG.

Then again, the broader community might be ultimately hurt by a proliferation of bite sized libraries as Tanner points out are the feelings of some in the NodeJS community.

My suggestion would be this: Recommend libraries like OpenAPIKit internalize "building block" dependencies for now, but explicitly plan for WG support of shared common core dependencies to be driven by emerging demand as more libraries are accepted into incubation. Details notwithstanding (and not belonging in this proposal thread), this approach could, for example, allow for a single common core library with different products for each core component instead of wholly separate libraries for each component.

I will start a new thread on the topic of dependencies because I like where this conversation is going but I don't think this proposal is a visible enough place to have the discussion.

On the topic of OpenAPIKit Dependencies:
Aside from code re-use, I am not concerned about the impact of bringing the three necessary dependencies into the library. I think the other primary consideration is interop:

  1. AnyCodable is generally used strictly as a type erasing wrapper where there is little utility in carrying an "AnyCodable" around; rather, you put something in to encode it and you take something out to decode it. This reduces the likelihood of confusion or frustration over another library also using AnyCodable (or writing their own version of such a utility).
  2. Poly (and specifically the Either type needed by OpenAPIKit) is similarly used as a wrapper type. You might carry an Either value around but ultimately (at least in the context of OpenAPIKit interaction) you are more likely to pull something out of it than you are to be frustrated by it not being the same type as another Either implementation in use. I believe this to be a particularly valid estimation because OpenAPIKit is not a library offering up general utility types.
  3. OrderedDictionary aims to offer the same APIs as Dictionary where possible. It is a goal (whether or not fully realized at this stage) that it be easy to create an OrderedDictionary, easy to interact with it directly as a "dictionary type", and also easy to get an Array of key/value pairs back out, which could be used to construct a Dictionary if you did need to pass an OrderedDictionary on to something that did not care about ordering or know about OrderedDictionary.
2 Likes

OpenAPIKit version 0.24.0 addresses feedback from @tanner0101 in the following ways (not intended to address all of the feedback I have gotten so far):

  • Unnecessary abbreviations removed.
  • AnyCodable no longer @_exported (of course it will soon be taken into the package anyway).
  • JSONReference heavily reworked as described below.

The first two points addressed by version 0.24.0 are self-explanatory.

After Tanner pointed out that the JSONReference code was not easy to digest, I revisited it with an aim to simplify which had been a goal of mine for a while anyway. The result is rough parity of public API, although some key changes were made in the interest of better ergonomics.

Meanwhile, the underlying code is totally different. In the interest of specifically supporting references to components in the Components Object I've removed whole types that were aimed at generalizing JSON References. References to components outside the Components Object are still supported, and in fact the support for those external references has gotten better without losing anything as a side effect of revitalizing the whole structure.

Here are a couple of before-and-after examples:

// declaration - before
let schemaReference: JSONReference<OpenAPI.Components, JSONSchema>

// declaration - after
let schemaReference: JSONReference<JSONSchema>

// definition - before
schemaReference = .internal(\.schemas, named: "example")

// definition - after
schemaReference = .component(named: "example")
// OR
schemaReference = try components.reference(named: "example", ofType: JSONSchema.self)

// access - before
/* not available yet */
// access - after
let schema: JSONSchema
schema = components[schemaReference]

OpenAPIKit version 0.26.0 moves Either, OrderedDictionary, and AnyCodable into OpenAPIKit, thus removing all of the external dependencies (excepting those used strictly for test targets).

I will update the proposal accordingly.

2 Likes

het @mattpolzin would you please open a PR at sswg/proposals at master · swift-server/sswg · GitHub with the proposal markdown, the proposal number is #11 and the review manager is @tanner0101

1 Like

I've gathered the feedback received so far in the pitch and discussion threads in order to collect my thoughts a bit more. Thanks to everyone who has had input.

I'd ask the following of anyone with time for it: Please read the brief summary below and let me know if you have any feedback not captured so far.

Dictionary Ordering

From OpenAPIKit - #2 by lassejansen

Feedback

OpenAPIKit did not preserve dictionary ordering when decoding/encoding. This caused semantic problems where ordering of OpenAPI components was deliberate and it separately caused problems for diffing algorithms that might pick up on ordering differences even though no intentional changes were made.

Resolution

OpenAPIKit began using an OrderedDictionary type in version v0.13.0. This means that if the Encoder and Decoder being used support stable ordering (some do and some don't) then OpenAPIKit will retain that ordering.

Compatibility Testing

From OpenAPIKit - #4 by mackoj

Feedback

OpenAPIKit did not test itself against any publicly available OpenAPI documentation.

Resolution

OpenAPIKit gained a compatibility test suite in v0.19.0. The suite only contains two different APIs so far, but it has already proven useful in catching edge cases not previously dreamt up in any unit tests. More APIs will be added in the future.

Error Handling

From OpenAPIKit - #2 by lassejansen

Feedback

OpenAPIKit did not provide concise understandable parsing errors.

Resolution

This was largely addressed by v0.21.0 which introduced a series of error types and one wrapper type called OpenAPI.Error that together offer up vastly improved error descriptions.

Reflection (via Mirror)

From OpenAPIKit - #12 by tanner0101

Feedback

There are a likely a lot of opinions on how to go about supporting OpenAPI generation via reflection. This could be moved to its own package.

Resolution

As of OpenAPIKit v0.23.0, existing reflection support has moved into another library called OpenAPIReflection. The features and maturity of that library need not be tied to that of OpenAPIKit.

JSONReference

From [Discussion] OpenAPIKit - #2 by tanner0101

Feedback

JSONReference code is difficult to understand.

Resolution

JSONReference was reworked in OpenAPIKit v0.24.0 to simplify and document the code and to improve the public API at the same time.

Dependencies

From OpenAPIKit - #12 by tanner0101

Feedback

OpenAPIKit pulls in a lot of dependencies.

Resolution

Since version 0.26.0 OpenAPIKit has had no external dependencies other than those used exclusively in test targets.

Public API Audit

From [Discussion] OpenAPIKit - #2 by tanner0101

Feedback

OpenAPIKit could benefit from an audit of its public API to make sure things that could be private/internal are not unnecessarily exposed (and therefore maintained) as public API.

Resolution

Tracked as ticket #25. I intend to tackle this soon, and certainly prior to v1.0.0.

Code Standards

From [Discussion] OpenAPIKit - #2 by tanner0101

Feedback

There are unnecessary abbreviations in use in OpenAPIKit in breach of the coding standards.

Resolution

I audited the codebase for abbrevations both in public APIs and in internal code. I removed all unnecessary abbrevations I found.

2 Likes

The discussion and changes here were very positive and the SSWG agrees this should move forward to the final [Feedback] phase.

The [Feedback] post should be similar to the original [Discussion] post but updated with all of the latest changes / requested clarification. You can see an example here:

If all goes well in the [Feedback] phase, the last step will be voting on accepting the package to the index.

Thanks for all your hard work on this @mattpolzin!

@tomerd can you lock this thread?