Skip to content

Commit

Permalink
Merge pull request #388 from mattpolzin/disable-vendor-extensions-via…
Browse files Browse the repository at this point in the history
…-userInfo

Disable vendor extensions via user info
  • Loading branch information
mattpolzin authored Nov 1, 2024
2 parents bc1d339 + 6641ac3 commit 2daac57
Show file tree
Hide file tree
Showing 43 changed files with 207 additions and 68 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,16 @@ Many OpenAPIKit types support [Specification Extensions](https://spec.openapis.o

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).

Expand Down
20 changes: 15 additions & 5 deletions Sources/OpenAPIKit/CodableVendorExtendable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 [:]
}

Expand Down Expand Up @@ -109,9 +122,6 @@ extension CodableVendorExtendable {
}

internal func encodeExtensions<T: KeyedEncodingContainerProtocol>(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))
Expand Down
4 changes: 3 additions & 1 deletion Sources/OpenAPIKit/Components Object/Components.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion Sources/OpenAPIKit/Content/Content.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion Sources/OpenAPIKit/Document/Document.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
12 changes: 9 additions & 3 deletions Sources/OpenAPIKit/Document/DocumentInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,9 @@ extension OpenAPI.Document.Info.License: Encodable {
}
}

try encodeExtensions(to: &container)
if VendorExtensionsConfiguration.isEnabled(for: encoder) {
try encodeExtensions(to: &container)
}
}
}

Expand Down Expand Up @@ -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)
}
}
}

Expand Down Expand Up @@ -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)
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion Sources/OpenAPIKit/Example.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,9 @@ extension OpenAPI.Example: Encodable {
break
}

try encodeExtensions(to: &container)
if VendorExtensionsConfiguration.isEnabled(for: encoder) {
try encodeExtensions(to: &container)
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion Sources/OpenAPIKit/ExternalDocumentation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion Sources/OpenAPIKit/Header/Header.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion Sources/OpenAPIKit/Link.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion Sources/OpenAPIKit/Operation/Operation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion Sources/OpenAPIKit/Parameter/Parameter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion Sources/OpenAPIKit/Path Item/PathItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion Sources/OpenAPIKit/Request/Request.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion Sources/OpenAPIKit/Response/Response.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions Sources/OpenAPIKit/Schema Object/JSONSchema.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
}
Expand Down
4 changes: 3 additions & 1 deletion Sources/OpenAPIKit/Security/SecurityScheme.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}

Expand Down
8 changes: 6 additions & 2 deletions Sources/OpenAPIKit/Server.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}

Expand Down Expand Up @@ -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)
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion Sources/OpenAPIKit/Tag.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}

Expand Down
20 changes: 15 additions & 5 deletions Sources/OpenAPIKit30/CodableVendorExtendable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 [:]
}

Expand Down Expand Up @@ -109,9 +122,6 @@ extension CodableVendorExtendable {
}

internal func encodeExtensions<T: KeyedEncodingContainerProtocol>(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))
Expand Down
4 changes: 3 additions & 1 deletion Sources/OpenAPIKit30/Components Object/Components.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion Sources/OpenAPIKit30/Content/Content.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion Sources/OpenAPIKit30/Document/Document.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
12 changes: 9 additions & 3 deletions Sources/OpenAPIKit30/Document/DocumentInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}

Expand Down Expand Up @@ -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)
}
}
}

Expand Down Expand Up @@ -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)
}
}
}

Expand Down
Loading

0 comments on commit 2daac57

Please sign in to comment.