Skip to content

Dynamite (3)

Pre-release
Pre-release
Compare
Choose a tag to compare
@mattpolzin mattpolzin released this 26 Aug 23:27
c598406

What's Changed

  • Store component name as x-component-name when locally dereferencing an object (#295 / #296)
  • Add vendor extension support to JSONSchema (#297)
  • Support schema reference description overrides (#299)
  • Fix decoding of explode property on Headers (#302)

Noteworthy Differences

Dereferencing

When locally dereferencing part or all of the schema with the OpenAPIKit dereferenced(in:)/dereferenced()/locallyDereferenced() functions, OpenAPIKit will store the name of the Component (the key within the Components Object) under a new x-component-name vendor extension on the dereferenced value.

Example

        let components = OpenAPI.Components(
            examples: [
                "test1": .init(value: .init("hello world"))
            ]
        )
        let content = try OpenAPI.Content(
            schema: .string,
            examples: [
                "ex1": .reference(.component(named: "test1"))
            ]
        ).dereferenced(in: components)
        XCTAssertEqual(
            content.examples, [
                "ex1": .init(
                    value: .init("hello world"),
                    vendorExtensions: ["x-component-name": "test1"]
                )
            ]
        )

Overriding description on schema $refs

OpenAPIKit module only. Does not apply to OpenAPIKit30 module.

See the PR that introduced this change for some comments on why the current implementation was chosen.

OpenAPIKit will now encode/decode any CoreContext JSONSchema property alongside $ref properties. This includes the description, default, examples, deprecated properties, and more.

If you use an OpenAPIKit method for locally dereferencing (e.g. the OpenAPI.Document type's locallyDerefenced() method) then any description found next to a $ref will automatically override any description found within the schema being dereferenced at that location.

If you want to have finer grained control over looking up references (likely you are looking JSONReference types up in the Components Object in your code), you may need to do a little work to get the overriding behavior. When case matching on a reference JSONSchema.Schema, you can now pull the CoreContext out as the second element of the case and access properties like description on it. Alternatively, you can call the coreContext method on JSONSchema to access the CoreContext without switching over the cases of its value. Carry any part or all of this CoreContext along with the JSONReference to the location where your code looks the reference up, and then override the properties you want to override.

This may sound a bit inconvenient relative to, say, OpenAPI.Reference (which handles overriding of description internally without any extra work on your part). As discussed in the notes on the PR linked to above, there are a few reasons for this design. Perhaps the most undeniable argument for this design is that unlike with OpenAPI reference overriding which only supports description and summary, JSON Schema leaves the door open for any properties to live alongside a $ref and it doesn't explicitly state that overriding should be the behavior when dereferencing and it specifically notes that if you try to perform dereferencing where you handle all possible properties alongside $refs then you will almost certainly end up misrepresenting the original author's intent in some cases. Therefore, it is left up to the project using OpenAPIKit to decide which properties to override and which to ignore.

Example

        let components = OpenAPI.Components(
            schemas: ["test": .string(description: "generic description")]
        )

        let data = """
        {
            "type": "object",
            "properties": {
                "prop1": {
                    "$ref": "#/components/schemas/test",
                    "description": "specialized description"
                }
            }
        }
        """.data(using: .utf8)!

        let schema = try JSONDecoder().decode(JSONSchema.self, from: data)
        let property = schema.objectContext?.properties["prop1"]?.value
        switch property {
            case .reference(let ref, let context):
                let result = try components.lookup(ref)
                    .overriddenNonNil(description: context.description)

                XCTAssertEqual(result, .string(description: "specialized description"))
            default:
                break
        }

Breaking Changes

OpenAPIKit module only. Does not apply to OpenAPIKit30 module.
In order to support new versions of the JSON Schema specification that allow $ref properties to live alongside other annotations like description, the JSONSchema type's reference case had its ReferenceContext replaced with a full CoreContext.

Because the ReferenceContext contained only a required property and the CoreContext also has a required property, some code bases will not need to change at all. However, if you did use the ReferenceContext by name in your code, you will need to address compiler errors because of this change.

Another way this change may break code is if you have used the JSONSchema referenceContext accessor. This accessor has been removed and you can now use the coreContext accessor on JSONSchema to get the CoreContext when it is relevant (which includes reference cases going forward).


Full Changelog: 3.0.0-beta.1...3.0.0-beta.2