From 97d026e990504784665b37ca208700966b3ef1a8 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Fri, 1 Nov 2024 17:14:40 -0500 Subject: [PATCH 1/2] use Encoder and Decoder userInfo to configure VendorExtension support for OpenAPIKit30 module. Add README note. --- README.md | 10 +++++++++ .../CodableVendorExtendable.swift | 20 ++++++++++++----- .../Components Object/Components.swift | 4 +++- Sources/OpenAPIKit30/Content/Content.swift | 4 +++- Sources/OpenAPIKit30/Document/Document.swift | 4 +++- .../OpenAPIKit30/Document/DocumentInfo.swift | 12 +++++++--- Sources/OpenAPIKit30/Example.swift | 4 +++- .../OpenAPIKit30/ExternalDocumentation.swift | 4 +++- Sources/OpenAPIKit30/Header/Header.swift | 4 +++- Sources/OpenAPIKit30/Link.swift | 4 +++- .../OpenAPIKit30/Operation/Operation.swift | 4 +++- .../OpenAPIKit30/Parameter/Parameter.swift | 4 +++- Sources/OpenAPIKit30/Path Item/PathItem.swift | 4 +++- Sources/OpenAPIKit30/Request/Request.swift | 4 +++- Sources/OpenAPIKit30/Response/Response.swift | 4 +++- .../Schema Object/JSONSchema.swift | 7 +++--- .../Security/SecurityScheme.swift | 4 +++- Sources/OpenAPIKit30/Server.swift | 8 +++++-- Sources/OpenAPIKit30/Tag.swift | 4 +++- .../Schema Object/JSONSchemaTests.swift | 22 +++++++++++++++++++ Tests/OpenAPIKit30Tests/TestHelpers.swift | 11 ++++++---- .../VendorExtendableTests.swift | 5 ++++- 22 files changed, 119 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index ae17c51a1..04dcc142a 100644 --- a/README.md +++ b/README.md @@ -293,6 +293,16 @@ Many OpenAPIKit types support [Specification Extensions](https://github.com/OAI/ You can get or set specification extensions via the `vendorExtensions` 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. +If you wish to disable decoding/encoding of vendor extensions for performance reasons, you can configure the Encoder and Decoder using their `userInfo`: +```swift +let userInfo = [VendorExtensionsConfiguration.enabledKey: false] +let encoder = JSONEncoder() +encoder.userInfo = userInfo + +let decoder = JSONDecoder() +decoder.userInfo = userInfo +``` + #### AnyCodable OpenAPIKit uses the `AnyCodable` type for vendor extensions and constructing examples for JSON Schemas. OpenAPIKit's `AnyCodable` type is an adaptation of the Flight School library that can be found [here](https://github.com/Flight-School/AnyCodable). diff --git a/Sources/OpenAPIKit30/CodableVendorExtendable.swift b/Sources/OpenAPIKit30/CodableVendorExtendable.swift index 1c75c293e..5f1d6401f 100644 --- a/Sources/OpenAPIKit30/CodableVendorExtendable.swift +++ b/Sources/OpenAPIKit30/CodableVendorExtendable.swift @@ -21,8 +21,21 @@ public protocol VendorExtendable { var vendorExtensions: VendorExtensions { get set } } +/// OpenAPIKit supports some additional Encoder/Decoder configuration above and beyond +/// what the Encoder or Decoder support out of box. +/// +/// To _disable_ encoding or decoding of Vendor Extensions (by default these are _enabled), +/// set `userInfo[VendorExtensionsConfiguration.enabledKey] = false` for your encoder or decoder. public enum VendorExtensionsConfiguration { - public static var isEnabled = true + public static let enabledKey: CodingUserInfoKey = .init(rawValue: "vendor-extensions-enabled")! + + static func isEnabled(for decoder: Decoder) -> Bool { + decoder.userInfo[enabledKey] as? Bool ?? true + } + + static func isEnabled(for encoder: Encoder) -> Bool { + encoder.userInfo[enabledKey] as? Bool ?? true + } } internal protocol ExtendableCodingKey: CodingKey, Equatable { @@ -75,7 +88,7 @@ internal enum VendorExtensionDecodingError: Swift.Error, CustomStringConvertible extension CodableVendorExtendable { internal static func extensions(from decoder: Decoder) throws -> VendorExtensions { - guard VendorExtensionsConfiguration.isEnabled else { + guard VendorExtensionsConfiguration.isEnabled(for: decoder) else { return [:] } @@ -109,9 +122,6 @@ extension CodableVendorExtendable { } internal func encodeExtensions(to container: inout T) throws where T.Key == Self.CodingKeys { - guard VendorExtensionsConfiguration.isEnabled else { - return - } for (key, value) in vendorExtensions { let xKey = key.starts(with: "x-") ? key : "x-\(key)" try container.encode(value, forKey: .extendedKey(for: xKey)) diff --git a/Sources/OpenAPIKit30/Components Object/Components.swift b/Sources/OpenAPIKit30/Components Object/Components.swift index 352f4841b..eda803eb7 100644 --- a/Sources/OpenAPIKit30/Components Object/Components.swift +++ b/Sources/OpenAPIKit30/Components Object/Components.swift @@ -176,7 +176,9 @@ extension OpenAPI.Components: Encodable { try container.encode(callbacks, forKey: .callbacks) } - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit30/Content/Content.swift b/Sources/OpenAPIKit30/Content/Content.swift index f2dc83f2e..82facb8fd 100644 --- a/Sources/OpenAPIKit30/Content/Content.swift +++ b/Sources/OpenAPIKit30/Content/Content.swift @@ -161,7 +161,9 @@ extension OpenAPI.Content: Encodable { try container.encodeIfPresent(encoding, forKey: .encoding) - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit30/Document/Document.swift b/Sources/OpenAPIKit30/Document/Document.swift index 89159e1eb..b46ee725d 100644 --- a/Sources/OpenAPIKit30/Document/Document.swift +++ b/Sources/OpenAPIKit30/Document/Document.swift @@ -442,7 +442,9 @@ extension OpenAPI.Document: Encodable { try container.encode(paths, forKey: .paths) - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } if !components.isEmpty { try container.encode(components, forKey: .components) diff --git a/Sources/OpenAPIKit30/Document/DocumentInfo.swift b/Sources/OpenAPIKit30/Document/DocumentInfo.swift index 7eda90a3c..dab15a39a 100644 --- a/Sources/OpenAPIKit30/Document/DocumentInfo.swift +++ b/Sources/OpenAPIKit30/Document/DocumentInfo.swift @@ -128,7 +128,9 @@ extension OpenAPI.Document.Info.License: Encodable { try container.encode(name, forKey: .name) try container.encodeIfPresent(url?.absoluteString, forKey: .url) - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } @@ -194,7 +196,9 @@ extension OpenAPI.Document.Info.Contact: Encodable { try container.encodeIfPresent(url?.absoluteString, forKey: .url) try container.encodeIfPresent(email, forKey: .email) - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } @@ -269,7 +273,9 @@ extension OpenAPI.Document.Info: Encodable { try container.encodeIfPresent(license, forKey: .license) try container.encode(version, forKey: .version) - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit30/Example.swift b/Sources/OpenAPIKit30/Example.swift index d28424238..46d014ece 100644 --- a/Sources/OpenAPIKit30/Example.swift +++ b/Sources/OpenAPIKit30/Example.swift @@ -82,7 +82,9 @@ extension OpenAPI.Example: Encodable { break } - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit30/ExternalDocumentation.swift b/Sources/OpenAPIKit30/ExternalDocumentation.swift index 84a90a273..01f2a520b 100644 --- a/Sources/OpenAPIKit30/ExternalDocumentation.swift +++ b/Sources/OpenAPIKit30/ExternalDocumentation.swift @@ -44,7 +44,9 @@ extension OpenAPI.ExternalDocumentation: Encodable { try container.encodeIfPresent(description, forKey: .description) try container.encode(url.absoluteString, forKey: .url) - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit30/Header/Header.swift b/Sources/OpenAPIKit30/Header/Header.swift index 718ed935b..e9dc74d57 100644 --- a/Sources/OpenAPIKit30/Header/Header.swift +++ b/Sources/OpenAPIKit30/Header/Header.swift @@ -275,7 +275,9 @@ extension OpenAPI.Header: Encodable { try container.encode(deprecated, forKey: .deprecated) } - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit30/Link.swift b/Sources/OpenAPIKit30/Link.swift index c416e97b7..d36d4ec25 100644 --- a/Sources/OpenAPIKit30/Link.swift +++ b/Sources/OpenAPIKit30/Link.swift @@ -163,7 +163,9 @@ extension OpenAPI.Link: Encodable { try container.encodeIfPresent(description, forKey: .description) try container.encodeIfPresent(server, forKey: .server) - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit30/Operation/Operation.swift b/Sources/OpenAPIKit30/Operation/Operation.swift index 992f0b3f5..9ecc7bb9c 100644 --- a/Sources/OpenAPIKit30/Operation/Operation.swift +++ b/Sources/OpenAPIKit30/Operation/Operation.swift @@ -271,7 +271,9 @@ extension OpenAPI.Operation: Encodable { try container.encodeIfPresent(servers, forKey: .servers) - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit30/Parameter/Parameter.swift b/Sources/OpenAPIKit30/Parameter/Parameter.swift index 045cdb13e..2e9d76a11 100644 --- a/Sources/OpenAPIKit30/Parameter/Parameter.swift +++ b/Sources/OpenAPIKit30/Parameter/Parameter.swift @@ -258,7 +258,9 @@ extension OpenAPI.Parameter: Encodable { try container.encode(deprecated, forKey: .deprecated) } - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit30/Path Item/PathItem.swift b/Sources/OpenAPIKit30/Path Item/PathItem.swift index 5829c6534..614d43207 100644 --- a/Sources/OpenAPIKit30/Path Item/PathItem.swift +++ b/Sources/OpenAPIKit30/Path Item/PathItem.swift @@ -257,7 +257,9 @@ extension OpenAPI.PathItem: Encodable { try container.encodeIfPresent(patch, forKey: .patch) try container.encodeIfPresent(trace, forKey: .trace) - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit30/Request/Request.swift b/Sources/OpenAPIKit30/Request/Request.swift index 0692215a4..2a465f6a1 100644 --- a/Sources/OpenAPIKit30/Request/Request.swift +++ b/Sources/OpenAPIKit30/Request/Request.swift @@ -97,7 +97,9 @@ extension OpenAPI.Request: Encodable { try container.encode(required, forKey: .required) } - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit30/Response/Response.swift b/Sources/OpenAPIKit30/Response/Response.swift index 56e057ddc..b68fa2bfc 100644 --- a/Sources/OpenAPIKit30/Response/Response.swift +++ b/Sources/OpenAPIKit30/Response/Response.swift @@ -158,7 +158,9 @@ extension OpenAPI.Response: Encodable { try container.encode(links, forKey: .links) } - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit30/Schema Object/JSONSchema.swift b/Sources/OpenAPIKit30/Schema Object/JSONSchema.swift index ff3fa8166..f8cce9993 100644 --- a/Sources/OpenAPIKit30/Schema Object/JSONSchema.swift +++ b/Sources/OpenAPIKit30/Schema Object/JSONSchema.swift @@ -264,7 +264,8 @@ public struct JSONSchema: JSONSchemaContext, HasWarnings, VendorExtendable { extension JSONSchema: Equatable { public static func == (lhs: JSONSchema, rhs: JSONSchema) -> Bool { - lhs.value == rhs.value + lhs.value == rhs.value && + lhs.vendorExtensions == rhs.vendorExtensions } } @@ -1718,7 +1719,7 @@ extension JSONSchema: Encodable { // Ad-hoc vendor extension encoding because keys are done differently for // JSONSchema - guard VendorExtensionsConfiguration.isEnabled else { + guard VendorExtensionsConfiguration.isEnabled(for: encoder) else { return } var container = encoder.container(keyedBy: VendorExtensionKeys.self) @@ -1889,7 +1890,7 @@ extension JSONSchema: Decodable { self.warnings = _warnings // Ad-hoc vendor extension support since JSONSchema does coding keys differently. - guard VendorExtensionsConfiguration.isEnabled else { + guard VendorExtensionsConfiguration.isEnabled(for: decoder) else { self.vendorExtensions = [:] return } diff --git a/Sources/OpenAPIKit30/Security/SecurityScheme.swift b/Sources/OpenAPIKit30/Security/SecurityScheme.swift index 22171d097..8bcdea35d 100644 --- a/Sources/OpenAPIKit30/Security/SecurityScheme.swift +++ b/Sources/OpenAPIKit30/Security/SecurityScheme.swift @@ -104,7 +104,9 @@ extension OpenAPI.SecurityScheme: Encodable { try container.encode(flows, forKey: .flows) } - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit30/Server.swift b/Sources/OpenAPIKit30/Server.swift index d44a280bf..b2b678584 100644 --- a/Sources/OpenAPIKit30/Server.swift +++ b/Sources/OpenAPIKit30/Server.swift @@ -104,7 +104,9 @@ extension OpenAPI.Server: Encodable { try container.encode(variables, forKey: .variables) } - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } @@ -180,7 +182,9 @@ extension OpenAPI.Server.Variable: Encodable { try container.encodeIfPresent(description, forKey: .description) - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit30/Tag.swift b/Sources/OpenAPIKit30/Tag.swift index 5669c977c..0f4c636c0 100644 --- a/Sources/OpenAPIKit30/Tag.swift +++ b/Sources/OpenAPIKit30/Tag.swift @@ -55,7 +55,9 @@ extension OpenAPI.Tag: Encodable { try container.encodeIfPresent(externalDocs, forKey: .externalDocs) - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Tests/OpenAPIKit30Tests/Schema Object/JSONSchemaTests.swift b/Tests/OpenAPIKit30Tests/Schema Object/JSONSchemaTests.swift index 673a69a2b..18dda979f 100644 --- a/Tests/OpenAPIKit30Tests/Schema Object/JSONSchemaTests.swift +++ b/Tests/OpenAPIKit30Tests/Schema Object/JSONSchemaTests.swift @@ -1373,6 +1373,28 @@ extension SchemaObjectTests { XCTAssertThrowsError(try orderUnstableDecode(JSONSchema.self, from: readOnlyWriteOnlyData)) } + func test_decodingWithVendorExtensionsTurnedOff() throws { + let vendorExtendedData = """ + { + "type": "object", + "x-hello": "hi" + } + """.data(using: .utf8)! + + let nonVendorExtendedData = """ + { + "type": "object" + } + """.data(using: .utf8)! + + let config = [VendorExtensionsConfiguration.enabledKey: false] + + let vendorExtended = try orderUnstableDecode(JSONSchema.self, from: vendorExtendedData, userInfo: config) + let nonVendorExtended = try orderUnstableDecode(JSONSchema.self, from: nonVendorExtendedData, userInfo: config) + + XCTAssertEqual(vendorExtended, nonVendorExtended) + } + func test_decodingWarnsForTypeAndPropertyConflict() throws { // has type "object" but "items" property that belongs with the "array" type. let badSchema = """ diff --git a/Tests/OpenAPIKit30Tests/TestHelpers.swift b/Tests/OpenAPIKit30Tests/TestHelpers.swift index ec2960f9d..eadd92726 100644 --- a/Tests/OpenAPIKit30Tests/TestHelpers.swift +++ b/Tests/OpenAPIKit30Tests/TestHelpers.swift @@ -40,8 +40,9 @@ func orderStableYAMLEncode(_ value: T) throws -> String { return try yamsTestEncoder.encode(value) } -fileprivate let foundationTestDecoder = { () -> JSONDecoder in +fileprivate func buildFoundationTestDecoder(_ userInfo: [CodingUserInfoKey: Any] = [:]) -> JSONDecoder { let decoder = JSONDecoder() + decoder.userInfo = userInfo if #available(macOS 10.12, *) { decoder.dateDecodingStrategy = .iso8601 decoder.keyDecodingStrategy = .useDefaultKeys @@ -51,10 +52,12 @@ fileprivate let foundationTestDecoder = { () -> JSONDecoder in decoder.keyDecodingStrategy = .useDefaultKeys #endif return decoder -}() +} + +fileprivate let foundationTestDecoder = { () -> JSONDecoder in buildFoundationTestDecoder() }() -func orderUnstableDecode(_ type: T.Type, from data: Data) throws -> T { - return try foundationTestDecoder.decode(T.self, from: data) +func orderUnstableDecode(_ type: T.Type, from data: Data, userInfo : [CodingUserInfoKey: Any] = [:]) throws -> T { + return try buildFoundationTestDecoder(userInfo).decode(T.self, from: data) } fileprivate let yamsTestDecoder = { () -> YAMLDecoder in diff --git a/Tests/OpenAPIKit30Tests/VendorExtendableTests.swift b/Tests/OpenAPIKit30Tests/VendorExtendableTests.swift index 944655df7..35cfa9e65 100644 --- a/Tests/OpenAPIKit30Tests/VendorExtendableTests.swift +++ b/Tests/OpenAPIKit30Tests/VendorExtendableTests.swift @@ -159,6 +159,9 @@ private struct TestStruct: Codable, CodableVendorExtendable { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode("world", forKey: .one) try container.encode("!", forKey: .two) - try encodeExtensions(to: &container) + + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } From 6641ac3f9ff645d27d02acab01739537319b937c Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Fri, 1 Nov 2024 17:23:56 -0500 Subject: [PATCH 2/2] use Encoder and Decoder userInfo to configure VendorExtension support for OpenAPIKit moudle --- .../OpenAPIKit/CodableVendorExtendable.swift | 20 ++++++++++++++----- .../Components Object/Components.swift | 4 +++- Sources/OpenAPIKit/Content/Content.swift | 4 +++- Sources/OpenAPIKit/Document/Document.swift | 4 +++- .../OpenAPIKit/Document/DocumentInfo.swift | 12 ++++++++--- Sources/OpenAPIKit/Example.swift | 4 +++- .../OpenAPIKit/ExternalDocumentation.swift | 4 +++- Sources/OpenAPIKit/Header/Header.swift | 4 +++- Sources/OpenAPIKit/Link.swift | 4 +++- Sources/OpenAPIKit/Operation/Operation.swift | 4 +++- Sources/OpenAPIKit/Parameter/Parameter.swift | 4 +++- Sources/OpenAPIKit/Path Item/PathItem.swift | 4 +++- Sources/OpenAPIKit/Request/Request.swift | 4 +++- Sources/OpenAPIKit/Response/Response.swift | 4 +++- .../OpenAPIKit/Schema Object/JSONSchema.swift | 4 ++-- .../OpenAPIKit/Security/SecurityScheme.swift | 4 +++- Sources/OpenAPIKit/Server.swift | 8 ++++++-- Sources/OpenAPIKit/Tag.swift | 4 +++- .../Schema Object/JSONSchemaTests.swift | 8 +++----- Tests/OpenAPIKitTests/TestHelpers.swift | 11 ++++++---- .../VendorExtendableTests.swift | 5 ++++- 21 files changed, 88 insertions(+), 36 deletions(-) diff --git a/Sources/OpenAPIKit/CodableVendorExtendable.swift b/Sources/OpenAPIKit/CodableVendorExtendable.swift index 1c75c293e..5f1d6401f 100644 --- a/Sources/OpenAPIKit/CodableVendorExtendable.swift +++ b/Sources/OpenAPIKit/CodableVendorExtendable.swift @@ -21,8 +21,21 @@ public protocol VendorExtendable { var vendorExtensions: VendorExtensions { get set } } +/// OpenAPIKit supports some additional Encoder/Decoder configuration above and beyond +/// what the Encoder or Decoder support out of box. +/// +/// To _disable_ encoding or decoding of Vendor Extensions (by default these are _enabled), +/// set `userInfo[VendorExtensionsConfiguration.enabledKey] = false` for your encoder or decoder. public enum VendorExtensionsConfiguration { - public static var isEnabled = true + public static let enabledKey: CodingUserInfoKey = .init(rawValue: "vendor-extensions-enabled")! + + static func isEnabled(for decoder: Decoder) -> Bool { + decoder.userInfo[enabledKey] as? Bool ?? true + } + + static func isEnabled(for encoder: Encoder) -> Bool { + encoder.userInfo[enabledKey] as? Bool ?? true + } } internal protocol ExtendableCodingKey: CodingKey, Equatable { @@ -75,7 +88,7 @@ internal enum VendorExtensionDecodingError: Swift.Error, CustomStringConvertible extension CodableVendorExtendable { internal static func extensions(from decoder: Decoder) throws -> VendorExtensions { - guard VendorExtensionsConfiguration.isEnabled else { + guard VendorExtensionsConfiguration.isEnabled(for: decoder) else { return [:] } @@ -109,9 +122,6 @@ extension CodableVendorExtendable { } internal func encodeExtensions(to container: inout T) throws where T.Key == Self.CodingKeys { - guard VendorExtensionsConfiguration.isEnabled else { - return - } for (key, value) in vendorExtensions { let xKey = key.starts(with: "x-") ? key : "x-\(key)" try container.encode(value, forKey: .extendedKey(for: xKey)) diff --git a/Sources/OpenAPIKit/Components Object/Components.swift b/Sources/OpenAPIKit/Components Object/Components.swift index cc1996b68..e563149cd 100644 --- a/Sources/OpenAPIKit/Components Object/Components.swift +++ b/Sources/OpenAPIKit/Components Object/Components.swift @@ -180,7 +180,9 @@ extension OpenAPI.Components: Encodable { try container.encode(pathItems, forKey: .pathItems) } - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit/Content/Content.swift b/Sources/OpenAPIKit/Content/Content.swift index e782c9818..474922032 100644 --- a/Sources/OpenAPIKit/Content/Content.swift +++ b/Sources/OpenAPIKit/Content/Content.swift @@ -161,7 +161,9 @@ extension OpenAPI.Content: Encodable { try container.encodeIfPresent(encoding, forKey: .encoding) - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit/Document/Document.swift b/Sources/OpenAPIKit/Document/Document.swift index d00f4d970..d92aa74b2 100644 --- a/Sources/OpenAPIKit/Document/Document.swift +++ b/Sources/OpenAPIKit/Document/Document.swift @@ -456,7 +456,9 @@ extension OpenAPI.Document: Encodable { try container.encode(paths, forKey: .paths) } - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } if !components.isEmpty { try container.encode(components, forKey: .components) diff --git a/Sources/OpenAPIKit/Document/DocumentInfo.swift b/Sources/OpenAPIKit/Document/DocumentInfo.swift index ddbf3a3ed..cc7fd30ab 100644 --- a/Sources/OpenAPIKit/Document/DocumentInfo.swift +++ b/Sources/OpenAPIKit/Document/DocumentInfo.swift @@ -191,7 +191,9 @@ extension OpenAPI.Document.Info.License: Encodable { } } - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } @@ -269,7 +271,9 @@ extension OpenAPI.Document.Info.Contact: Encodable { try container.encodeIfPresent(url?.absoluteString, forKey: .url) try container.encodeIfPresent(email, forKey: .email) - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } @@ -345,7 +349,9 @@ extension OpenAPI.Document.Info: Encodable { try container.encodeIfPresent(license, forKey: .license) try container.encode(version, forKey: .version) - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit/Example.swift b/Sources/OpenAPIKit/Example.swift index e9c904f7c..77fecd1f7 100644 --- a/Sources/OpenAPIKit/Example.swift +++ b/Sources/OpenAPIKit/Example.swift @@ -106,7 +106,9 @@ extension OpenAPI.Example: Encodable { break } - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit/ExternalDocumentation.swift b/Sources/OpenAPIKit/ExternalDocumentation.swift index 6fb8a7761..0621f908a 100644 --- a/Sources/OpenAPIKit/ExternalDocumentation.swift +++ b/Sources/OpenAPIKit/ExternalDocumentation.swift @@ -57,7 +57,9 @@ extension OpenAPI.ExternalDocumentation: Encodable { try container.encodeIfPresent(description, forKey: .description) try container.encode(url.absoluteString, forKey: .url) - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit/Header/Header.swift b/Sources/OpenAPIKit/Header/Header.swift index 309901af9..e4db3a705 100644 --- a/Sources/OpenAPIKit/Header/Header.swift +++ b/Sources/OpenAPIKit/Header/Header.swift @@ -289,7 +289,9 @@ extension OpenAPI.Header: Encodable { try container.encode(deprecated, forKey: .deprecated) } - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit/Link.swift b/Sources/OpenAPIKit/Link.swift index 39a1da918..1b731a924 100644 --- a/Sources/OpenAPIKit/Link.swift +++ b/Sources/OpenAPIKit/Link.swift @@ -174,7 +174,9 @@ extension OpenAPI.Link: Encodable { try container.encodeIfPresent(description, forKey: .description) try container.encodeIfPresent(server, forKey: .server) - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit/Operation/Operation.swift b/Sources/OpenAPIKit/Operation/Operation.swift index ab620e00e..446381609 100644 --- a/Sources/OpenAPIKit/Operation/Operation.swift +++ b/Sources/OpenAPIKit/Operation/Operation.swift @@ -291,7 +291,9 @@ extension OpenAPI.Operation: Encodable { try container.encodeIfPresent(servers, forKey: .servers) - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit/Parameter/Parameter.swift b/Sources/OpenAPIKit/Parameter/Parameter.swift index 9c4cf5611..00f1eae37 100644 --- a/Sources/OpenAPIKit/Parameter/Parameter.swift +++ b/Sources/OpenAPIKit/Parameter/Parameter.swift @@ -274,7 +274,9 @@ extension OpenAPI.Parameter: Encodable { try container.encode(deprecated, forKey: .deprecated) } - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit/Path Item/PathItem.swift b/Sources/OpenAPIKit/Path Item/PathItem.swift index 71f213c35..82e38c106 100644 --- a/Sources/OpenAPIKit/Path Item/PathItem.swift +++ b/Sources/OpenAPIKit/Path Item/PathItem.swift @@ -275,7 +275,9 @@ extension OpenAPI.PathItem: Encodable { try container.encodeIfPresent(patch, forKey: .patch) try container.encodeIfPresent(trace, forKey: .trace) - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit/Request/Request.swift b/Sources/OpenAPIKit/Request/Request.swift index 9d6df8d4e..64cc7084f 100644 --- a/Sources/OpenAPIKit/Request/Request.swift +++ b/Sources/OpenAPIKit/Request/Request.swift @@ -107,7 +107,9 @@ extension OpenAPI.Request: Encodable { try container.encode(required, forKey: .required) } - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit/Response/Response.swift b/Sources/OpenAPIKit/Response/Response.swift index 351d487bf..f66025963 100644 --- a/Sources/OpenAPIKit/Response/Response.swift +++ b/Sources/OpenAPIKit/Response/Response.swift @@ -168,7 +168,9 @@ extension OpenAPI.Response: Encodable { try container.encode(links, forKey: .links) } - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit/Schema Object/JSONSchema.swift b/Sources/OpenAPIKit/Schema Object/JSONSchema.swift index 599dfcbd1..036e5ed81 100644 --- a/Sources/OpenAPIKit/Schema Object/JSONSchema.swift +++ b/Sources/OpenAPIKit/Schema Object/JSONSchema.swift @@ -1930,7 +1930,7 @@ extension JSONSchema: Encodable { // Ad-hoc vendor extension encoding because keys are done differently for // JSONSchema - guard VendorExtensionsConfiguration.isEnabled else { + guard VendorExtensionsConfiguration.isEnabled(for: encoder) else { return } var container = encoder.container(keyedBy: VendorExtensionKeys.self) @@ -2140,7 +2140,7 @@ extension JSONSchema: Decodable { // Ad-hoc vendor extension support since JSONSchema does coding keys differently. let extensions: [String: AnyCodable] - guard VendorExtensionsConfiguration.isEnabled else { + guard VendorExtensionsConfiguration.isEnabled(for: decoder) else { self.value = value return } diff --git a/Sources/OpenAPIKit/Security/SecurityScheme.swift b/Sources/OpenAPIKit/Security/SecurityScheme.swift index 8e487bba7..c3228712e 100644 --- a/Sources/OpenAPIKit/Security/SecurityScheme.swift +++ b/Sources/OpenAPIKit/Security/SecurityScheme.swift @@ -125,7 +125,9 @@ extension OpenAPI.SecurityScheme: Encodable { try container.encode(SecurityType.Name.mutualTLS, forKey: .type) } - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit/Server.swift b/Sources/OpenAPIKit/Server.swift index d21a1b5e7..bc302c8fe 100644 --- a/Sources/OpenAPIKit/Server.swift +++ b/Sources/OpenAPIKit/Server.swift @@ -118,7 +118,9 @@ extension OpenAPI.Server: Encodable { try container.encode(variables, forKey: .variables) } - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } @@ -194,7 +196,9 @@ extension OpenAPI.Server.Variable: Encodable { try container.encodeIfPresent(description, forKey: .description) - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Sources/OpenAPIKit/Tag.swift b/Sources/OpenAPIKit/Tag.swift index 73ff53a64..b8bb60901 100644 --- a/Sources/OpenAPIKit/Tag.swift +++ b/Sources/OpenAPIKit/Tag.swift @@ -69,7 +69,9 @@ extension OpenAPI.Tag: Encodable { try container.encodeIfPresent(externalDocs, forKey: .externalDocs) - try encodeExtensions(to: &container) + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } } diff --git a/Tests/OpenAPIKitTests/Schema Object/JSONSchemaTests.swift b/Tests/OpenAPIKitTests/Schema Object/JSONSchemaTests.swift index b69e4b020..85f30f083 100644 --- a/Tests/OpenAPIKitTests/Schema Object/JSONSchemaTests.swift +++ b/Tests/OpenAPIKitTests/Schema Object/JSONSchemaTests.swift @@ -1741,14 +1741,12 @@ extension SchemaObjectTests { } """.data(using: .utf8)! - VendorExtensionsConfiguration.isEnabled = false + let config = [VendorExtensionsConfiguration.enabledKey: false] - let vendorExtended = try orderUnstableDecode(JSONSchema.self, from: vendorExtendedData) - let nonVendorExtended = try orderUnstableDecode(JSONSchema.self, from: nonVendorExtendedData) + let vendorExtended = try orderUnstableDecode(JSONSchema.self, from: vendorExtendedData, userInfo: config) + let nonVendorExtended = try orderUnstableDecode(JSONSchema.self, from: nonVendorExtendedData, userInfo: config) XCTAssertEqual(vendorExtended, nonVendorExtended) - - VendorExtensionsConfiguration.isEnabled = true } func test_decodingWarnsForTypeAndPropertyConflict() throws { diff --git a/Tests/OpenAPIKitTests/TestHelpers.swift b/Tests/OpenAPIKitTests/TestHelpers.swift index ec2960f9d..eadd92726 100644 --- a/Tests/OpenAPIKitTests/TestHelpers.swift +++ b/Tests/OpenAPIKitTests/TestHelpers.swift @@ -40,8 +40,9 @@ func orderStableYAMLEncode(_ value: T) throws -> String { return try yamsTestEncoder.encode(value) } -fileprivate let foundationTestDecoder = { () -> JSONDecoder in +fileprivate func buildFoundationTestDecoder(_ userInfo: [CodingUserInfoKey: Any] = [:]) -> JSONDecoder { let decoder = JSONDecoder() + decoder.userInfo = userInfo if #available(macOS 10.12, *) { decoder.dateDecodingStrategy = .iso8601 decoder.keyDecodingStrategy = .useDefaultKeys @@ -51,10 +52,12 @@ fileprivate let foundationTestDecoder = { () -> JSONDecoder in decoder.keyDecodingStrategy = .useDefaultKeys #endif return decoder -}() +} + +fileprivate let foundationTestDecoder = { () -> JSONDecoder in buildFoundationTestDecoder() }() -func orderUnstableDecode(_ type: T.Type, from data: Data) throws -> T { - return try foundationTestDecoder.decode(T.self, from: data) +func orderUnstableDecode(_ type: T.Type, from data: Data, userInfo : [CodingUserInfoKey: Any] = [:]) throws -> T { + return try buildFoundationTestDecoder(userInfo).decode(T.self, from: data) } fileprivate let yamsTestDecoder = { () -> YAMLDecoder in diff --git a/Tests/OpenAPIKitTests/VendorExtendableTests.swift b/Tests/OpenAPIKitTests/VendorExtendableTests.swift index b42b0c0bc..da10a74db 100644 --- a/Tests/OpenAPIKitTests/VendorExtendableTests.swift +++ b/Tests/OpenAPIKitTests/VendorExtendableTests.swift @@ -159,6 +159,9 @@ private struct TestStruct: Codable, CodableVendorExtendable { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode("world", forKey: .one) try container.encode("!", forKey: .two) - try encodeExtensions(to: &container) + + if VendorExtensionsConfiguration.isEnabled(for: encoder) { + try encodeExtensions(to: &container) + } } }