Skip to content

Commit e285b2a

Browse files
authored
Merge pull request #432 from mattpolzin/feature/431/new-http-methods
Add new OAS 3.2.0 HTTP method support
2 parents 0710155 + 495a63d commit e285b2a

File tree

28 files changed

+828
-188
lines changed

28 files changed

+828
-188
lines changed

README.md

Lines changed: 33 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,21 @@
44

55
# OpenAPIKit <!-- omit in toc -->
66

7-
A library containing Swift types that encode to- and decode from [OpenAPI 3.0.x](https://spec.openapis.org/oas/v3.0.4.html) and [OpenAPI 3.1.x](https://spec.openapis.org/oas/v3.1.1.html) Documents and their components.
7+
A library containing Swift types that encode to- and decode from [OpenAPI 3.0.x](https://spec.openapis.org/oas/v3.0.4.html), [OpenAPI 3.1.x](https://spec.openapis.org/oas/v3.1.2.html), and [OpenAPI 3.2.x](https://spec.openapis.org/oas/v3.2.0.html) Documents and their components.
88

99
OpenAPIKit follows semantic versioning despite the fact that the OpenAPI specificaiton does not. The following chart shows which OpenAPI specification versions and key features are supported by which OpenAPIKit versions.
1010

11-
| OpenAPIKit | Swift | OpenAPI v3.0 | OpenAPI v3.1 | External Dereferencing & Sendable |
12-
|------------|-------|--------------|--------------|-----------------------------------|
13-
| v2.x | 5.1+ || | |
14-
| v3.x | 5.1+ ||| |
15-
| v4.x | 5.8+ ||| |
11+
| OpenAPIKit | Swift | OpenAPI v3.0, v3.1 | External Dereferencing & Sendable | OpenAPI v3.2 |
12+
|------------|-------|--------------------|-----------------------------------|--------------|
13+
| v3.x | 5.1+ | | | |
14+
| v4.x | 5.8+ | || |
15+
| v4.x | 5.8+ | | | |
1616

1717
- [Usage](#usage)
1818
- [Migration](#migration)
19-
- [1.x to 2.x](#1.x-to-2.x)
20-
- [2.x to 3.x](#2.x-to-3.x)
21-
- [3.x to 4.x](#3.x-to-4.x)
19+
- [Older Versions](#older-versions)
20+
- [3.x to 4.x](#3x-to-4x)
21+
- [4.x to 5.x](#4x-to-5x)
2222
- [Decoding OpenAPI Documents](#decoding-openapi-documents)
2323
- [Decoding Errors](#decoding-errors)
2424
- [Encoding OpenAPI Documents](#encoding-openapi-documents)
@@ -47,40 +47,25 @@ OpenAPIKit follows semantic versioning despite the fact that the OpenAPI specifi
4747
## Usage
4848

4949
### Migration
50-
#### 1.x to 2.x
51-
If you are migrating from OpenAPIKit 1.x to OpenAPIKit 2.x, check out the [v2 migration guide](./documentation/v2_migration_guide.md).
50+
#### Older Versions
51+
- [`1.x` to `2.x`](./documentation/migration_guides/v2_migration_guide.md)
52+
- [`2.x` to `3.x`](./documentation/migration_guides/v3_migration_guide.md)
5253

53-
#### 2.x to 3.x
54-
If you are migrating from OpenAPIKit 2.x to OpenAPIKit 3.x, check out the [v3 migration guide](./documentation/v3_migration_guide.md).
55-
56-
You will need to start being explicit about which of the two new modules you want to use in your project: `OpenAPIKit` (now supports OpenAPI spec v3.1) and/or `OpenAPIKit30` (continues to support OpenAPI spec v3.0 like the previous versions of OpenAPIKit did).
57-
58-
In package manifests, dependencies will be one of:
59-
```
60-
// v3.0 of spec:
61-
dependencies: [.product(name: "OpenAPIKit30", package: "OpenAPIKit")]
62-
63-
// v3.1 of spec:
64-
dependencies: [.product(name: "OpenAPIKit", package: "OpenAPIKit")]
65-
```
66-
67-
Your imports need to be specific as well:
68-
```swift
69-
// v3.0 of spec:
70-
import OpenAPIKit30
54+
#### 3.x to 4.x
55+
If you are migrating from OpenAPIKit 3.x to OpenAPIKit 4.x, check out the [v4 migration guide](./documentation/migration_guides/v4_migration_guide.md).
7156

72-
// v3.1 of spec:
73-
import OpenAPIKit
74-
```
57+
Be aware of the changes to minimum Swift version and minimum Yams version (although Yams is only a test dependency of OpenAPIKit).
7558

76-
It is recommended that you build your project against the `OpenAPIKit` module and only use `OpenAPIKit30` to support reading OpenAPI 3.0.x documents in and then [converting them](#supporting-openapi-30x-documents) to OpenAPI 3.1.x documents. The situation not supported yet by this strategy is where you need to write out an OpenAPI 3.0.x document (as opposed to 3.1.x). That is a planned feature but it has not yet been implemented. If your use-case benefits from reading in an OpenAPI 3.0.x document and also writing out an OpenAPI 3.0.x document then you can operate entirely against the `OpenAPIKit30` module.
59+
#### 4.x to 5.x
60+
If you are migrating from OpenAPIKit 4.x to OpenAPIKit 5.x, check out the [v5 migration guide](./documentation/migration_guides/v5_migration_guide.md).
7761

78-
#### 3.x to 4.x
79-
If you are migrating from OpenAPIKit 3.x to OpenAPIKit 4.x, check out the [v4 migration guide](./documentation/v4_migration_guide.md).
62+
Be aware of the change to minimum Swift version.
8063

8164
### Decoding OpenAPI Documents
8265

83-
Most documentation will focus on what it looks like to work with the `OpenAPIKit` module and OpenAPI 3.1.x documents. If you need to support OpenAPI 3.0.x documents, take a look at the section on [supporting OpenAPI 3.0.x documents](#supporting-openapi-30x-documents) before you get too deep into this library's docs.
66+
Most documentation will focus on what it looks like to work with the `OpenAPIKit` module and OpenAPI 3.2.x documents. If you need to support OpenAPI 3.0.x documents, take a look at the section on [supporting OpenAPI 3.0.x documents](#supporting-openapi-30x-documents) before you get too deep into this library's docs.
67+
68+
Version 3.2.x of the OpenAPI Specification is backwards compatible with version 3.1.x of the specification but it adds some new features. The OpenAPIKit types support these new features regardless of what the stated Document version is, but if a Document states that it is version 3.1.x and it uses OAS 3.2.x features then OpenAPIKit will produce a warning. If you run strict validations on the document, those warnings will be errors. If you choose not to run strict validations on the document, you can handle such a document leniently.
8469

8570
You can decode a JSON OpenAPI document (i.e. using the `JSONDecoder` from **Foundation** library) or a YAML OpenAPI document (i.e. using the `YAMLDecoder` from the [**Yams**](https://github.com/jpsim/Yams) library) with the following code:
8671
```swift
@@ -148,21 +133,21 @@ You can use this same validation system to dig arbitrarily deep into an OpenAPI
148133
### Supporting OpenAPI 3.0.x Documents
149134
If you need to operate on OpenAPI 3.0.x documents and only 3.0.x documents, you can use the `OpenAPIKit30` module throughout your code.
150135

151-
However, if you need to operate on both OpenAPI 3.0.x and 3.1.x documents, the recommendation is to use the OpenAPIKit compatibility layer to read in a 3.0.x document and convert it to a 3.1.x document so that you can use just the one set of Swift types throughout most of your program. An example of that follows.
136+
However, if you need to operate on both OpenAPI 3.0.x and 3.1.x/3.2.x documents, the recommendation is to use the OpenAPIKit compatibility layer to read in a 3.0.x document and convert it to a 3.1.x or 3.2.x document so that you can use just the one set of Swift types throughout most of your program. An example of that follows.
152137

153-
In this example, only one file in the whole project needs to import `OpenAPIKit30` or `OpenAPIKitCompat`. Every other file would just import `OpenAPIKit` and work with the document in the 3.1.x format.
138+
In this example, only one file in the whole project needs to import `OpenAPIKit30` or `OpenAPIKitCompat`. Every other file would just import `OpenAPIKit` and work with the document in the 3.2.x format.
154139

155-
#### Converting from 3.0.x to 3.1.x
140+
#### Converting from 3.0.x to 3.2.x
156141
```swift
157142
// import OpenAPIKit30 for OpenAPI 3.0 document support
158143
import OpenAPIKit30
159-
// import OpenAPIKit for OpenAPI 3.1 document support
144+
// import OpenAPIKit for OpenAPI 3.2 document support
160145
import OpenAPIKit
161146
// import OpenAPIKitCompat to convert between the versions
162147
import OpenAPIKitCompat
163148

164-
// if most of your project just works with OpenAPI v3.1, most files only need to import OpenAPIKit.
165-
// Only in the file where you are supporting converting from OpenAPI v3.0 to v3.1 do you need the
149+
// if most of your project just works with OpenAPI v3.2, most files only need to import OpenAPIKit.
150+
// Only in the file where you are supporting converting from OpenAPI v3.0 to v3.2 do you need the
166151
// other two imports.
167152

168153
// we can support either version by attempting to parse an old version and then a new version if the old version fails
@@ -171,12 +156,12 @@ let newDoc: OpenAPIKit.OpenAPI.Document
171156

172157
oldDoc = try? JSONDecoder().decode(OpenAPI.Document.self, from: someFileData)
173158

174-
newDoc = oldDoc?.convert(to: .v3_1_1) ??
159+
newDoc = oldDoc?.convert(to: .v3_2_0) ??
175160
(try! JSONDecoder().decode(OpenAPI.Document.self, from: someFileData))
176-
// ^ Here we simply fall-back to 3.1.x if loading as 3.0.x failed. You could do a more
161+
// ^ Here we simply fall-back to 3.2.x if loading as 3.0.x failed. You could do a more
177162
// graceful job of this by determining up front which version to attempt to load or by
178163
// holding onto errors for each decode attempt so you can tell the user why the document
179-
// failed to decode as neither 3.0.x nor 3.1.x if it fails in both cases.
164+
// failed to decode as neither 3.0.x nor 3.2.x if it fails in both cases.
180165
```
181166

182167
### A note on dictionary ordering
@@ -187,7 +172,7 @@ If retaining order is important for your use-case, I recommend the [**Yams**](ht
187172
The Foundation JSON encoding and decoding will be the most stable and battle-tested option with Yams as a pretty well established and stable option as well. FineJSON is lesser used (to my knowledge) but I have had success with it in the past.
188173

189174
### OpenAPI Document structure
190-
The types used by this library largely mirror the object definitions found in the OpenAPI specification [version 3.1.1](https://spec.openapis.org/oas/v3.1.1.html) (`OpenAPIKit` module) and [version 3.0.4](https://spec.openapis.org/oas/v3.0.4.html) (`OpenAPIKit30` module). The [Project Status](#project-status) lists each object defined by the spec and the name of the respective type in this library. The project status page currently focuses on OpenAPI 3.1.x but for the purposes of determining what things are named and what is supported you can mostly infer the status of the OpenAPI 3.0.x support as well.
175+
The types used by this library largely mirror the object definitions found in the OpenAPI specification [version 3.2.0](https://spec.openapis.org/oas/v3.2.0.html) (`OpenAPIKit` module) and [version 3.0.4](https://spec.openapis.org/oas/v3.0.4.html) (`OpenAPIKit30` module). The [Project Status](#project-status) lists each object defined by the spec and the name of the respective type in this library. The project status page currently focuses on OpenAPI 3.2.x but for the purposes of determining what things are named and what is supported you can mostly infer the status of the OpenAPI 3.0.x support as well.
191176

192177
#### Document Root
193178
At the root there is an `OpenAPI.Document`. In addition to some information that applies to the entire API, the document contains `OpenAPI.Components` (essentially a dictionary of reusable components that can be referenced with `JSONReferences` and `OpenAPI.References`) and an `OpenAPI.PathItem.Map` (a dictionary of routes your API defines).
@@ -210,7 +195,7 @@ A schema can be made **optional** (i.e. it can be omitted) with `JSONSchema.inte
210195

211196
A schema can be made **nullable** with `JSONSchema.number(nullable: true)` or an existing schema can be asked for a `nullableSchemaObject()`.
212197

213-
Nullability highlights an important decision OpenAPIKit makes. The JSON Schema specification that dictates how OpenAPI v3.1 documents _encode_ nullability states that a nullable property is encoded as having the `null` type in addition to whatever other type(s) it has. So in OpenAPIKit you set `nullability` as a property of a schema, but when encoded/decoded it will represent the inclusion of absence of `null` in the list of `type`s of the schema. If you are using the `OpenAPIKit30` module then nullability is encoded as a `nullable` property per the OpenAPI 3.0.x specification.
198+
Nullability highlights an important decision OpenAPIKit makes. The JSON Schema specification that dictates how OpenAPI v3.2 documents _encode_ nullability states that a nullable property is encoded as having the `null` type in addition to whatever other type(s) it has. So in OpenAPIKit you set `nullability` as a property of a schema, but when encoded/decoded it will represent the inclusion of absence of `null` in the list of `type`s of the schema. If you are using the `OpenAPIKit30` module then nullability is encoded as a `nullable` property per the OpenAPI 3.0.x specification.
214199

215200
Some types of schemas can be further specialized with a **format**. For example, `JSONSchema.number(format: .double)` or `JSONSchema.string(format: .dateTime)`.
216201

@@ -311,7 +296,7 @@ let document = OpenAPI.Document(
311296
```
312297

313298
#### Specification Extensions
314-
Many OpenAPIKit types support [Specification Extensions](https://spec.openapis.org/oas/v3.1.1.html#specification-extensions). As described in the OpenAPI Specification, these extensions must be objects that are keyed with the prefix "x-". For example, a property named "specialProperty" on the root OpenAPI Object (`OpenAPI.Document`) is invalid but the property "x-specialProperty" is a valid specification extension.
299+
Many OpenAPIKit types support [Specification Extensions](https://spec.openapis.org/oas/v3.2.0.html#specification-extensions). As described in the OpenAPI Specification, these extensions must be objects that are keyed with the prefix "x-". For example, a property named "specialProperty" on the root OpenAPI Object (`OpenAPI.Document`) is invalid but the property "x-specialProperty" is a valid specification extension.
315300

316301
You can get or set specification extensions via the [`vendorExtensions`](https://mattpolzin.github.io/OpenAPIKit/documentation/openapikit/vendorextendable/vendorextensions-swift.property) property on any object that supports this feature. The keys are `Strings` beginning with the aforementioned "x-" prefix and the values are `AnyCodable`. If you set an extension without using the "x-" prefix, the prefix will be added upon encoding.
317302

Sources/OpenAPIKit/Either/Either+Convenience.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ extension Either where B == OpenAPI.PathItem {
169169
head: OpenAPI.Operation? = nil,
170170
patch: OpenAPI.Operation? = nil,
171171
trace: OpenAPI.Operation? = nil,
172+
query: OpenAPI.Operation? = nil,
173+
additionalOperations: OrderedDictionary<OpenAPI.HttpMethod, OpenAPI.Operation> = [:],
172174
vendorExtensions: [String: AnyCodable] = [:]
173175
) {
174176
self = .b(
@@ -185,6 +187,8 @@ extension Either where B == OpenAPI.PathItem {
185187
head: head,
186188
patch: patch,
187189
trace: trace,
190+
query: query,
191+
additionalOperations: additionalOperations,
188192
vendorExtensions: vendorExtensions
189193
)
190194
)

Sources/OpenAPIKit/Encoding and Decoding Errors/PathDecodingError.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ extension OpenAPI.Error.Decoding.Path {
104104

105105
internal init(_ error: DecodingError) {
106106
var codingPath = error.codingPathWithoutSubject.dropFirst()
107-
let route = OpenAPI.Path(rawValue: codingPath.removeFirst().stringValue)
107+
let route = OpenAPI.Path(rawValue: codingPath.removeFirstPathComponentString())
108108

109109
path = route
110110
context = .other(error)
@@ -113,7 +113,7 @@ extension OpenAPI.Error.Decoding.Path {
113113

114114
internal init(_ error: OpenAPI.Error.Decoding.Operation) {
115115
var codingPath = error.codingPath.dropFirst()
116-
let route = OpenAPI.Path(rawValue: codingPath.removeFirst().stringValue)
116+
let route = OpenAPI.Path(rawValue: codingPath.removeFirstPathComponentString())
117117

118118
path = route
119119
context = .endpoint(error)
@@ -122,7 +122,7 @@ extension OpenAPI.Error.Decoding.Path {
122122

123123
internal init(_ error: GenericError) {
124124
var codingPath = error.codingPath.dropFirst()
125-
let route = OpenAPI.Path(rawValue: codingPath.removeFirst().stringValue)
125+
let route = OpenAPI.Path(rawValue: codingPath.removeFirstPathComponentString())
126126

127127
path = route
128128
context = .inconsistency(error)
@@ -148,7 +148,7 @@ extension OpenAPI.Error.Decoding.Path {
148148
// }
149149

150150
var codingPath = eitherError.codingPath.dropFirst()
151-
let route = OpenAPI.Path(rawValue: codingPath.removeFirst().stringValue)
151+
let route = OpenAPI.Path(rawValue: codingPath.removeFirstPathComponentString())
152152

153153
path = route
154154
context = .neither(eitherError)

0 commit comments

Comments
 (0)