For general information on the v3 release, see the release notes on GitHub. The rest of this guide will be formatted as a series of changes and what options you have to migrate code from v2 to v3. You can also refer back to the release notes for each of the v3 pre-releases for the most thorough look at what changed.
If you are migrating from v2 to v3 of OpenAPIKit, skip the next section and go straigh to Picking a Module. The next section is aimed at those migrating between the two new modules in OpenAPIKit v3.
If you are migrating from the OpenAPIKit v3 OpenAPIKit30
module to the OpenAPIKit
module, you can do a bit of a visual manual diff of the two migration sections below in this document to see the differences but really the differences are almost entirely born of the differences between the OpenAPI 3.0.x specification and the OpenAI 3.1.x specification and they mostly fall into the category of stuff your compiler will let you know about: You'll get errors that x isn't optional or y is optional in places where optionality changed or you'll get an error about not handling all cases of an enum where additional cases were added or you'll get an error where the values associated with an enum case have changed, etc.
If you do migrate between the two new modules and run into unintuitive errors or behavior that you cannot easily explain, please reach out via a GitHub issue and helping you will lead to additional documentation here for the next person.
You'll either need to migrate your codebase to the new OpenAPIKit
module (can read and write OpenAPI 3.1.x documents) or the new OpenAPIKit30
module (can read and write OpenAPI 3.0.x documents and be converted to the OpenAPIKit
module.
If your use-case needs to write out OpenAPI 3.0.x documents, you don't yet have much of a choice. There is not currently a way to convert OpenAPI 3.1.x documents into OpenAPI 3.0.x documents within OpenAPIKit. That is a planned feature, though. In this case, you must switch your imports to OpenAPIKit30
and see the section below on Migrating to the OpenAPIKit30 module.
If your use-case either doesn't require writing OpenAPI documents out or your use-case only needs to support writing out OpenAPI 3.1.x documents, you should leave your imports for the OpenAPIKit
module and see the section below on Migrating to the OpenAPIKit module.
This section describes migrating to the module that supports OpenAPI 3.0.x documents. The recommended path for many uses of OpenAPIKit would be to migrate to the OpenAPIKit
module as described in the next section.
The first order of business is to replace all of your imports.
Before:
import OpenAPIKit
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")
.
JSONSchema
changed from an enum
to a struct
. For most code, use will not change, but if you switch over any JSONSchema
s you should change to switching over the JSONSchema
value
property instead.
The reference
case has gained a CoreContext
so switching on it will need to change as follows:
Before:
case .reference(let ref): ...
After:
case .reference(let ref, let context): ...
ContentType
changed from an enum
to a struct
. Equality checks for all of the previous enum's cases will still work against the new static constructors on the struct, but switch statements will no longer be possible.
Response.StatusCode
changed from an enum
to a struct
. For most code, use will not change, but if you switch over any values of this type your code will need to change to switch over the StatusCode
value
property instead.
Constructing an entry in the Document.paths
dictionary now requires specifying Either
a JSON reference to a Path Item Object or the Path Item Object itself.
This means that where you used to write something like:
paths: [
"/hello/world": OpenAPI.PathItem(description: "hi", ...)
]
You will now need to wrap it in an Either
constructor like:
paths: [
"/hello/world": .pathItem(OpenAPI.PathItem(description: "hi", ...))
]
There is also a convenience initializer for Either
in this context which means that you can also write:
paths: [
"/hello/world": .init(description: "hi", ...)
]
You may already have been using the .init
shorthand to construct OpenAPI.PathItem
s in which case your code does not need to change.
Accessing an entry in the Document.paths
dictionary now requires digging into an Either
that may be a JSON Reference or a Path Item Object -- this means that where you used to write something like:
let pathItem = document.paths["hello/world"]
You will now need to decide between the following:
// access the path item (ignoring the possibility that it could be a reference)
let pathItemObject = document.paths["hello/world"]?.pathItemValue
// access the reference directly (ignoring the possibility that it could be a path item object):
let pathItemReference = document.paths["hello/world"]?.reference
// look the path item up in the Components Object (OpenAPIKit module only because this requires OpenAPI 3.1.x):
let pathItem = document.paths["hello/world"].flatMap { document.components[$0] }
// switch on the Either and handle it differently depending on the result:
switch document.paths["hello/world"] {
case .a(let reference):
break
case .b(let pathItem):
break
case nil:
break
}
NOTE: The error you will get in places where you need to make the above adjustments will look like:
Cannot convert value of type 'OpenAPI.PathItem' to expected dictionary value type 'Either<JSONReference<OpenAPI.PathItem>, OpenAPI.PathItem>'
The OpenAPI.Example
type's value
property has become optional so you will now need to handle a nil
case or use optional chaining in places where you switch-on or otherwise access that property.
The DereferencedOperation
type's callbacks
property is now an OpenAPI.DereferencedCallbacksMap
instead of an OpenAPI.CallbacksMap
.
The DereferencedResponse
type's links
property is now an OrderedDictionary<String, OpenAPI.Link>
instead of an OpenAPI.Link.Map
.
More Dictionary
types have been changed to OrderedDictionary
so that ordering is retained in more situations. This change won't impact most code, but because some Dictionary
methods are not supported by OrderedDictionary
, there is a small chance of code breaking. See the following file changes if you need to know which properties switched from Dictionary
to OrderedDictionary
: https://github.com/mattpolzin/OpenAPIKit/pull/233/files
An additional location where OrderedDictionary
replaced Dictionary
is the mapping
property of Discriminator
.
This section describes migrating to the module that supports OpenAPI 3.1.x documents.
All occurrences of JSONReference
outside of JSONSchema
have been replaced by OpenAPI.Reference
, a type that supports OpenAPI 3.1.x's ability to override summary and description.
Anywhere your code used to create a reference with JSONReference.internal(.path(...))
you will now use OpenAPI.Reference.internal(path: ...)
(and similarly for external references).
JSONSchema
changed from an enum
to a struct
. For most code, use will not change, but if you switch over any JSONSchema
s you should change to switching over the JSONSchema
value
property instead.
The .reference
case has gained a CoreContext
so switching on it will need to change as follows:
Before:
case .reference(let ref): ...
After:
case .reference(let ref, let context): ...
JSONSchema
only exposes an examples
(plural) property. This is backwards compatible with the example
(singular) property when decoding, but code that references the example
(singular) property will need to be updated to use the examples
array instead.
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).
The .null
case of JSONSchema
now has a CoreContext
property associated with it. If you pattern match on it, your code may need to change (though you are still allowed to match against just .null
and ignore the associated value if desirable).
The JSONSchema.null
property that used to serve as a convenient way of creating a null-type JSONSchema
is now a function. You can call it as .null()
which means most code will just need to gain the ()
parens.
When a JSONSchema
only has one allowedValue
, it will now be encoded as const
rather than enum
. There's nothing to change about your code in response to this, but be aware of the difference in encoding behavior.
The JSONSchema
type's coreContext
accessor now gives a non-optional value because all cases have a CoreContext
. The compiler will let you know where you need to stop handling it as Optional.
There is no longer .extended
formats for .string
JSON Schemas. Instead, all existing .extended
formats are now just regular .string
formats (e.g. you can just replace .extended(.uuid)
with .uuid
).
There are no longer .byte
or .binary
formats for .string
JSON Schemas. Instead, use the contentEncoding
s of .base64
and .binary
, respectively.
The .uriReference
.extended
JSON Schema .string
format used to serialize to uriref
whereas the new .uriReference
JSON Schema .string
format serializes to uri-reference
, per the JSON Schema standard.
ContentType
changed from an enum
to a struct
. Equality checks for all of the previous enum's cases will still work against the new static constructors on the struct, but switch statements will no longer be possible.
The following ContentTypes have been added as builtins (you can use static functions on the ContentType
type to construct these content types):
.avi
.aac
.doc
.docx
.gif
This also means that if you have previously constructed these media types yourself (e.g. .other("image/gif")
), those custom types will not compare as equal to the new builtin types. You may find use in comparing the .rawValue
of such content types instead if you want two content types to be equal as long as their string serializations are the same.
Response.StatusCode
changed from an enum
to a struct
. For most code, use will not change, but if you switch over any values of this type your code will need to change to switch over the StatusCode
value
property instead.
Constructing an entry in the Document.paths
dictionary now requires specifying Either
a JSON reference to a Path Item Object or the Path Item Object itself.
This means that where you used to write something like:
paths: [
"/hello/world": OpenAPI.PathItem(description: "hi", ...)
]
You will now need to wrap it in an Either
constructor like:
paths: [
"/hello/world": .pathItem(OpenAPI.PathItem(description: "hi", ...))
]
There is also a convenience initializer for Either
in this context which means that you can also write:
paths: [
"/hello/world": .init(description: "hi", ...)
]
You may already have been using the .init
shorthand to construct OpenAPI.PathItem
s in which case your code does not need to change.
Accessing an entry in the Document.paths
dictionary now requires digging into an Either
that may be a JSON Reference or a Path Item Object -- this means that where you used to write something like:
let pathItem = document.paths["hello/world"]
You will now need to decide between the following:
// access the path item (ignoring the possibility that it could be a reference)
let pathItemObject = document.paths["hello/world"]?.pathItemValue
// access the reference directly (ignoring the possibility that it could be a path item object):
let pathItemReference = document.paths["hello/world"]?.reference
// look the path item up in the Components Object (OpenAPIKit module only because this requires OpenAPI 3.1.x):
let pathItem = document.paths["hello/world"].flatMap { document.components[$0] }
// switch on the Either and handle it differently depending on the result:
switch document.paths["hello/world"] {
case .a(let reference):
break
case .b(let pathItem):
break
case nil:
break
}
NOTE: The error you will get in places where you need to make the above adjustments will look like:
Cannot convert value of type 'OpenAPI.PathItem' to expected dictionary value type 'Either<JSONReference<OpenAPI.PathItem>, OpenAPI.PathItem>'
The OpenAPI.Example
type's value
property has become optional so you will now need to handle a nil
case or use optional chaining in places where you switch-on or otherwise access that property.
The DereferencedOperation
type's callbacks
property is now an OpenAPI.DereferencedCallbacksMap
instead of an OpenAPI.CallbacksMap
.
The DereferencedResponse
type's links
property is now an OrderedDictionary<String, OpenAPI.Link>
instead of an OpenAPI.Link.Map
.
More Dictionary
types have been changed to OrderedDictionary
so that ordering is retained in more situations. This change won't impact most code, but because some Dictionary
methods are not supported by OrderedDictionary
, there is a small chance of code breaking. See the following file changes if you need to know which properties switched from Dictionary
to OrderedDictionary
: https://github.com/mattpolzin/OpenAPIKit/pull/233/files
An additional location where OrderedDictionary
replaced Dictionary
is the mapping
property of Discriminator
.
The Document
validate()
method now throws errors related to warnings from parsing the OpenAPI Document by defualt. If you want to ignore or handle those warnings differently, use validate(strict: false)
.
The .operationsContainResponses
validation is now opt-in instead of applied by default when validating. If you still want to ensure documents have responses in all Operation objects, tack the .operationsContainResponses
validation onto your validator, e.g.: try document.validate(using: Validator().validating(.operationsContainResponses))
.