From 6726d580cc4d5fe9c7b3fafab84dc98a137f1563 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Fri, 4 Aug 2023 16:38:45 -0500 Subject: [PATCH 001/110] beginning to play with an interface for loading external references. --- .../Components+Locatable.swift | 25 ++++---- .../Content/DereferencedContent.swift | 8 +++ .../Content/DereferencedContentEncoding.swift | 9 +++ .../Either+LocallyDereferenceable.swift | 9 +++ Sources/OpenAPIKit/Example.swift | 4 ++ .../OpenAPIKit/ExternalLoaderContext.swift | 39 +++++++++++++ .../Header/DereferencedHeader.swift | 6 ++ Sources/OpenAPIKit/JSONReference.swift | 22 ++++++- .../Operation/DereferencedOperation.swift | 6 ++ .../Parameter/DereferencedParameter.swift | 9 +++ .../Parameter/DereferencedSchemaContext.swift | 6 ++ Sources/OpenAPIKit/Parameter/Parameter.swift | 3 + .../Path Item/DereferencedPathItem.swift | 19 +++++++ .../Request/DereferencedRequest.swift | 6 ++ .../Response/DereferencedResponse.swift | 6 ++ .../DereferencedJSONSchema.swift | 6 ++ .../OpenAPIKit/Security/SecurityScheme.swift | 4 ++ .../OpenAPIKitCore/Shared/ComponentKey.swift | 23 ++++++++ .../Path Item/PathItemTests.swift | 57 +++++++++++++++++++ .../Validator/BuiltinValidationTests.swift | 2 +- 20 files changed, 255 insertions(+), 14 deletions(-) create mode 100644 Sources/OpenAPIKit/ExternalLoaderContext.swift diff --git a/Sources/OpenAPIKit/Components Object/Components+Locatable.swift b/Sources/OpenAPIKit/Components Object/Components+Locatable.swift index d7ff05880..c38d2aaa9 100644 --- a/Sources/OpenAPIKit/Components Object/Components+Locatable.swift +++ b/Sources/OpenAPIKit/Components Object/Components+Locatable.swift @@ -6,6 +6,7 @@ // import OpenAPIKitCore +import Foundation /// Anything conforming to ComponentDictionaryLocatable knows /// where to find resources of its type in the Components Dictionary. @@ -15,57 +16,57 @@ public protocol ComponentDictionaryLocatable: SummaryOverridable { /// This can be used to create a JSON path /// like `#/name1/name2/name3` static var openAPIComponentsKey: String { get } - static var openAPIComponentsKeyPath: KeyPath> { get } + static var openAPIComponentsKeyPath: WritableKeyPath> { get } } extension JSONSchema: ComponentDictionaryLocatable { public static var openAPIComponentsKey: String { "schemas" } - public static var openAPIComponentsKeyPath: KeyPath> { \.schemas } + public static var openAPIComponentsKeyPath: WritableKeyPath> { \.schemas } } extension OpenAPI.Response: ComponentDictionaryLocatable { public static var openAPIComponentsKey: String { "responses" } - public static var openAPIComponentsKeyPath: KeyPath> { \.responses } + public static var openAPIComponentsKeyPath: WritableKeyPath> { \.responses } } extension OpenAPI.Callbacks: ComponentDictionaryLocatable & SummaryOverridable { public static var openAPIComponentsKey: String { "callbacks" } - public static var openAPIComponentsKeyPath: KeyPath> { \.callbacks } + public static var openAPIComponentsKeyPath: WritableKeyPath> { \.callbacks } } extension OpenAPI.Parameter: ComponentDictionaryLocatable { public static var openAPIComponentsKey: String { "parameters" } - public static var openAPIComponentsKeyPath: KeyPath> { \.parameters } + public static var openAPIComponentsKeyPath: WritableKeyPath> { \.parameters } } extension OpenAPI.Example: ComponentDictionaryLocatable { public static var openAPIComponentsKey: String { "examples" } - public static var openAPIComponentsKeyPath: KeyPath> { \.examples } + public static var openAPIComponentsKeyPath: WritableKeyPath> { \.examples } } extension OpenAPI.Request: ComponentDictionaryLocatable { public static var openAPIComponentsKey: String { "requestBodies" } - public static var openAPIComponentsKeyPath: KeyPath> { \.requestBodies } + public static var openAPIComponentsKeyPath: WritableKeyPath> { \.requestBodies } } extension OpenAPI.Header: ComponentDictionaryLocatable { public static var openAPIComponentsKey: String { "headers" } - public static var openAPIComponentsKeyPath: KeyPath> { \.headers } + public static var openAPIComponentsKeyPath: WritableKeyPath> { \.headers } } extension OpenAPI.SecurityScheme: ComponentDictionaryLocatable { public static var openAPIComponentsKey: String { "securitySchemes" } - public static var openAPIComponentsKeyPath: KeyPath> { \.securitySchemes } + public static var openAPIComponentsKeyPath: WritableKeyPath> { \.securitySchemes } } extension OpenAPI.Link: ComponentDictionaryLocatable { public static var openAPIComponentsKey: String { "links" } - public static var openAPIComponentsKeyPath: KeyPath> { \.links } + public static var openAPIComponentsKeyPath: WritableKeyPath> { \.links } } extension OpenAPI.PathItem: ComponentDictionaryLocatable { public static var openAPIComponentsKey: String { "pathItems" } - public static var openAPIComponentsKeyPath: KeyPath> { \.pathItems } + public static var openAPIComponentsKeyPath: WritableKeyPath> { \.pathItems } } /// A dereferenceable type can be recursively looked up in @@ -93,6 +94,8 @@ public protocol LocallyDereferenceable { /// All types that provide a `_dereferenced(in:following:)` implementation have a `dereferenced(in:)` /// implementation for free. func _dereferenced(in components: OpenAPI.Components, following references: Set) throws -> DereferencedSelf + + func externallyDereferenced(in context: inout Context) throws -> Self } extension LocallyDereferenceable { diff --git a/Sources/OpenAPIKit/Content/DereferencedContent.swift b/Sources/OpenAPIKit/Content/DereferencedContent.swift index d078815d2..0bf73f892 100644 --- a/Sources/OpenAPIKit/Content/DereferencedContent.swift +++ b/Sources/OpenAPIKit/Content/DereferencedContent.swift @@ -63,4 +63,12 @@ extension OpenAPI.Content: LocallyDereferenceable { public func _dereferenced(in components: OpenAPI.Components, following references: Set) throws -> DereferencedContent { return try DereferencedContent(self, resolvingIn: components, following: references) } + + public func externallyDereferenced(in context: inout Context) throws -> OpenAPI.Content where Context : ExternalLoaderContext { + var content = self + + // TOOD: need to locally dereference the schema, examples, and content encoding here. +#warning("need to locally dereference the schema, examples, and content encoding here.") + return content + } } diff --git a/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift b/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift index 9e7a6a549..8b1aa8b18 100644 --- a/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift +++ b/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift @@ -51,4 +51,13 @@ extension OpenAPI.Content.Encoding: LocallyDereferenceable { public func _dereferenced(in components: OpenAPI.Components, following references: Set) throws -> DereferencedContentEncoding { return try DereferencedContentEncoding(self, resolvingIn: components, following: references) } + + public func externallyDereferenced(in context: inout Context) throws -> OpenAPI.Content.Encoding where Context : ExternalLoaderContext { + var contentEncoding = self + + // TODO: need to externally dereference the headers here. +#warning("need to externally dereference the headers here.") + + return self + } } diff --git a/Sources/OpenAPIKit/Either/Either+LocallyDereferenceable.swift b/Sources/OpenAPIKit/Either/Either+LocallyDereferenceable.swift index 4c0225baa..321b09406 100644 --- a/Sources/OpenAPIKit/Either/Either+LocallyDereferenceable.swift +++ b/Sources/OpenAPIKit/Either/Either+LocallyDereferenceable.swift @@ -17,4 +17,13 @@ extension Either: LocallyDereferenceable where A: LocallyDereferenceable, B: Loc return try value._dereferenced(in: components, following: references) } } + + public func externallyDereferenced(in context: inout Context) throws -> Self where Context : ExternalLoaderContext { + switch self { + case .a(let a): + return .a(try a.externallyDereferenced(in: &context)) + case .b(let b): + return .b(try b.externallyDereferenced(in: &context)) + } + } } diff --git a/Sources/OpenAPIKit/Example.swift b/Sources/OpenAPIKit/Example.swift index fe2dd97e9..f6390d2b8 100644 --- a/Sources/OpenAPIKit/Example.swift +++ b/Sources/OpenAPIKit/Example.swift @@ -186,6 +186,10 @@ extension OpenAPI.Example: LocallyDereferenceable { public func _dereferenced(in components: OpenAPI.Components, following references: Set) throws -> OpenAPI.Example { return self } + + public func externallyDereferenced(in context: inout Context) throws -> OpenAPI.Example where Context : ExternalLoaderContext { + return self + } } extension OpenAPI.Example: Validatable {} diff --git a/Sources/OpenAPIKit/ExternalLoaderContext.swift b/Sources/OpenAPIKit/ExternalLoaderContext.swift new file mode 100644 index 000000000..bdedc2d5c --- /dev/null +++ b/Sources/OpenAPIKit/ExternalLoaderContext.swift @@ -0,0 +1,39 @@ +// +// ExternalLoaderContext.swift +// +// +// Created by Mathew Polzin on 7/30/2023. +// + +import OpenAPIKitCore +import Foundation + +/// An `ExternalLoaderContext` enables `OpenAPIKit` to load external references +/// without knowing the details of what decoder is being used or how new internal +/// references should be named. +public protocol ExternalLoaderContext { + /// External references are loaded into this Components Object. This allows for + /// loading external references into a single Document but also retaining the + /// identity of those refernces; that is, if three parts of a Document refer to + /// the same external reference, the external object will be loaded into this + /// Components Object and the three locations will still refer to the same + /// object (these are now internal references). + var components: OpenAPI.Components { get set } + + /// Load the given URL and decode it as type T. + static func load(_: URL) throws -> T where T: Decodable + + /// Determine the next Component Key (where to store something in the + /// Components Object) for a new object of the given type that was loaded + /// at the given external URL. + mutating func nextComponentKey(type: T.Type, at: URL) -> OpenAPI.ComponentKey +} + +extension ExternalLoaderContext { + mutating func store(type: T.Type, from url: URL) throws -> OpenAPI.Reference where T: ComponentDictionaryLocatable & Equatable & Decodable & LocallyDereferenceable { + let key = nextComponentKey(type: type, at: url) + let value: T = try Self.load(url) + components[keyPath: T.openAPIComponentsKeyPath][key] = try value.externallyDereferenced(in: &self) + return try components.reference(named: key.rawValue, ofType: T.self) + } +} diff --git a/Sources/OpenAPIKit/Header/DereferencedHeader.swift b/Sources/OpenAPIKit/Header/DereferencedHeader.swift index 6762e4d52..1cd81fd1a 100644 --- a/Sources/OpenAPIKit/Header/DereferencedHeader.swift +++ b/Sources/OpenAPIKit/Header/DereferencedHeader.swift @@ -71,4 +71,10 @@ extension OpenAPI.Header: LocallyDereferenceable { public func _dereferenced(in components: OpenAPI.Components, following references: Set) throws -> DereferencedHeader { return try DereferencedHeader(self, resolvingIn: components, following: references) } + + public func externallyDereferenced(in context: inout Context) throws -> OpenAPI.Header where Context : ExternalLoaderContext { + // TODO: externally dereference the schemaOrContent +#warning("externally dereference the schemaOrContent") + return self + } } diff --git a/Sources/OpenAPIKit/JSONReference.swift b/Sources/OpenAPIKit/JSONReference.swift index f1b5b7074..e6a796a68 100644 --- a/Sources/OpenAPIKit/JSONReference.swift +++ b/Sources/OpenAPIKit/JSONReference.swift @@ -501,7 +501,7 @@ extension OpenAPI.Reference: Decodable { } // MARK: - LocallyDereferenceable -extension JSONReference: LocallyDereferenceable where ReferenceType: LocallyDereferenceable { +extension JSONReference: LocallyDereferenceable where ReferenceType: LocallyDereferenceable & Decodable & Equatable { /// Look up the component this reference points to and then /// dereference it. /// @@ -522,9 +522,23 @@ extension JSONReference: LocallyDereferenceable where ReferenceType: LocallyDere .lookup(self) ._dereferenced(in: components, following: newReferences) } + + public func externallyDereferenced(in context: inout Context) throws -> Self where Context : ExternalLoaderContext { + switch self { + case .internal(let ref): + let value = try context.components.lookup(self) + .externallyDereferenced(in: &context) + let key = try OpenAPI.ComponentKey.forceInit(rawValue: ref.name) + context.components[keyPath: ReferenceType.openAPIComponentsKeyPath][key] = + value + return self + case .external(let url): + return try context.store(type: ReferenceType.self, from: url).jsonReference + } + } } -extension OpenAPI.Reference: LocallyDereferenceable where ReferenceType: LocallyDereferenceable { +extension OpenAPI.Reference: LocallyDereferenceable where ReferenceType: LocallyDereferenceable & Decodable & Equatable { /// Look up the component this reference points to and then /// dereference it. /// @@ -545,6 +559,10 @@ extension OpenAPI.Reference: LocallyDereferenceable where ReferenceType: Locally .lookup(self) ._dereferenced(in: components, following: newReferences) } + + public func externallyDereferenced(in context: inout Context) throws -> Self where Context : ExternalLoaderContext { + return .init(try jsonReference.externallyDereferenced(in: &context)) + } } extension OpenAPI.Reference: Validatable where ReferenceType: Validatable {} diff --git a/Sources/OpenAPIKit/Operation/DereferencedOperation.swift b/Sources/OpenAPIKit/Operation/DereferencedOperation.swift index ed45d7bc3..d02edc1a8 100644 --- a/Sources/OpenAPIKit/Operation/DereferencedOperation.swift +++ b/Sources/OpenAPIKit/Operation/DereferencedOperation.swift @@ -110,4 +110,10 @@ extension OpenAPI.Operation: LocallyDereferenceable { public func _dereferenced(in components: OpenAPI.Components, following references: Set) throws -> DereferencedOperation { return try DereferencedOperation(self, resolvingIn: components, following: references) } + + public func externallyDereferenced(in context: inout Context) throws -> OpenAPI.Operation where Context : ExternalLoaderContext { + // TODO: externally dereference security, responses, requestBody, and parameters +#warning("externally dereference security, responses, requestBody, and parameters") + return self + } } diff --git a/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift b/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift index 954ad52af..22be440c8 100644 --- a/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift +++ b/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift @@ -71,4 +71,13 @@ extension OpenAPI.Parameter: LocallyDereferenceable { public func _dereferenced(in components: OpenAPI.Components, following references: Set) throws -> DereferencedParameter { return try DereferencedParameter(self, resolvingIn: components, following: references) } + + public func externallyDereferenced(in context: inout Context) throws -> Self { + var parameter = self + + // TODO: externallyDerefence the schemaOrContent +#warning("need to externally dereference the schemaOrContent here") + + return parameter + } } diff --git a/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift b/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift index 76f4305a3..63f7dbec4 100644 --- a/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift +++ b/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift @@ -63,4 +63,10 @@ extension OpenAPI.Parameter.SchemaContext: LocallyDereferenceable { public func _dereferenced(in components: OpenAPI.Components, following references: Set) throws -> DereferencedSchemaContext { return try DereferencedSchemaContext(self, resolvingIn: components, following: references) } + + public func externallyDereferenced(in context: inout Context) throws -> OpenAPI.Parameter.SchemaContext where Context : ExternalLoaderContext { + // TODO: externally dereference schema, examples, and example +#warning("externally dereference schema, examples, and example") + return self + } } diff --git a/Sources/OpenAPIKit/Parameter/Parameter.swift b/Sources/OpenAPIKit/Parameter/Parameter.swift index 608126efb..a65dfcb0c 100644 --- a/Sources/OpenAPIKit/Parameter/Parameter.swift +++ b/Sources/OpenAPIKit/Parameter/Parameter.swift @@ -46,7 +46,10 @@ extension OpenAPI { /// where the values are anything codable. public var vendorExtensions: [String: AnyCodable] + /// Whether or not this parameter is required. See the context + /// which determines whether the parameter is required or not. public var required: Bool { context.required } + /// The location (e.g. "query") of the parameter. /// /// See the `context` property for more details on the diff --git a/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift b/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift index 9081e4650..c7172b708 100644 --- a/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift +++ b/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift @@ -6,6 +6,7 @@ // import OpenAPIKitCore +import Foundation /// An `OpenAPI.PathItem` type that guarantees /// its `parameters` and operations are inlined instead of @@ -126,4 +127,22 @@ extension OpenAPI.PathItem: LocallyDereferenceable { public func _dereferenced(in components: OpenAPI.Components, following references: Set) throws -> DereferencedPathItem { return try DereferencedPathItem(self, resolvingIn: components, following: references) } + + public func externallyDereferenced(in context: inout Context) throws -> Self { + var pathItem = self + + var newParameters = OpenAPI.Parameter.Array() + for parameterRef in pathItem.parameters { + newParameters.append( + try parameterRef.externallyDereferenced(in: &context) + ) + } + + pathItem.parameters = newParameters + + // TODO: load external references for entire PathItem object + + return pathItem + } } + diff --git a/Sources/OpenAPIKit/Request/DereferencedRequest.swift b/Sources/OpenAPIKit/Request/DereferencedRequest.swift index 6a2c00001..c0116d0c5 100644 --- a/Sources/OpenAPIKit/Request/DereferencedRequest.swift +++ b/Sources/OpenAPIKit/Request/DereferencedRequest.swift @@ -50,4 +50,10 @@ extension OpenAPI.Request: LocallyDereferenceable { public func _dereferenced(in components: OpenAPI.Components, following references: Set) throws -> DereferencedRequest { return try DereferencedRequest(self, resolvingIn: components, following: references) } + + public func externallyDereferenced(in context: inout Context) throws -> OpenAPI.Request where Context : ExternalLoaderContext { + // TODO: externally dereference the content +#warning("externally dereference the content") + return self + } } diff --git a/Sources/OpenAPIKit/Response/DereferencedResponse.swift b/Sources/OpenAPIKit/Response/DereferencedResponse.swift index ab6749fdf..7c9666d20 100644 --- a/Sources/OpenAPIKit/Response/DereferencedResponse.swift +++ b/Sources/OpenAPIKit/Response/DereferencedResponse.swift @@ -59,4 +59,10 @@ extension OpenAPI.Response: LocallyDereferenceable { public func _dereferenced(in components: OpenAPI.Components, following references: Set) throws -> DereferencedResponse { return try DereferencedResponse(self, resolvingIn: components, following: references) } + + public func externallyDereferenced(in context: inout Context) throws -> OpenAPI.Response where Context : ExternalLoaderContext { + // TODO: externally dereference the headers and content +#warning("externally dereference the headers and content") + return self + } } diff --git a/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift b/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift index a809c48d2..573837245 100644 --- a/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift +++ b/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift @@ -407,4 +407,10 @@ extension JSONSchema: LocallyDereferenceable { public func dereferenced() -> DereferencedJSONSchema? { return try? dereferenced(in: .noComponents) } + + public func externallyDereferenced(in context: inout Context) throws -> Self where Context : ExternalLoaderContext { + // TODO: externally dereference this schema +#warning("need to externally dereference json schemas") + return self + } } diff --git a/Sources/OpenAPIKit/Security/SecurityScheme.swift b/Sources/OpenAPIKit/Security/SecurityScheme.swift index 07e88e63f..8d8f09027 100644 --- a/Sources/OpenAPIKit/Security/SecurityScheme.swift +++ b/Sources/OpenAPIKit/Security/SecurityScheme.swift @@ -264,4 +264,8 @@ extension OpenAPI.SecurityScheme: LocallyDereferenceable { public func _dereferenced(in components: OpenAPI.Components, following references: Set) throws -> OpenAPI.SecurityScheme { return self } + + public func externallyDereferenced(in context: inout Context) throws -> OpenAPI.SecurityScheme where Context : ExternalLoaderContext { + return self + } } diff --git a/Sources/OpenAPIKitCore/Shared/ComponentKey.swift b/Sources/OpenAPIKitCore/Shared/ComponentKey.swift index f8ff7f48e..5388e5d1b 100644 --- a/Sources/OpenAPIKitCore/Shared/ComponentKey.swift +++ b/Sources/OpenAPIKitCore/Shared/ComponentKey.swift @@ -31,6 +31,16 @@ extension Shared { self.rawValue = rawValue } + public static func forceInit(rawValue: String?) throws -> ComponentKey { + guard let rawValue = rawValue else { + throw InvalidComponentKey() + } + guard let value = ComponentKey(rawValue: rawValue) else { + throw InvalidComponentKey(Self.problem(with: rawValue), rawValue: rawValue) + } + return value + } + public static func problem(with proposedString: String) -> String? { if Self(rawValue: proposedString) == nil { return "Keys for components in the Components Object must conform to the regex `^[a-zA-Z0-9\\.\\-_]+$`. '\(proposedString)' does not.." @@ -66,4 +76,17 @@ extension Shared { try container.encode(rawValue) } } + + public struct InvalidComponentKey: Swift.Error { + public let description: String + + internal init() { + description = "Failed to create a ComponentKey" + } + + internal init(_ message: String?, rawValue: String) { + description = message + ?? "Failed to create a ComponentKey from \(rawValue)" + } + } } diff --git a/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift b/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift index 0225bca79..97ccfbb78 100644 --- a/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift +++ b/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift @@ -472,3 +472,60 @@ extension PathItemTests { ) } } + +// MARK: External Dereferencing Tests +extension PathItemTests { + struct MockLoad: ExternalLoaderContext { + var components: OpenAPI.Components + + func nextComponentKey(type: T.Type, at url: URL) -> OpenAPI.ComponentKey { + "hello-world" + } + + static func load(_: URL) throws -> T where T : Decodable { + if let ret = OpenAPI.Request(description: "hello", content: [:]) as? T { + return ret + } + if let ret = OpenAPI.Parameter(name: "other-param", context: .header, schema: .string) as? T { + return ret + } + throw ValidationError(reason: "", at: []) + } + } + + func test_tmp() throws { + let components = OpenAPI.Components( + parameters: [ + "already-internal": + .init(name: "internal-param", context: .query, schema: .string), + ] + ) + let op = OpenAPI.Operation(responses: [:]) + let pathItem = OpenAPI.PathItem( + summary: "summary", + description: "description", + servers: [OpenAPI.Server(url: URL(string: "http://google.com")!)], + parameters: [ + .parameter(name: "hello", context: .query, schema: .string), + .reference(.component(named: "already-internal")), + .reference(.external(URL(string: "https://some-param.com")!)) + ], + get: .init(requestBody: .reference(.external(URL(string: "https://website.com")!)), responses: [:]), + put: op, + post: op, + delete: op, + options: op, + head: op, + patch: op, + trace: op + ) + + print(pathItem.parameters.debugDescription) + print("------") + var context = MockLoad(components: components) + let x = try pathItem.externallyDereferenced(in: &context) + print(x.parameters.debugDescription) + print("=======") + print(context.components.parameters) + } +} diff --git a/Tests/OpenAPIKitTests/Validator/BuiltinValidationTests.swift b/Tests/OpenAPIKitTests/Validator/BuiltinValidationTests.swift index 939057b99..0d2a6b8bf 100644 --- a/Tests/OpenAPIKitTests/Validator/BuiltinValidationTests.swift +++ b/Tests/OpenAPIKitTests/Validator/BuiltinValidationTests.swift @@ -749,7 +749,7 @@ final class BuiltinValidationTests: XCTestCase { components: .noComponents ) - // NOTE this is part of default validation + // NOTE these are part of default validation XCTAssertThrowsError(try document.validate()) { error in let error = error as? ValidationErrorCollection XCTAssertEqual(error?.values.count, 8) From b658586b37f36ebd20240f46809f0062a49d6de1 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sat, 5 Aug 2023 16:35:42 -0500 Subject: [PATCH 002/110] continuing to tweak the new external loading interface. --- .../Components+Locatable.swift | 2 +- .../Components Object/Components.swift | 30 +++++ .../Content/DereferencedContent.swift | 2 +- .../Content/DereferencedContentEncoding.swift | 2 +- Sources/OpenAPIKit/Document/Document.swift | 9 ++ .../Either+LocallyDereferenceable.swift | 6 +- Sources/OpenAPIKit/Example.swift | 2 +- Sources/OpenAPIKit/ExternalLoader.swift | 58 +++++++++ .../OpenAPIKit/ExternalLoaderContext.swift | 39 ------ .../Header/DereferencedHeader.swift | 2 +- Sources/OpenAPIKit/JSONReference.swift | 14 +-- .../Operation/DereferencedOperation.swift | 2 +- ...eredDictionary+ExternallyDereference.swift | 19 +++ .../Parameter/DereferencedParameter.swift | 2 +- .../Parameter/DereferencedSchemaContext.swift | 2 +- .../Path Item/DereferencedPathItem.swift | 4 +- .../Request/DereferencedRequest.swift | 2 +- .../Response/DereferencedResponse.swift | 2 +- .../DereferencedJSONSchema.swift | 2 +- .../OpenAPIKit/Security/SecurityScheme.swift | 2 +- .../Document/DocumentTests.swift | 119 ++++++++++++++++++ .../Path Item/PathItemTests.swift | 11 +- 22 files changed, 264 insertions(+), 69 deletions(-) create mode 100644 Sources/OpenAPIKit/ExternalLoader.swift delete mode 100644 Sources/OpenAPIKit/ExternalLoaderContext.swift create mode 100644 Sources/OpenAPIKit/OrderedDictionary+ExternallyDereference.swift diff --git a/Sources/OpenAPIKit/Components Object/Components+Locatable.swift b/Sources/OpenAPIKit/Components Object/Components+Locatable.swift index c38d2aaa9..f270e1d51 100644 --- a/Sources/OpenAPIKit/Components Object/Components+Locatable.swift +++ b/Sources/OpenAPIKit/Components Object/Components+Locatable.swift @@ -95,7 +95,7 @@ public protocol LocallyDereferenceable { /// implementation for free. func _dereferenced(in components: OpenAPI.Components, following references: Set) throws -> DereferencedSelf - func externallyDereferenced(in context: inout Context) throws -> Self + func externallyDereferenced(with loader: inout ExternalLoader) throws -> Self } extension LocallyDereferenceable { diff --git a/Sources/OpenAPIKit/Components Object/Components.swift b/Sources/OpenAPIKit/Components Object/Components.swift index b1aae51b7..2c0bb0804 100644 --- a/Sources/OpenAPIKit/Components Object/Components.swift +++ b/Sources/OpenAPIKit/Components Object/Components.swift @@ -260,4 +260,34 @@ extension OpenAPI.Components { } } +extension OpenAPI.Components { + private mutating func externallyDereference(dictionary: OpenAPI.ComponentDictionary, with loader: inout ExternalLoader) throws -> OpenAPI.ComponentDictionary where Context: ExternalLoaderContext, T: LocallyDereferenceable { + var newValues = OpenAPI.ComponentDictionary() + for (key, value) in dictionary { + newValues[key] = try value.externallyDereferenced(with: &loader) + } + return newValues + } + + internal mutating func externallyDereference(in context: Context) throws -> ExternalLoader where Context: ExternalLoaderContext { + var loader = ExternalLoader(components: self, context: context) + + schemas = try externallyDereference(dictionary: schemas, with: &loader) + responses = try externallyDereference(dictionary: responses, with: &loader) + parameters = try externallyDereference(dictionary: parameters, with: &loader) + examples = try externallyDereference(dictionary: examples, with: &loader) + requestBodies = try externallyDereference(dictionary: requestBodies, with: &loader) + headers = try externallyDereference(dictionary: headers, with: &loader) + securitySchemes = try externallyDereference(dictionary: securitySchemes, with: &loader) + + var newCallbacks = OpenAPI.ComponentDictionary() + for (key, value) in callbacks { + newCallbacks[key] = try value.externallyDereferenced(with: &loader) + } + callbacks = newCallbacks + + return loader + } +} + extension OpenAPI.Components: Validatable {} diff --git a/Sources/OpenAPIKit/Content/DereferencedContent.swift b/Sources/OpenAPIKit/Content/DereferencedContent.swift index 0bf73f892..6a6eac1f2 100644 --- a/Sources/OpenAPIKit/Content/DereferencedContent.swift +++ b/Sources/OpenAPIKit/Content/DereferencedContent.swift @@ -64,7 +64,7 @@ extension OpenAPI.Content: LocallyDereferenceable { return try DereferencedContent(self, resolvingIn: components, following: references) } - public func externallyDereferenced(in context: inout Context) throws -> OpenAPI.Content where Context : ExternalLoaderContext { + public func externallyDereferenced(with loader: inout ExternalLoader) throws -> OpenAPI.Content where Context : ExternalLoaderContext { var content = self // TOOD: need to locally dereference the schema, examples, and content encoding here. diff --git a/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift b/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift index 8b1aa8b18..7e04f20b7 100644 --- a/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift +++ b/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift @@ -52,7 +52,7 @@ extension OpenAPI.Content.Encoding: LocallyDereferenceable { return try DereferencedContentEncoding(self, resolvingIn: components, following: references) } - public func externallyDereferenced(in context: inout Context) throws -> OpenAPI.Content.Encoding where Context : ExternalLoaderContext { + public func externallyDereferenced(with loader: inout ExternalLoader) throws -> OpenAPI.Content.Encoding where Context : ExternalLoaderContext { var contentEncoding = self // TODO: need to externally dereference the headers here. diff --git a/Sources/OpenAPIKit/Document/Document.swift b/Sources/OpenAPIKit/Document/Document.swift index f34729339..d43f6c5aa 100644 --- a/Sources/OpenAPIKit/Document/Document.swift +++ b/Sources/OpenAPIKit/Document/Document.swift @@ -350,6 +350,15 @@ extension OpenAPI.Document { public func locallyDereferenced() throws -> DereferencedDocument { return try DereferencedDocument(self) } + + public mutating func externallyDereference(in context: Context) throws where Context: ExternalLoaderContext { + var loader: ExternalLoader = try components.externallyDereference(in: context) + + paths = try paths.externallyDereferenced(with: &loader) + webhooks = try webhooks.externallyDereferenced(with: &loader) + + components = loader.components + } } extension OpenAPI { diff --git a/Sources/OpenAPIKit/Either/Either+LocallyDereferenceable.swift b/Sources/OpenAPIKit/Either/Either+LocallyDereferenceable.swift index 321b09406..6ccc7b9b6 100644 --- a/Sources/OpenAPIKit/Either/Either+LocallyDereferenceable.swift +++ b/Sources/OpenAPIKit/Either/Either+LocallyDereferenceable.swift @@ -18,12 +18,12 @@ extension Either: LocallyDereferenceable where A: LocallyDereferenceable, B: Loc } } - public func externallyDereferenced(in context: inout Context) throws -> Self where Context : ExternalLoaderContext { + public func externallyDereferenced(with loader: inout ExternalLoader) throws -> Self where Context : ExternalLoaderContext { switch self { case .a(let a): - return .a(try a.externallyDereferenced(in: &context)) + return .a(try a.externallyDereferenced(with: &loader)) case .b(let b): - return .b(try b.externallyDereferenced(in: &context)) + return .b(try b.externallyDereferenced(with: &loader)) } } } diff --git a/Sources/OpenAPIKit/Example.swift b/Sources/OpenAPIKit/Example.swift index f6390d2b8..87702ef7d 100644 --- a/Sources/OpenAPIKit/Example.swift +++ b/Sources/OpenAPIKit/Example.swift @@ -187,7 +187,7 @@ extension OpenAPI.Example: LocallyDereferenceable { return self } - public func externallyDereferenced(in context: inout Context) throws -> OpenAPI.Example where Context : ExternalLoaderContext { + public func externallyDereferenced(with loader: inout ExternalLoader) throws -> OpenAPI.Example where Context : ExternalLoaderContext { return self } } diff --git a/Sources/OpenAPIKit/ExternalLoader.swift b/Sources/OpenAPIKit/ExternalLoader.swift new file mode 100644 index 000000000..96f57d2f2 --- /dev/null +++ b/Sources/OpenAPIKit/ExternalLoader.swift @@ -0,0 +1,58 @@ +// +// ExternalLoader.swift +// +// +// Created by Mathew Polzin on 7/30/2023. +// + +import OpenAPIKitCore +import Foundation + +/// An `ExternalLoaderContext` enables `OpenAPIKit` to load external references +/// without knowing the details of what decoder is being used or how new internal +/// references should be named. +public protocol ExternalLoaderContext { + /// Load the given URL and decode it as Type `T`. All Types `T` are `Decodable`, so + /// the only real responsibility of a `load` function is to locate and load the given + /// `URL` and pass its `Data` or `String` (depending on the decoder) to an appropriate + /// `Decoder` for the given file type. + static func load(_: URL) throws -> T where T: Decodable + + /// Determine the next Component Key (where to store something in the + /// Components Object) for a new object of the given type that was loaded + /// at the given external URL. + /// + /// - Important: Ideally, this function returns distinct keys for all different objects + /// but the same key for all equal objects. In practice, this probably means that any + /// time the same type and URL pair are passed in the same `ComponentKey` should be + /// returned. + mutating func nextComponentKey(type: T.Type, at: URL, given components: OpenAPI.Components) throws -> OpenAPI.ComponentKey +} + +public struct ExternalLoader { + public init(components: OpenAPI.Components, context: Context) { + self.components = components + self.context = context + } + + /// External references are loaded into this Components Object. This allows for + /// loading external references into a single Document but also retaining the + /// identity of those refernces; that is, if three parts of a Document refer to + /// the same external reference, the external object will be loaded into this + /// Components Object and the three locations will still refer to the same + /// object (these are now internal references). + /// + /// In the most common use-cases, the starting place for this `components` property + /// should be the existing `Components` for some OpenAPI `Document`. This allows local + /// references to be followed while external references are loaded. + public internal(set) var components: OpenAPI.Components + + internal var context: Context + + internal mutating func store(type: T.Type, from url: URL) throws -> OpenAPI.Reference where T: ComponentDictionaryLocatable & Equatable & Decodable & LocallyDereferenceable { + let key = try context.nextComponentKey(type: type, at: url, given: components) + let value: T = try Context.load(url) + components[keyPath: T.openAPIComponentsKeyPath][key] = try value.externallyDereferenced(with: &self) + return try components.reference(named: key.rawValue, ofType: T.self) + } +} diff --git a/Sources/OpenAPIKit/ExternalLoaderContext.swift b/Sources/OpenAPIKit/ExternalLoaderContext.swift deleted file mode 100644 index bdedc2d5c..000000000 --- a/Sources/OpenAPIKit/ExternalLoaderContext.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// ExternalLoaderContext.swift -// -// -// Created by Mathew Polzin on 7/30/2023. -// - -import OpenAPIKitCore -import Foundation - -/// An `ExternalLoaderContext` enables `OpenAPIKit` to load external references -/// without knowing the details of what decoder is being used or how new internal -/// references should be named. -public protocol ExternalLoaderContext { - /// External references are loaded into this Components Object. This allows for - /// loading external references into a single Document but also retaining the - /// identity of those refernces; that is, if three parts of a Document refer to - /// the same external reference, the external object will be loaded into this - /// Components Object and the three locations will still refer to the same - /// object (these are now internal references). - var components: OpenAPI.Components { get set } - - /// Load the given URL and decode it as type T. - static func load(_: URL) throws -> T where T: Decodable - - /// Determine the next Component Key (where to store something in the - /// Components Object) for a new object of the given type that was loaded - /// at the given external URL. - mutating func nextComponentKey(type: T.Type, at: URL) -> OpenAPI.ComponentKey -} - -extension ExternalLoaderContext { - mutating func store(type: T.Type, from url: URL) throws -> OpenAPI.Reference where T: ComponentDictionaryLocatable & Equatable & Decodable & LocallyDereferenceable { - let key = nextComponentKey(type: type, at: url) - let value: T = try Self.load(url) - components[keyPath: T.openAPIComponentsKeyPath][key] = try value.externallyDereferenced(in: &self) - return try components.reference(named: key.rawValue, ofType: T.self) - } -} diff --git a/Sources/OpenAPIKit/Header/DereferencedHeader.swift b/Sources/OpenAPIKit/Header/DereferencedHeader.swift index 1cd81fd1a..fe9c1476b 100644 --- a/Sources/OpenAPIKit/Header/DereferencedHeader.swift +++ b/Sources/OpenAPIKit/Header/DereferencedHeader.swift @@ -72,7 +72,7 @@ extension OpenAPI.Header: LocallyDereferenceable { return try DereferencedHeader(self, resolvingIn: components, following: references) } - public func externallyDereferenced(in context: inout Context) throws -> OpenAPI.Header where Context : ExternalLoaderContext { + public func externallyDereferenced(with loader: inout ExternalLoader) throws -> OpenAPI.Header where Context : ExternalLoaderContext { // TODO: externally dereference the schemaOrContent #warning("externally dereference the schemaOrContent") return self diff --git a/Sources/OpenAPIKit/JSONReference.swift b/Sources/OpenAPIKit/JSONReference.swift index e6a796a68..297e6bf93 100644 --- a/Sources/OpenAPIKit/JSONReference.swift +++ b/Sources/OpenAPIKit/JSONReference.swift @@ -523,17 +523,17 @@ extension JSONReference: LocallyDereferenceable where ReferenceType: LocallyDere ._dereferenced(in: components, following: newReferences) } - public func externallyDereferenced(in context: inout Context) throws -> Self where Context : ExternalLoaderContext { + public func externallyDereferenced(with loader: inout ExternalLoader) throws -> Self where Context : ExternalLoaderContext { switch self { case .internal(let ref): - let value = try context.components.lookup(self) - .externallyDereferenced(in: &context) + let value = try loader.components.lookup(self) + .externallyDereferenced(with: &loader) let key = try OpenAPI.ComponentKey.forceInit(rawValue: ref.name) - context.components[keyPath: ReferenceType.openAPIComponentsKeyPath][key] = + loader.components[keyPath: ReferenceType.openAPIComponentsKeyPath][key] = value return self case .external(let url): - return try context.store(type: ReferenceType.self, from: url).jsonReference + return try loader.store(type: ReferenceType.self, from: url).jsonReference } } } @@ -560,8 +560,8 @@ extension OpenAPI.Reference: LocallyDereferenceable where ReferenceType: Locally ._dereferenced(in: components, following: newReferences) } - public func externallyDereferenced(in context: inout Context) throws -> Self where Context : ExternalLoaderContext { - return .init(try jsonReference.externallyDereferenced(in: &context)) + public func externallyDereferenced(with loader: inout ExternalLoader) throws -> Self where Context : ExternalLoaderContext { + return .init(try jsonReference.externallyDereferenced(with: &loader)) } } diff --git a/Sources/OpenAPIKit/Operation/DereferencedOperation.swift b/Sources/OpenAPIKit/Operation/DereferencedOperation.swift index d02edc1a8..56d341009 100644 --- a/Sources/OpenAPIKit/Operation/DereferencedOperation.swift +++ b/Sources/OpenAPIKit/Operation/DereferencedOperation.swift @@ -111,7 +111,7 @@ extension OpenAPI.Operation: LocallyDereferenceable { return try DereferencedOperation(self, resolvingIn: components, following: references) } - public func externallyDereferenced(in context: inout Context) throws -> OpenAPI.Operation where Context : ExternalLoaderContext { + public func externallyDereferenced(with loader: inout ExternalLoader) throws -> OpenAPI.Operation where Context : ExternalLoaderContext { // TODO: externally dereference security, responses, requestBody, and parameters #warning("externally dereference security, responses, requestBody, and parameters") return self diff --git a/Sources/OpenAPIKit/OrderedDictionary+ExternallyDereference.swift b/Sources/OpenAPIKit/OrderedDictionary+ExternallyDereference.swift new file mode 100644 index 000000000..e436c45f9 --- /dev/null +++ b/Sources/OpenAPIKit/OrderedDictionary+ExternallyDereference.swift @@ -0,0 +1,19 @@ +// +// OrderedDictionary+ExternallyDereference.swift +// OpenAPI +// +// Created by Mathew Polzin on 08/05/2023. +// + +import OpenAPIKitCore + +extension OrderedDictionary where Value: LocallyDereferenceable { + internal func externallyDereferenced(with loader: inout ExternalLoader) throws -> Self where Context: ExternalLoaderContext { + var newDict = Self() + for (key, value) in self { + let newRef = try value.externallyDereferenced(with: &loader) + newDict[key] = newRef + } + return newDict + } +} diff --git a/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift b/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift index 22be440c8..0ce65a871 100644 --- a/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift +++ b/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift @@ -72,7 +72,7 @@ extension OpenAPI.Parameter: LocallyDereferenceable { return try DereferencedParameter(self, resolvingIn: components, following: references) } - public func externallyDereferenced(in context: inout Context) throws -> Self { + public func externallyDereferenced(with loader: inout ExternalLoader) throws -> Self { var parameter = self // TODO: externallyDerefence the schemaOrContent diff --git a/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift b/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift index 63f7dbec4..aa584bf44 100644 --- a/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift +++ b/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift @@ -64,7 +64,7 @@ extension OpenAPI.Parameter.SchemaContext: LocallyDereferenceable { return try DereferencedSchemaContext(self, resolvingIn: components, following: references) } - public func externallyDereferenced(in context: inout Context) throws -> OpenAPI.Parameter.SchemaContext where Context : ExternalLoaderContext { + public func externallyDereferenced(with loader: inout ExternalLoader) throws -> OpenAPI.Parameter.SchemaContext where Context : ExternalLoaderContext { // TODO: externally dereference schema, examples, and example #warning("externally dereference schema, examples, and example") return self diff --git a/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift b/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift index c7172b708..28a2290d8 100644 --- a/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift +++ b/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift @@ -128,13 +128,13 @@ extension OpenAPI.PathItem: LocallyDereferenceable { return try DereferencedPathItem(self, resolvingIn: components, following: references) } - public func externallyDereferenced(in context: inout Context) throws -> Self { + public func externallyDereferenced(with loader: inout ExternalLoader) throws -> Self { var pathItem = self var newParameters = OpenAPI.Parameter.Array() for parameterRef in pathItem.parameters { newParameters.append( - try parameterRef.externallyDereferenced(in: &context) + try parameterRef.externallyDereferenced(with: &loader) ) } diff --git a/Sources/OpenAPIKit/Request/DereferencedRequest.swift b/Sources/OpenAPIKit/Request/DereferencedRequest.swift index c0116d0c5..a157d2ac6 100644 --- a/Sources/OpenAPIKit/Request/DereferencedRequest.swift +++ b/Sources/OpenAPIKit/Request/DereferencedRequest.swift @@ -51,7 +51,7 @@ extension OpenAPI.Request: LocallyDereferenceable { return try DereferencedRequest(self, resolvingIn: components, following: references) } - public func externallyDereferenced(in context: inout Context) throws -> OpenAPI.Request where Context : ExternalLoaderContext { + public func externallyDereferenced(with loader: inout ExternalLoader) throws -> OpenAPI.Request where Context : ExternalLoaderContext { // TODO: externally dereference the content #warning("externally dereference the content") return self diff --git a/Sources/OpenAPIKit/Response/DereferencedResponse.swift b/Sources/OpenAPIKit/Response/DereferencedResponse.swift index 7c9666d20..3eb2e23d9 100644 --- a/Sources/OpenAPIKit/Response/DereferencedResponse.swift +++ b/Sources/OpenAPIKit/Response/DereferencedResponse.swift @@ -60,7 +60,7 @@ extension OpenAPI.Response: LocallyDereferenceable { return try DereferencedResponse(self, resolvingIn: components, following: references) } - public func externallyDereferenced(in context: inout Context) throws -> OpenAPI.Response where Context : ExternalLoaderContext { + public func externallyDereferenced(with loader: inout ExternalLoader) throws -> OpenAPI.Response where Context : ExternalLoaderContext { // TODO: externally dereference the headers and content #warning("externally dereference the headers and content") return self diff --git a/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift b/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift index 573837245..6fbfe30be 100644 --- a/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift +++ b/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift @@ -408,7 +408,7 @@ extension JSONSchema: LocallyDereferenceable { return try? dereferenced(in: .noComponents) } - public func externallyDereferenced(in context: inout Context) throws -> Self where Context : ExternalLoaderContext { + public func externallyDereferenced(with loader: inout ExternalLoader) throws -> Self where Context : ExternalLoaderContext { // TODO: externally dereference this schema #warning("need to externally dereference json schemas") return self diff --git a/Sources/OpenAPIKit/Security/SecurityScheme.swift b/Sources/OpenAPIKit/Security/SecurityScheme.swift index 8d8f09027..7ab4e42d5 100644 --- a/Sources/OpenAPIKit/Security/SecurityScheme.swift +++ b/Sources/OpenAPIKit/Security/SecurityScheme.swift @@ -265,7 +265,7 @@ extension OpenAPI.SecurityScheme: LocallyDereferenceable { return self } - public func externallyDereferenced(in context: inout Context) throws -> OpenAPI.SecurityScheme where Context : ExternalLoaderContext { + public func externallyDereferenced(with loader: inout ExternalLoader) throws -> OpenAPI.SecurityScheme where Context : ExternalLoaderContext { return self } } diff --git a/Tests/OpenAPIKitTests/Document/DocumentTests.swift b/Tests/OpenAPIKitTests/Document/DocumentTests.swift index 63a80dbcb..0d2fdd7cd 100644 --- a/Tests/OpenAPIKitTests/Document/DocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/DocumentTests.swift @@ -1077,3 +1077,122 @@ extension DocumentTests { ) } } + +// MARK: - External Dereferencing +extension DocumentTests { + // temporarily test with an example of the new interface + func test_example() throws { + + /// An example of implementing a loader context for loading external references + /// into an OpenAPI document. + struct ExampleLoaderContext: ExternalLoaderContext { + static func load(_ url: URL) throws -> T where T : Decodable { + // load data from file, perhaps. we will just mock that up for the example: + let data = mockParameterData(url) + + return try JSONDecoder().decode(T.self, from: data) + } + + mutating func nextComponentKey(type: T.Type, at url: URL, given components: OpenAPIKit.OpenAPI.Components) throws -> OpenAPIKit.OpenAPI.ComponentKey { + // do anything you want here to determine what key the new component should be stored at. + // for the example, we will just transform the URL into a valid components key: + let urlString = url.pathComponents.dropFirst().joined(separator: "_").replacingOccurrences(of: ".", with: "_") + return try .forceInit(rawValue: urlString) + } + + /// Mock up some data, just for the example. + static func mockParameterData(_ url: URL) -> Data { + return """ + { + "name": "name", + "in": "path", + "schema": { "type": "string" }, + "required": true + } + """.data(using: .utf8)! + } + } + + + var document = OpenAPI.Document( + info: .init(title: "test document", version: "1.0.0"), + servers: [], + paths: [ + "/hello/{name}": .init( + parameters: [ + .reference(.external(URL(string: "file://./params/name.json")!)) + ] + ) + ], + components: .init( + // just to show, no parameters defined within document components : + parameters: [:] + ) + ) + + let encoder = JSONEncoder() + encoder.outputFormatting = .prettyPrinted + + // - MARK: Before + print( + String(data: try encoder.encode(document), encoding: .utf8)! + ) + /* + { + "openapi": "3.1.0", + "info": { + "title": "test document", + "version": "1.0.0" + }, + "paths": { + "\/hello\/{name}": { + "parameters": [ + { + "$ref": "file:\/\/.\/params\/name.json" + } + ] + } + } + } + */ + + let context = ExampleLoaderContext() + try document.externallyDereference(in: context) + + // - MARK: After + + print( + String(data: try encoder.encode(document), encoding: .utf8)! + ) + /* + { + "paths": { + "\/hello\/{name}": { + "parameters": [ + { + "$ref": "#\/components\/parameters\/params_name_json" + } + ] + } + }, + "components": { + "parameters": { + "params_name_json": { + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string" + } + } + } + }, + "openapi": "3.1.0", + "info": { + "title": "test document", + "version": "1.0.0" + } + } + */ + } +} diff --git a/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift b/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift index 97ccfbb78..3f5a09104 100644 --- a/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift +++ b/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift @@ -476,9 +476,7 @@ extension PathItemTests { // MARK: External Dereferencing Tests extension PathItemTests { struct MockLoad: ExternalLoaderContext { - var components: OpenAPI.Components - - func nextComponentKey(type: T.Type, at url: URL) -> OpenAPI.ComponentKey { + func nextComponentKey(type: T.Type, at url: URL, given components: OpenAPI.Components) -> OpenAPI.ComponentKey { "hello-world" } @@ -522,10 +520,11 @@ extension PathItemTests { print(pathItem.parameters.debugDescription) print("------") - var context = MockLoad(components: components) - let x = try pathItem.externallyDereferenced(in: &context) + let context = MockLoad() + var loader = ExternalLoader(components: components, context: context) + let x = try pathItem.externallyDereferenced(with: &loader) print(x.parameters.debugDescription) print("=======") - print(context.components.parameters) + print(loader.components.parameters) } } From 70c4faf1f5e4ec9924e5148eb99873fdd7f6cb0b Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sun, 6 Aug 2023 09:39:10 -0500 Subject: [PATCH 003/110] maybe store source URL as vendor extension. --- Sources/OpenAPIKit/CodableVendorExtendable.swift | 2 +- Sources/OpenAPIKit/Example.swift | 2 +- Tests/OpenAPIKitTests/Document/DocumentTests.swift | 12 ++++++++++-- Tests/OpenAPIKitTests/VendorExtendableTests.swift | 2 +- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Sources/OpenAPIKit/CodableVendorExtendable.swift b/Sources/OpenAPIKit/CodableVendorExtendable.swift index 9cfa2e0e0..1c75c293e 100644 --- a/Sources/OpenAPIKit/CodableVendorExtendable.swift +++ b/Sources/OpenAPIKit/CodableVendorExtendable.swift @@ -18,7 +18,7 @@ public protocol VendorExtendable { /// These should be of the form: /// `[ "x-extensionKey": ]` /// where the values are anything codable. - var vendorExtensions: VendorExtensions { get } + var vendorExtensions: VendorExtensions { get set } } public enum VendorExtensionsConfiguration { diff --git a/Sources/OpenAPIKit/Example.swift b/Sources/OpenAPIKit/Example.swift index 87702ef7d..2821faf9c 100644 --- a/Sources/OpenAPIKit/Example.swift +++ b/Sources/OpenAPIKit/Example.swift @@ -24,7 +24,7 @@ extension OpenAPI { /// These should be of the form: /// `[ "x-extensionKey": ]` /// where the values are anything codable. - public let vendorExtensions: [String: AnyCodable] + public var vendorExtensions: [String: AnyCodable] public init( summary: String? = nil, diff --git a/Tests/OpenAPIKitTests/Document/DocumentTests.swift b/Tests/OpenAPIKitTests/Document/DocumentTests.swift index 0d2fdd7cd..2094eee32 100644 --- a/Tests/OpenAPIKitTests/Document/DocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/DocumentTests.swift @@ -1090,7 +1090,15 @@ extension DocumentTests { // load data from file, perhaps. we will just mock that up for the example: let data = mockParameterData(url) - return try JSONDecoder().decode(T.self, from: data) + let decoded = try JSONDecoder().decode(T.self, from: data) + let finished: T + if var extendable = decoded as? VendorExtendable { + extendable.vendorExtensions["x-source-url"] = AnyCodable(url) + finished = extendable as! T + } else { + finished = decoded + } + return finished } mutating func nextComponentKey(type: T.Type, at url: URL, given components: OpenAPIKit.OpenAPI.Components) throws -> OpenAPIKit.OpenAPI.ComponentKey { @@ -1160,7 +1168,6 @@ extension DocumentTests { try document.externallyDereference(in: context) // - MARK: After - print( String(data: try encoder.encode(document), encoding: .utf8)! ) @@ -1178,6 +1185,7 @@ extension DocumentTests { "components": { "parameters": { "params_name_json": { + "x-source-url": "file:\/\/.\/params\/name.json", "in": "path", "name": "name", "required": true, diff --git a/Tests/OpenAPIKitTests/VendorExtendableTests.swift b/Tests/OpenAPIKitTests/VendorExtendableTests.swift index 5a49e5714..b42b0c0bc 100644 --- a/Tests/OpenAPIKitTests/VendorExtendableTests.swift +++ b/Tests/OpenAPIKitTests/VendorExtendableTests.swift @@ -145,7 +145,7 @@ private struct TestStruct: Codable, CodableVendorExtendable { } } - public let vendorExtensions: Self.VendorExtensions + public var vendorExtensions: Self.VendorExtensions init(vendorExtensions: Self.VendorExtensions) { self.vendorExtensions = vendorExtensions From 83b1ce13c88b5d7568c2b712b413e8aed1c03a1c Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sat, 4 Nov 2023 20:05:24 -0500 Subject: [PATCH 004/110] remove testing for swift prior to 5.8 --- .github/workflows/codecov.yml | 2 +- .github/workflows/tests.yml | 60 ++--------------------------------- 2 files changed, 3 insertions(+), 59 deletions(-) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 668c51c80..0d392008d 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -5,7 +5,7 @@ on: [pull_request] jobs: codecov: container: - image: swift:5.7 + image: swift:5.8 runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 442b5d5ee..13cbf756b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,62 +13,6 @@ jobs: fail-fast: false matrix: image: - - swift:5.1-xenial - - swift:5.1-bionic - - swift:5.2-xenial - - swift:5.2-bionic - - swift:5.2-focal - - swift:5.2-centos8 - - swift:5.2-amazonlinux2 - - swift:5.3-xenial - - swift:5.3-bionic - - swift:5.3-focal - - swift:5.3-centos8 - - swift:5.3-amazonlinux2 - # see below for 5.4, 5.5, etc. - container: ${{ matrix.image }} - steps: - - name: Checkout code - uses: actions/checkout@v3 - - name: Run tests - run: swift test --enable-test-discovery - # 5.4 is separate because there was a bug in the compiler that caused - # us to need to run swift tets with the -sil-verify-none flag. - linux-5_4: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - image: - - swift:5.4-xenial - - swift:5.4-bionic - - swift:5.4-focal - - swift:5.4-centos8 - - swift:5.4-amazonlinux2 - container: ${{ matrix.image }} - steps: - - name: Checkout code - uses: actions/checkout@v3 - - name: Run tests (without test discovery flag) - run: swift test -Xswiftc -Xfrontend -Xswiftc -sil-verify-none - linux-5_5-plus: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - image: - - swift:5.5-xenial - - swift:5.5-bionic - - swift:5.5-focal - - swift:5.5-centos8 - - swift:5.5-amazonlinux2 - - swift:5.6-bionic - - swift:5.6-focal - - swift:5.6-amazonlinux2 - - swift:5.7-bionic - - swift:5.7-focal - - swift:5.7-jammy - - swift:5.7-amazonlinux2 - swift:5.8-bionic - swift:5.8-focal - swift:5.8-jammy @@ -87,7 +31,7 @@ jobs: - name: Run tests run: swift test osx: - runs-on: macOS-12 + runs-on: macOS-13 steps: - name: Select latest available Xcode uses: maxim-lobanov/setup-xcode@v1 @@ -96,4 +40,4 @@ jobs: - name: Checkout code uses: actions/checkout@v3 - name: Run tests - run: swift test --enable-test-discovery + run: swift test From 84584677639bfb8d0c815286e5ff8eaa8ac63ce1 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sat, 4 Nov 2023 20:12:26 -0500 Subject: [PATCH 005/110] update the Package manifest --- Package.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Package.swift b/Package.swift index 9da86ea46..bb7163de1 100644 --- a/Package.swift +++ b/Package.swift @@ -1,11 +1,11 @@ -// swift-tools-version:5.1 +// swift-tools-version: 5.8 import PackageDescription let package = Package( name: "OpenAPIKit", platforms: [ - .macOS(.v10_10), + .macOS(.v10_13), .iOS(.v11) ], products: [ @@ -25,7 +25,8 @@ let package = Package( targets: [ .target( name: "OpenAPIKitCore", - dependencies: []), + dependencies: [], + exclude: ["AnyCodable/README.md"]), .testTarget( name: "OpenAPIKitCoreTests", dependencies: ["OpenAPIKitCore"]), From a48a435b9c4179804d3021a4ae93a9cf3fe11d22 Mon Sep 17 00:00:00 2001 From: PARAIPAN SORIN Date: Wed, 15 Nov 2023 00:13:06 +0200 Subject: [PATCH 006/110] Fix typos --- Sources/OpenAPIKit/Validator/Validation+Builtins.swift | 4 ++-- Sources/OpenAPIKit/Validator/Validator.swift | 4 ++-- .../Validator/BuiltinValidationTests.swift | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Sources/OpenAPIKit/Validator/Validation+Builtins.swift b/Sources/OpenAPIKit/Validator/Validation+Builtins.swift index 2316b3890..38cfa98cb 100644 --- a/Sources/OpenAPIKit/Validator/Validation+Builtins.swift +++ b/Sources/OpenAPIKit/Validator/Validation+Builtins.swift @@ -435,7 +435,7 @@ extension Validation { /// /// - Important: This is included in validation by default. /// - public static var serverVarialbeEnumIsValid: Validation { + public static var serverVariableEnumIsValid: Validation { .init( description: "Server Variable's enum is either not defined or is non-empty (if defined).", check: { context in @@ -450,7 +450,7 @@ extension Validation { /// /// - Important: This is included in validation by default. /// - public static var serverVarialbeDefaultExistsInEnum : Validation { + public static var serverVariableDefaultExistsInEnum : Validation { .init( description: "Server Variable's default must exist in enum, if enum is defined.", check: { context in diff --git a/Sources/OpenAPIKit/Validator/Validator.swift b/Sources/OpenAPIKit/Validator/Validator.swift index 812a9b1ab..b2be840f4 100644 --- a/Sources/OpenAPIKit/Validator/Validator.swift +++ b/Sources/OpenAPIKit/Validator/Validator.swift @@ -191,8 +191,8 @@ public final class Validator { .init(.headerReferencesAreValid), .init(.linkReferencesAreValid), .init(.pathItemReferencesAreValid), - .init(.serverVarialbeEnumIsValid), - .init(.serverVarialbeDefaultExistsInEnum) + .init(.serverVariableEnumIsValid), + .init(.serverVariableDefaultExistsInEnum) ]) } diff --git a/Tests/OpenAPIKitTests/Validator/BuiltinValidationTests.swift b/Tests/OpenAPIKitTests/Validator/BuiltinValidationTests.swift index eb683ccee..f907b433c 100644 --- a/Tests/OpenAPIKitTests/Validator/BuiltinValidationTests.swift +++ b/Tests/OpenAPIKitTests/Validator/BuiltinValidationTests.swift @@ -338,7 +338,7 @@ final class BuiltinValidationTests: XCTestCase { paths: [:], components: .noComponents ) - let validator = Validator.blank.validating(.serverVarialbeEnumIsValid) + let validator = Validator.blank.validating(.serverVariableEnumIsValid) XCTAssertThrowsError(try document.validate(using: validator)) { error in let error = error as? ValidationErrorCollection XCTAssertEqual(error?.values.first?.reason, "Failed to satisfy: Server Variable's enum is either not defined or is non-empty (if defined).") @@ -365,7 +365,7 @@ final class BuiltinValidationTests: XCTestCase { paths: [:], components: .noComponents ) - let validator = Validator.blank.validating(.serverVarialbeEnumIsValid) + let validator = Validator.blank.validating(.serverVariableEnumIsValid) try document.validate(using: validator) } @@ -389,7 +389,7 @@ final class BuiltinValidationTests: XCTestCase { paths: [:], components: .noComponents ) - let validator = Validator.blank.validating(.serverVarialbeDefaultExistsInEnum) + let validator = Validator.blank.validating(.serverVariableDefaultExistsInEnum) XCTAssertThrowsError(try document.validate(using: validator)) { error in let error = error as? ValidationErrorCollection XCTAssertEqual(error?.values.first?.reason, "Failed to satisfy: Server Variable's default must exist in enum, if enum is defined.") @@ -416,7 +416,7 @@ final class BuiltinValidationTests: XCTestCase { paths: [:], components: .noComponents ) - let validator = Validator.blank.validating(.serverVarialbeDefaultExistsInEnum) + let validator = Validator.blank.validating(.serverVariableDefaultExistsInEnum) try document.validate(using: validator) } From 00144b28357e87d1e4528c3b0704a058349b4502 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sat, 2 Dec 2023 17:54:18 -0600 Subject: [PATCH 007/110] small improvements to AnyCodable, more tests, and better documentation about limitations --- README.md | 22 ++++++ .../AnyCodable/AnyCodable.swift | 6 ++ Tests/AnyCodableTests/AnyCodableTests.swift | 75 +++++++++++++++++++ 3 files changed, 103 insertions(+) diff --git a/README.md b/README.md index 453707b66..7099ce9ca 100644 --- a/README.md +++ b/README.md @@ -288,6 +288,9 @@ 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. +#### 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). + `AnyCodable` can be constructed from literals or explicitly. The following are all valid. ```swift @@ -300,6 +303,25 @@ document.vendorExtensions["x-specialProperty4"] = ["hello": "world"] document.vendorExtensions["x-specialProperty5"] = AnyCodable("hello world") ``` +It is important to note that `AnyCodable` wraps Swift types in a way that keeps track of the Swift type used to construct it as much as possible, but if you encode an `AnyCodable` and then decode that result, the decoded value may not always be the same as the pre-encoded value started out. This is because many Swift types will encode to "stringy" values and then decode as simply `String` values. There are two ways to cope with this: + 1. When adding stringy values to structures that will be passed to `AnyCodable`, you can explicitly turn them into `String`s. For example, you can use `URL(...).absoluteString` both to specify you want the absolute value of the URL encoded and also to turn it into a `String` up front. + 2. When comparing `AnyCodable` values that have passed through a full encode/decode cycle, you can compare the `description` of the two `AnyCodable` values. This stringy result is _more likely_ to compare equivalently. + +Keep in mind, this issue only occurs when you are comparing value `a` and value `b` for equality given that `b` is `a` after being encoded and then subsequently decoded. + +The other sure-fire way to handle this (if you need encode-decode equality, not just equivalence) is to make sure you run both values being compared through encoding. For example, you might use the following function which doesn't even care if the input is `AnyCodable` or not: +```swift +func encodedEqual(_ a: A, _ b: B) throws -> Bool { + let a = try JSONEncoder().encode(a) + let b = try JSONEncoder().encode(b) + return a == b +} +``` +For example, the result of the following is `true`: +```swift +try encodeEqual(URL(string: "https://website.com"), AnyCodable(URL(string: "https://website.com"))) +``` + ### Dereferencing & Resolving In addition to looking something up in the `Components` object, you can entirely derefererence many OpenAPIKit types. A dereferenced type has had all of its references looked up (and all of its properties' references, all the way down). diff --git a/Sources/OpenAPIKitCore/AnyCodable/AnyCodable.swift b/Sources/OpenAPIKitCore/AnyCodable/AnyCodable.swift index de8b82aa1..bcefa16db 100644 --- a/Sources/OpenAPIKitCore/AnyCodable/AnyCodable.swift +++ b/Sources/OpenAPIKitCore/AnyCodable/AnyCodable.swift @@ -87,6 +87,8 @@ extension AnyCodable: Encodable { try container.encode(array.map { AnyCodable($0) }) case let dictionary as [String: Any?]: try container.encode(dictionary.mapValues { AnyCodable($0) }) + case let encodableValue as Encodable: + try container.encode(encodableValue) default: let context = EncodingError.Context(codingPath: container.codingPath, debugDescription: "AnyCodable value cannot be encoded") throw EncodingError.invalidValue(value, context) @@ -196,6 +198,8 @@ extension AnyCodable: Equatable { return lhs == rhs case let (lhs as [String: AnyCodable], rhs as [String: AnyCodable]): return lhs == rhs + case let (lhs as [String: Any], rhs as [String: Any]): + return lhs.mapValues(AnyCodable.init) == rhs.mapValues(AnyCodable.init) case let (lhs as [String], rhs as [String]): return lhs == rhs case let (lhs as [Int], rhs as [Int]): @@ -206,6 +210,8 @@ extension AnyCodable: Equatable { return lhs == rhs case let (lhs as [AnyCodable], rhs as [AnyCodable]): return lhs == rhs + case let (lhs as [Any], rhs as [Any]): + return lhs.map(AnyCodable.init) == rhs.map(AnyCodable.init) default: return false } diff --git a/Tests/AnyCodableTests/AnyCodableTests.swift b/Tests/AnyCodableTests/AnyCodableTests.swift index 9d17d7c7d..e45618ceb 100644 --- a/Tests/AnyCodableTests/AnyCodableTests.swift +++ b/Tests/AnyCodableTests/AnyCodableTests.swift @@ -41,10 +41,85 @@ class AnyCodableTests: XCTestCase { XCTAssertNotEqual(AnyCodable(()), AnyCodable(true)) } + func testEqualityFromJSON() throws { + let json = """ + { + "boolean": true, + "integer": 1, + "string": "string", + "array": [1, 2, 3], + "nested": { + "a": "alpha", + "b": "bravo", + "c": "charlie" + } + } + """.data(using: .utf8)! + let decoder = JSONDecoder() + let anyCodable0 = try decoder.decode(AnyCodable.self, from: json) + let anyCodable1 = try decoder.decode(AnyCodable.self, from: json) + XCTAssertEqual(anyCodable0, anyCodable1) + } + + struct CustomEncodable: Encodable { + let value1: String + + func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode("hi hi hi " + value1) + } + } + + func test_encodable() throws { + let value = CustomEncodable(value1: "hello") + let anyCodable = AnyCodable(value) + let thing = try JSONEncoder().encode(anyCodable) + XCTAssertEqual(String(data: thing, encoding: .utf8)!, "\"hi hi hi hello\"") + } + func testVoidDescription() { XCTAssertEqual(String(describing: AnyCodable(Void())), "nil") } + func test_encodedDecodedURL() throws { + let value = URL(string: "https://www.google.com") + let anyCodable = AnyCodable(value) + + // URL's absoluteString compares as equal to the wrapped any codable description. + XCTAssertEqual(value?.absoluteString, anyCodable.description) + + let encodedValue = try JSONEncoder().encode(value) + let encodedAnyCodable = try JSONEncoder().encode(anyCodable) + // the URL and the wrapped any codable encode as equals. + XCTAssertEqual(encodedValue, encodedAnyCodable) + + let decodedFromValue = try JSONDecoder().decode(AnyCodable.self, from: encodedValue) + // the URL decoded as any codable has the same description as the original any codable wrapper. + XCTAssertEqual(anyCodable.description, decodedFromValue.description) + + let decodedFromAnyCodable = try JSONDecoder().decode(AnyCodable.self, from: encodedAnyCodable) + // the decoded any codable has the same description as the original any codable wrapper. + XCTAssertEqual(anyCodable.description, decodedFromAnyCodable.description) + + func roundTripEqual(_ a: A, _ b: B) throws -> Bool { + let a = try JSONDecoder().decode(AnyCodable.self, + from: JSONEncoder().encode(a)) + let b = try JSONDecoder().decode(AnyCodable.self, + from: JSONEncoder().encode(b)) + return a == b + } + // if you encode/decode both, the URL and its AnyCodable wrapper are equal. + try XCTAssert(roundTripEqual(anyCodable, value)) + + func encodedEqual(_ a: A, _ b: B) throws -> Bool { + let a = try JSONEncoder().encode(a) + let b = try JSONEncoder().encode(b) + return a == b + } + // if you just compare the encoded data, the URL and its AnyCodable wrapper are equal. + try XCTAssert(encodedEqual(anyCodable, value)) + } + func testJSONDecoding() throws { let json = """ { From 5c9fd8663122768721367d14b8d4f9bc15d6bb45 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sat, 9 Dec 2023 18:57:12 -0600 Subject: [PATCH 008/110] use modern if-let syntax --- Sources/OpenAPIKit/Example.swift | 2 +- Sources/OpenAPIKit/Header/DereferencedHeader.swift | 2 +- Sources/OpenAPIKit/Link.swift | 2 +- Sources/OpenAPIKit/Parameter/DereferencedParameter.swift | 2 +- Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift | 2 +- Sources/OpenAPIKit/Request/DereferencedRequest.swift | 2 +- Sources/OpenAPIKit/Response/DereferencedResponse.swift | 2 +- Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift | 4 ++-- Sources/OpenAPIKit/Schema Object/JSONSchema+Combining.swift | 4 ++-- Sources/OpenAPIKit/Schema Object/JSONSchema.swift | 2 +- Sources/OpenAPIKit/Security/SecurityScheme.swift | 2 +- Sources/OpenAPIKit30/Example.swift | 2 +- Sources/OpenAPIKit30/Header/DereferencedHeader.swift | 2 +- Sources/OpenAPIKit30/Link.swift | 2 +- Sources/OpenAPIKit30/Parameter/DereferencedParameter.swift | 2 +- Sources/OpenAPIKit30/Path Item/DereferencedPathItem.swift | 2 +- Sources/OpenAPIKit30/Request/DereferencedRequest.swift | 2 +- Sources/OpenAPIKit30/Response/DereferencedResponse.swift | 2 +- Sources/OpenAPIKit30/Schema Object/JSONSchema+Combining.swift | 4 ++-- Sources/OpenAPIKit30/Schema Object/JSONSchema.swift | 2 +- Sources/OpenAPIKit30/Security/SecurityScheme.swift | 2 +- Sources/OpenAPIKitCompat/Compat30To31.swift | 4 ++-- 22 files changed, 26 insertions(+), 26 deletions(-) diff --git a/Sources/OpenAPIKit/Example.swift b/Sources/OpenAPIKit/Example.swift index 457edcc76..0ef738ee2 100644 --- a/Sources/OpenAPIKit/Example.swift +++ b/Sources/OpenAPIKit/Example.swift @@ -195,7 +195,7 @@ extension OpenAPI.Example: LocallyDereferenceable { dereferencedFromComponentNamed name: String? ) throws -> OpenAPI.Example{ var vendorExtensions = self.vendorExtensions - if let name = name { + if let name { vendorExtensions[OpenAPI.Components.componentNameExtension] = .init(name) } diff --git a/Sources/OpenAPIKit/Header/DereferencedHeader.swift b/Sources/OpenAPIKit/Header/DereferencedHeader.swift index 5f8eee40b..18f453a6c 100644 --- a/Sources/OpenAPIKit/Header/DereferencedHeader.swift +++ b/Sources/OpenAPIKit/Header/DereferencedHeader.swift @@ -57,7 +57,7 @@ public struct DereferencedHeader: Equatable { } var header = header - if let name = name { + if let name { header.vendorExtensions[OpenAPI.Components.componentNameExtension] = .init(name) } diff --git a/Sources/OpenAPIKit/Link.swift b/Sources/OpenAPIKit/Link.swift index 428e7c280..d1d2ac4b4 100644 --- a/Sources/OpenAPIKit/Link.swift +++ b/Sources/OpenAPIKit/Link.swift @@ -274,7 +274,7 @@ extension OpenAPI.Link: LocallyDereferenceable { dereferencedFromComponentNamed name: String? ) throws -> OpenAPI.Link { var vendorExtensions = self.vendorExtensions - if let name = name { + if let name { vendorExtensions[OpenAPI.Components.componentNameExtension] = .init(name) } diff --git a/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift b/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift index 97fb607b7..f2cdf232a 100644 --- a/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift +++ b/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift @@ -59,7 +59,7 @@ public struct DereferencedParameter: Equatable { } var parameter = parameter - if let name = name { + if let name { parameter.vendorExtensions[OpenAPI.Components.componentNameExtension] = .init(name) } diff --git a/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift b/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift index 7b711b1b1..93767b025 100644 --- a/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift +++ b/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift @@ -65,7 +65,7 @@ public struct DereferencedPathItem: Equatable { self.trace = try pathItem.trace.map { try DereferencedOperation($0, resolvingIn: components, following: references) } var pathItem = pathItem - if let name = name { + if let name { pathItem.vendorExtensions[OpenAPI.Components.componentNameExtension] = .init(name) } diff --git a/Sources/OpenAPIKit/Request/DereferencedRequest.swift b/Sources/OpenAPIKit/Request/DereferencedRequest.swift index 6e54e0c40..e7da192a5 100644 --- a/Sources/OpenAPIKit/Request/DereferencedRequest.swift +++ b/Sources/OpenAPIKit/Request/DereferencedRequest.swift @@ -38,7 +38,7 @@ public struct DereferencedRequest: Equatable { } var request = request - if let name = name { + if let name { request.vendorExtensions[OpenAPI.Components.componentNameExtension] = .init(name) } diff --git a/Sources/OpenAPIKit/Response/DereferencedResponse.swift b/Sources/OpenAPIKit/Response/DereferencedResponse.swift index 76e883179..c61ae5c7b 100644 --- a/Sources/OpenAPIKit/Response/DereferencedResponse.swift +++ b/Sources/OpenAPIKit/Response/DereferencedResponse.swift @@ -51,7 +51,7 @@ public struct DereferencedResponse: Equatable { } var response = response - if let name = name { + if let name { response.vendorExtensions[OpenAPI.Components.componentNameExtension] = .init(name) } diff --git a/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift b/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift index 509df1c09..077021b34 100644 --- a/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift +++ b/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift @@ -457,7 +457,7 @@ extension JSONSchema: LocallyDereferenceable { ) throws -> DereferencedJSONSchema { func addComponentNameExtension(to context: CoreContext) -> CoreContext { var extensions = context.vendorExtensions - if let name = name { + if let name { extensions[OpenAPI.Components.componentNameExtension] = .init(name) } return context.with(vendorExtensions: extensions) @@ -479,7 +479,7 @@ extension JSONSchema: LocallyDereferenceable { // TODO: consider which other core context properties to override here as with description ^ var extensions = dereferenced.vendorExtensions - if let name = name { + if let name { extensions[OpenAPI.Components.componentNameExtension] = .init(name) } dereferenced = dereferenced.with(vendorExtensions: vendorExtensions) diff --git a/Sources/OpenAPIKit/Schema Object/JSONSchema+Combining.swift b/Sources/OpenAPIKit/Schema Object/JSONSchema+Combining.swift index a8f48b3a5..06179aa46 100644 --- a/Sources/OpenAPIKit/Schema Object/JSONSchema+Combining.swift +++ b/Sources/OpenAPIKit/Schema Object/JSONSchema+Combining.swift @@ -608,7 +608,7 @@ extension JSONSchema.CoreContext { extension JSONSchema.IntegerContext { internal func validatedContext() throws -> JSONSchema.IntegerContext { let validatedMinimum: Bound? - if let minimum = minimum { + if let minimum { guard minimum.value >= 0 else { throw JSONSchemaResolutionError(.inconsistency("Integer minimum (\(minimum.value) cannot be below 0")) } @@ -633,7 +633,7 @@ extension JSONSchema.IntegerContext { extension JSONSchema.NumericContext { internal func validatedContext() throws -> JSONSchema.NumericContext { let validatedMinimum: Bound? - if let minimum = minimum { + if let minimum { guard minimum.value >= 0 else { throw JSONSchemaResolutionError(.inconsistency("Number minimum (\(minimum.value) cannot be below 0")) } diff --git a/Sources/OpenAPIKit/Schema Object/JSONSchema.swift b/Sources/OpenAPIKit/Schema Object/JSONSchema.swift index 9eebc8428..6f27102a6 100644 --- a/Sources/OpenAPIKit/Schema Object/JSONSchema.swift +++ b/Sources/OpenAPIKit/Schema Object/JSONSchema.swift @@ -1897,7 +1897,7 @@ extension JSONSchema: Decodable { // TODO: support multiple types instead of just grabbing the first one (see TODO immediately above as well) let typeHint = typeHints.first - if let typeHint = typeHint { + if let typeHint { let keysFromElsewhere = keysFrom.filter({ $0 != typeHint.group }) if !keysFromElsewhere.isEmpty { _warnings.append( diff --git a/Sources/OpenAPIKit/Security/SecurityScheme.swift b/Sources/OpenAPIKit/Security/SecurityScheme.swift index 2a33e1acd..ee15c6f0c 100644 --- a/Sources/OpenAPIKit/Security/SecurityScheme.swift +++ b/Sources/OpenAPIKit/Security/SecurityScheme.swift @@ -267,7 +267,7 @@ extension OpenAPI.SecurityScheme: LocallyDereferenceable { dereferencedFromComponentNamed name: String? ) throws -> OpenAPI.SecurityScheme { var ret = self - if let name = name { + if let name { ret.vendorExtensions[OpenAPI.Components.componentNameExtension] = .init(name) } return ret diff --git a/Sources/OpenAPIKit30/Example.swift b/Sources/OpenAPIKit30/Example.swift index 013f5b213..ad0786843 100644 --- a/Sources/OpenAPIKit30/Example.swift +++ b/Sources/OpenAPIKit30/Example.swift @@ -171,7 +171,7 @@ extension OpenAPI.Example: LocallyDereferenceable { dereferencedFromComponentNamed name: String? ) throws -> OpenAPI.Example{ var vendorExtensions = self.vendorExtensions - if let name = name { + if let name { vendorExtensions[OpenAPI.Components.componentNameExtension] = .init(name) } diff --git a/Sources/OpenAPIKit30/Header/DereferencedHeader.swift b/Sources/OpenAPIKit30/Header/DereferencedHeader.swift index 5f8eee40b..18f453a6c 100644 --- a/Sources/OpenAPIKit30/Header/DereferencedHeader.swift +++ b/Sources/OpenAPIKit30/Header/DereferencedHeader.swift @@ -57,7 +57,7 @@ public struct DereferencedHeader: Equatable { } var header = header - if let name = name { + if let name { header.vendorExtensions[OpenAPI.Components.componentNameExtension] = .init(name) } diff --git a/Sources/OpenAPIKit30/Link.swift b/Sources/OpenAPIKit30/Link.swift index 21c56ca7c..b6585d98a 100644 --- a/Sources/OpenAPIKit30/Link.swift +++ b/Sources/OpenAPIKit30/Link.swift @@ -263,7 +263,7 @@ extension OpenAPI.Link: LocallyDereferenceable { dereferencedFromComponentNamed name: String? ) throws -> OpenAPI.Link { var vendorExtensions = self.vendorExtensions - if let name = name { + if let name { vendorExtensions[OpenAPI.Components.componentNameExtension] = .init(name) } diff --git a/Sources/OpenAPIKit30/Parameter/DereferencedParameter.swift b/Sources/OpenAPIKit30/Parameter/DereferencedParameter.swift index 97fb607b7..f2cdf232a 100644 --- a/Sources/OpenAPIKit30/Parameter/DereferencedParameter.swift +++ b/Sources/OpenAPIKit30/Parameter/DereferencedParameter.swift @@ -59,7 +59,7 @@ public struct DereferencedParameter: Equatable { } var parameter = parameter - if let name = name { + if let name { parameter.vendorExtensions[OpenAPI.Components.componentNameExtension] = .init(name) } diff --git a/Sources/OpenAPIKit30/Path Item/DereferencedPathItem.swift b/Sources/OpenAPIKit30/Path Item/DereferencedPathItem.swift index 7b711b1b1..93767b025 100644 --- a/Sources/OpenAPIKit30/Path Item/DereferencedPathItem.swift +++ b/Sources/OpenAPIKit30/Path Item/DereferencedPathItem.swift @@ -65,7 +65,7 @@ public struct DereferencedPathItem: Equatable { self.trace = try pathItem.trace.map { try DereferencedOperation($0, resolvingIn: components, following: references) } var pathItem = pathItem - if let name = name { + if let name { pathItem.vendorExtensions[OpenAPI.Components.componentNameExtension] = .init(name) } diff --git a/Sources/OpenAPIKit30/Request/DereferencedRequest.swift b/Sources/OpenAPIKit30/Request/DereferencedRequest.swift index 18d37c70d..454e4c7aa 100644 --- a/Sources/OpenAPIKit30/Request/DereferencedRequest.swift +++ b/Sources/OpenAPIKit30/Request/DereferencedRequest.swift @@ -38,7 +38,7 @@ public struct DereferencedRequest: Equatable { } var request = request - if let name = name { + if let name { request.vendorExtensions[OpenAPI.Components.componentNameExtension] = .init(name) } diff --git a/Sources/OpenAPIKit30/Response/DereferencedResponse.swift b/Sources/OpenAPIKit30/Response/DereferencedResponse.swift index 76e883179..c61ae5c7b 100644 --- a/Sources/OpenAPIKit30/Response/DereferencedResponse.swift +++ b/Sources/OpenAPIKit30/Response/DereferencedResponse.swift @@ -51,7 +51,7 @@ public struct DereferencedResponse: Equatable { } var response = response - if let name = name { + if let name { response.vendorExtensions[OpenAPI.Components.componentNameExtension] = .init(name) } diff --git a/Sources/OpenAPIKit30/Schema Object/JSONSchema+Combining.swift b/Sources/OpenAPIKit30/Schema Object/JSONSchema+Combining.swift index 79adb97c1..e8097fed5 100644 --- a/Sources/OpenAPIKit30/Schema Object/JSONSchema+Combining.swift +++ b/Sources/OpenAPIKit30/Schema Object/JSONSchema+Combining.swift @@ -578,7 +578,7 @@ extension JSONSchema.CoreContext { extension JSONSchema.IntegerContext { internal func validatedContext() throws -> JSONSchema.IntegerContext { let validatedMinimum: Bound? - if let minimum = minimum { + if let minimum { guard minimum.value >= 0 else { throw JSONSchemaResolutionError(.inconsistency("Integer minimum (\(minimum.value) cannot be below 0")) } @@ -603,7 +603,7 @@ extension JSONSchema.IntegerContext { extension JSONSchema.NumericContext { internal func validatedContext() throws -> JSONSchema.NumericContext { let validatedMinimum: Bound? - if let minimum = minimum { + if let minimum { guard minimum.value >= 0 else { throw JSONSchemaResolutionError(.inconsistency("Number minimum (\(minimum.value) cannot be below 0")) } diff --git a/Sources/OpenAPIKit30/Schema Object/JSONSchema.swift b/Sources/OpenAPIKit30/Schema Object/JSONSchema.swift index 514bf3659..93dc1b10a 100644 --- a/Sources/OpenAPIKit30/Schema Object/JSONSchema.swift +++ b/Sources/OpenAPIKit30/Schema Object/JSONSchema.swift @@ -1831,7 +1831,7 @@ extension JSONSchema: Decodable { ) } - if let typeHint = typeHint { + if let typeHint { let keysFromElsewhere = keysFrom.filter({ $0 != typeHint.group }) if !keysFromElsewhere.isEmpty { _warnings.append( diff --git a/Sources/OpenAPIKit30/Security/SecurityScheme.swift b/Sources/OpenAPIKit30/Security/SecurityScheme.swift index 516471f21..8b883491e 100644 --- a/Sources/OpenAPIKit30/Security/SecurityScheme.swift +++ b/Sources/OpenAPIKit30/Security/SecurityScheme.swift @@ -244,7 +244,7 @@ extension OpenAPI.SecurityScheme: LocallyDereferenceable { dereferencedFromComponentNamed name: String? ) throws -> OpenAPI.SecurityScheme { var ret = self - if let name = name { + if let name { ret.vendorExtensions[OpenAPI.Components.componentNameExtension] = .init(name) } return ret diff --git a/Sources/OpenAPIKitCompat/Compat30To31.swift b/Sources/OpenAPIKitCompat/Compat30To31.swift index 92629cf08..444aabfea 100644 --- a/Sources/OpenAPIKitCompat/Compat30To31.swift +++ b/Sources/OpenAPIKitCompat/Compat30To31.swift @@ -172,7 +172,7 @@ extension OpenAPIKit30.OpenAPI.Parameter.SchemaContext: To31 { let newExamples = examples?.mapValues(eitherRefTo31) switch schema { case .a(let ref): - if let newExamples = newExamples { + if let newExamples { return OpenAPIKit.OpenAPI.Parameter.SchemaContext( schemaReference: .init(ref.to31()), style: style, @@ -190,7 +190,7 @@ extension OpenAPIKit30.OpenAPI.Parameter.SchemaContext: To31 { ) } case .b(let schema): - if let newExamples = newExamples { + if let newExamples { return OpenAPIKit.OpenAPI.Parameter.SchemaContext( schema.to31(), style: style, From 9fef5c32d43f6c3410e3be4bad2f5bf6cdd6c063 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Fri, 1 Mar 2024 14:29:18 -0600 Subject: [PATCH 009/110] Add Docc to package --- Package.resolved | 42 +++++++++++++++++++++++++++++------------- Package.swift | 1 + 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/Package.resolved b/Package.resolved index 3c56c5b7e..ac2140923 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,16 +1,32 @@ { - "object": { - "pins": [ - { - "package": "Yams", - "repositoryURL": "https://github.com/jpsim/Yams.git", - "state": { - "branch": null, - "revision": "9ff1cc9327586db4e0c8f46f064b6a82ec1566fa", - "version": "4.0.6" - } + "pins" : [ + { + "identity" : "swift-docc-plugin", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-docc-plugin", + "state" : { + "revision" : "26ac5758409154cc448d7ab82389c520fa8a8247", + "version" : "1.3.0" } - ] - }, - "version": 1 + }, + { + "identity" : "swift-docc-symbolkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-docc-symbolkit", + "state" : { + "revision" : "b45d1f2ed151d057b54504d653e0da5552844e34", + "version" : "1.0.0" + } + }, + { + "identity" : "yams", + "kind" : "remoteSourceControl", + "location" : "https://github.com/jpsim/Yams.git", + "state" : { + "revision" : "9ff1cc9327586db4e0c8f46f064b6a82ec1566fa", + "version" : "4.0.6" + } + } + ], + "version" : 2 } diff --git a/Package.swift b/Package.swift index bb7163de1..8402adb61 100644 --- a/Package.swift +++ b/Package.swift @@ -20,6 +20,7 @@ let package = Package( targets: ["OpenAPIKitCompat"]), ], dependencies: [ + .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"), .package(url: "https://github.com/jpsim/Yams.git", "4.0.0"..<"6.0.0") // just for tests ], targets: [ From 596e17361ee05c48258e1fa1de6e387d128f73e8 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Fri, 1 Mar 2024 16:11:48 -0600 Subject: [PATCH 010/110] update code doc link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7099ce9ca..fdebfeaaf 100644 --- a/README.md +++ b/README.md @@ -424,4 +424,4 @@ Please see [Security](./SECURITY.md) for information on how to report vulnerabil **Please do not report security vulnerabilities via GitHub issues.** ## Specification Coverage & Type Reference -For a full list of OpenAPI Specification types annotated with whether OpenAPIKit supports them and relevant translations to OpenAPIKit types, see the [Specification Coverage](./documentation/specification_coverage.md) documentation. For detailed information on the OpenAPIKit types, see the [full type documentation](https://github.com/mattpolzin/OpenAPIKit/wiki). +For a full list of OpenAPI Specification types annotated with whether OpenAPIKit supports them and relevant translations to OpenAPIKit types, see the [Specification Coverage](./documentation/specification_coverage.md) documentation. For detailed information on the OpenAPIKit types, see the [full type documentation](https://mattpolzin.github.io/OpenAPIKit/documentation/openapikit). From 5e72749541f0058079e6493725bcf8d9a28d3b12 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Fri, 1 Mar 2024 17:03:51 -0600 Subject: [PATCH 011/110] Adding documentation building to CI --- .github/workflows/documentation.yml | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index f5732698b..bfbc52ec4 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -3,7 +3,8 @@ name: Documentation on: push: branches: - - main + - release/4_0 + # ^ for now, we only want to use the v4.0 release branch. jobs: build: @@ -11,6 +12,25 @@ jobs: steps: - uses: actions/checkout@v3 + + - name: Build Docs + run: | + mkdir -p ./gh-pages + swift package --allow-writing-to-directory ./gh-pages/docs \ + generate-documentation --include-extended-types \ + --disable-indexing \ + --output-path ./gh-pages/docs \ + --transform-for-static-hosting \ + --hosting-base-path OpenAPIKit \ + --target OpenAPIKit + + - name: Deploy to GitHub Pages + uses: JamesIves/github-pages-deploy-action@v4.5.0 + with: + folder: gh-pages + branch: gh-pages + + # TODO: replace the documentation generator with something that is still maintained. # - name: Generate Documentation From fced755dd7d186a9dffb7dbdc0e41a8ee51f73db Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Fri, 1 Mar 2024 18:28:47 -0600 Subject: [PATCH 012/110] just get things building again --- Sources/OpenAPIKit/Callbacks.swift | 8 ++++++++ Sources/OpenAPIKit/Link.swift | 8 ++++++++ .../OrderedDictionary+ExternallyDereference.swift | 2 +- Sources/OpenAPIKit/Schema Object/JSONSchema.swift | 7 ++++++- 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/Sources/OpenAPIKit/Callbacks.swift b/Sources/OpenAPIKit/Callbacks.swift index 3d1edf793..5d34d42df 100644 --- a/Sources/OpenAPIKit/Callbacks.swift +++ b/Sources/OpenAPIKit/Callbacks.swift @@ -35,5 +35,13 @@ extension OpenAPI.CallbackURL: LocallyDereferenceable { ) throws -> OpenAPI.CallbackURL { self } + + public func externallyDereferenced( + with loader: inout ExternalLoader + ) throws -> Self where Context : ExternalLoaderContext { + // TODO: externally dereference security, responses, requestBody, and parameters +#warning("externally dereference security, responses, requestBody, and parameters") + return self + } } diff --git a/Sources/OpenAPIKit/Link.swift b/Sources/OpenAPIKit/Link.swift index 428e7c280..836c6fc85 100644 --- a/Sources/OpenAPIKit/Link.swift +++ b/Sources/OpenAPIKit/Link.swift @@ -287,6 +287,14 @@ extension OpenAPI.Link: LocallyDereferenceable { vendorExtensions: vendorExtensions ) } + + public func externallyDereferenced( + with loader: inout ExternalLoader + ) throws -> Self where Context : ExternalLoaderContext { + // TODO: externally dereference security, responses, requestBody, and parameters +#warning("externally dereference security, responses, requestBody, and parameters") + return self + } } extension OpenAPI.Link: Validatable {} diff --git a/Sources/OpenAPIKit/OrderedDictionary+ExternallyDereference.swift b/Sources/OpenAPIKit/OrderedDictionary+ExternallyDereference.swift index e436c45f9..46bbd8ba4 100644 --- a/Sources/OpenAPIKit/OrderedDictionary+ExternallyDereference.swift +++ b/Sources/OpenAPIKit/OrderedDictionary+ExternallyDereference.swift @@ -8,7 +8,7 @@ import OpenAPIKitCore extension OrderedDictionary where Value: LocallyDereferenceable { - internal func externallyDereferenced(with loader: inout ExternalLoader) throws -> Self where Context: ExternalLoaderContext { + public func externallyDereferenced(with loader: inout ExternalLoader) throws -> Self where Context: ExternalLoaderContext { var newDict = Self() for (key, value) in self { let newRef = try value.externallyDereferenced(with: &loader) diff --git a/Sources/OpenAPIKit/Schema Object/JSONSchema.swift b/Sources/OpenAPIKit/Schema Object/JSONSchema.swift index 122e64beb..9cb813541 100644 --- a/Sources/OpenAPIKit/Schema Object/JSONSchema.swift +++ b/Sources/OpenAPIKit/Schema Object/JSONSchema.swift @@ -427,7 +427,12 @@ extension JSONSchema: VendorExtendable { /// `[ "x-extensionKey": ]` /// where the values are anything codable. public var vendorExtensions: VendorExtensions { - coreContext.vendorExtensions + get { + coreContext.vendorExtensions + } + set { + #warning("implement me") + } } public func with(vendorExtensions: [String: AnyCodable]) -> JSONSchema { From 33f4d1fd1ef41dfdec1865462acfcc62a34977e4 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sat, 2 Mar 2024 17:28:02 -0600 Subject: [PATCH 013/110] bump macos version to support concurrency. sprinkle some async function handling into external dereferencing calls --- Package.swift | 2 +- Sources/OpenAPIKit/Callbacks.swift | 2 +- .../Components+Locatable.swift | 2 +- .../Components Object/Components.swift | 22 +++++++++---------- Sources/OpenAPIKit/Document/Document.swift | 9 ++++---- .../Either+LocallyDereferenceable.swift | 6 ++--- Sources/OpenAPIKit/ExternalLoader.swift | 8 +++---- Sources/OpenAPIKit/JSONReference.swift | 10 ++++----- Sources/OpenAPIKit/Link.swift | 2 +- .../Operation/DereferencedOperation.swift | 2 +- ...eredDictionary+ExternallyDereference.swift | 4 ++-- .../Parameter/DereferencedParameter.swift | 2 +- .../Parameter/DereferencedSchemaContext.swift | 2 +- .../Path Item/DereferencedPathItem.swift | 4 ++-- .../Request/DereferencedRequest.swift | 2 +- 15 files changed, 40 insertions(+), 39 deletions(-) diff --git a/Package.swift b/Package.swift index bb7163de1..1e8bed458 100644 --- a/Package.swift +++ b/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "OpenAPIKit", platforms: [ - .macOS(.v10_13), + .macOS(.v10_15), .iOS(.v11) ], products: [ diff --git a/Sources/OpenAPIKit/Callbacks.swift b/Sources/OpenAPIKit/Callbacks.swift index 5d34d42df..1347da656 100644 --- a/Sources/OpenAPIKit/Callbacks.swift +++ b/Sources/OpenAPIKit/Callbacks.swift @@ -38,7 +38,7 @@ extension OpenAPI.CallbackURL: LocallyDereferenceable { public func externallyDereferenced( with loader: inout ExternalLoader - ) throws -> Self where Context : ExternalLoaderContext { + ) async throws -> Self where Context : ExternalLoaderContext { // TODO: externally dereference security, responses, requestBody, and parameters #warning("externally dereference security, responses, requestBody, and parameters") return self diff --git a/Sources/OpenAPIKit/Components Object/Components+Locatable.swift b/Sources/OpenAPIKit/Components Object/Components+Locatable.swift index bf930fd79..b323db8ef 100644 --- a/Sources/OpenAPIKit/Components Object/Components+Locatable.swift +++ b/Sources/OpenAPIKit/Components Object/Components+Locatable.swift @@ -101,7 +101,7 @@ public protocol LocallyDereferenceable { func externallyDereferenced( with loader: inout ExternalLoader - ) throws -> Self + ) async throws -> Self } extension LocallyDereferenceable { diff --git a/Sources/OpenAPIKit/Components Object/Components.swift b/Sources/OpenAPIKit/Components Object/Components.swift index c50c82976..a8bc3e1a4 100644 --- a/Sources/OpenAPIKit/Components Object/Components.swift +++ b/Sources/OpenAPIKit/Components Object/Components.swift @@ -269,28 +269,28 @@ extension OpenAPI.Components { } extension OpenAPI.Components { - private mutating func externallyDereference(dictionary: OpenAPI.ComponentDictionary, with loader: inout ExternalLoader) throws -> OpenAPI.ComponentDictionary where Context: ExternalLoaderContext, T: LocallyDereferenceable { + private mutating func externallyDereference(dictionary: OpenAPI.ComponentDictionary, with loader: inout ExternalLoader) async throws -> OpenAPI.ComponentDictionary where Context: ExternalLoaderContext, T: LocallyDereferenceable { var newValues = OpenAPI.ComponentDictionary() for (key, value) in dictionary { - newValues[key] = try value.externallyDereferenced(with: &loader) + newValues[key] = try await value.externallyDereferenced(with: &loader) } return newValues } - internal mutating func externallyDereference(in context: Context) throws -> ExternalLoader where Context: ExternalLoaderContext { + internal mutating func externallyDereference(in context: Context) async throws -> ExternalLoader where Context: ExternalLoaderContext { var loader = ExternalLoader(components: self, context: context) - schemas = try externallyDereference(dictionary: schemas, with: &loader) - responses = try externallyDereference(dictionary: responses, with: &loader) - parameters = try externallyDereference(dictionary: parameters, with: &loader) - examples = try externallyDereference(dictionary: examples, with: &loader) - requestBodies = try externallyDereference(dictionary: requestBodies, with: &loader) - headers = try externallyDereference(dictionary: headers, with: &loader) - securitySchemes = try externallyDereference(dictionary: securitySchemes, with: &loader) + schemas = try await externallyDereference(dictionary: schemas, with: &loader) + responses = try await externallyDereference(dictionary: responses, with: &loader) + parameters = try await externallyDereference(dictionary: parameters, with: &loader) + examples = try await externallyDereference(dictionary: examples, with: &loader) + requestBodies = try await externallyDereference(dictionary: requestBodies, with: &loader) + headers = try await externallyDereference(dictionary: headers, with: &loader) + securitySchemes = try await externallyDereference(dictionary: securitySchemes, with: &loader) var newCallbacks = OpenAPI.ComponentDictionary() for (key, value) in callbacks { - newCallbacks[key] = try value.externallyDereferenced(with: &loader) + newCallbacks[key] = try await value.externallyDereferenced(with: &loader) } callbacks = newCallbacks diff --git a/Sources/OpenAPIKit/Document/Document.swift b/Sources/OpenAPIKit/Document/Document.swift index 7a0e91798..cabf8badb 100644 --- a/Sources/OpenAPIKit/Document/Document.swift +++ b/Sources/OpenAPIKit/Document/Document.swift @@ -351,11 +351,12 @@ extension OpenAPI.Document { return try DereferencedDocument(self) } - public mutating func externallyDereference(in context: Context) throws where Context: ExternalLoaderContext { - var loader: ExternalLoader = try components.externallyDereference(in: context) + public mutating func externallyDereference(in context: Context) async throws where Context: ExternalLoaderContext { + var loader: ExternalLoader = + try await components.externallyDereference(in: context) - paths = try paths.externallyDereferenced(with: &loader) - webhooks = try webhooks.externallyDereferenced(with: &loader) + paths = try await paths.externallyDereferenced(with: &loader) + webhooks = try await webhooks.externallyDereferenced(with: &loader) components = loader.components } diff --git a/Sources/OpenAPIKit/Either/Either+LocallyDereferenceable.swift b/Sources/OpenAPIKit/Either/Either+LocallyDereferenceable.swift index 4fe7b6c17..ab7658acd 100644 --- a/Sources/OpenAPIKit/Either/Either+LocallyDereferenceable.swift +++ b/Sources/OpenAPIKit/Either/Either+LocallyDereferenceable.swift @@ -22,12 +22,12 @@ extension Either: LocallyDereferenceable where A: LocallyDereferenceable, B: Loc } } - public func externallyDereferenced(with loader: inout ExternalLoader) throws -> Self where Context : ExternalLoaderContext { + public func externallyDereferenced(with loader: inout ExternalLoader) async throws -> Self where Context : ExternalLoaderContext { switch self { case .a(let a): - return .a(try a.externallyDereferenced(with: &loader)) + return .a(try await a.externallyDereferenced(with: &loader)) case .b(let b): - return .b(try b.externallyDereferenced(with: &loader)) + return .b(try await b.externallyDereferenced(with: &loader)) } } } diff --git a/Sources/OpenAPIKit/ExternalLoader.swift b/Sources/OpenAPIKit/ExternalLoader.swift index 96f57d2f2..3d0fef9bf 100644 --- a/Sources/OpenAPIKit/ExternalLoader.swift +++ b/Sources/OpenAPIKit/ExternalLoader.swift @@ -16,7 +16,7 @@ public protocol ExternalLoaderContext { /// the only real responsibility of a `load` function is to locate and load the given /// `URL` and pass its `Data` or `String` (depending on the decoder) to an appropriate /// `Decoder` for the given file type. - static func load(_: URL) throws -> T where T: Decodable + static func load(_: URL) async throws -> T where T: Decodable /// Determine the next Component Key (where to store something in the /// Components Object) for a new object of the given type that was loaded @@ -49,10 +49,10 @@ public struct ExternalLoader { internal var context: Context - internal mutating func store(type: T.Type, from url: URL) throws -> OpenAPI.Reference where T: ComponentDictionaryLocatable & Equatable & Decodable & LocallyDereferenceable { + internal mutating func store(type: T.Type, from url: URL) async throws -> OpenAPI.Reference where T: ComponentDictionaryLocatable & Equatable & Decodable & LocallyDereferenceable { let key = try context.nextComponentKey(type: type, at: url, given: components) - let value: T = try Context.load(url) - components[keyPath: T.openAPIComponentsKeyPath][key] = try value.externallyDereferenced(with: &self) + let value: T = try await Context.load(url) + components[keyPath: T.openAPIComponentsKeyPath][key] = try await value.externallyDereferenced(with: &self) return try components.reference(named: key.rawValue, ofType: T.self) } } diff --git a/Sources/OpenAPIKit/JSONReference.swift b/Sources/OpenAPIKit/JSONReference.swift index 806eedfd5..96a21ab23 100644 --- a/Sources/OpenAPIKit/JSONReference.swift +++ b/Sources/OpenAPIKit/JSONReference.swift @@ -536,17 +536,17 @@ extension JSONReference: LocallyDereferenceable where ReferenceType: LocallyDere ._dereferenced(in: components, following: newReferences, dereferencedFromComponentNamed: self.name) } - public func externallyDereferenced(with loader: inout ExternalLoader) throws -> Self where Context : ExternalLoaderContext { + public func externallyDereferenced(with loader: inout ExternalLoader) async throws -> Self where Context : ExternalLoaderContext { switch self { case .internal(let ref): - let value = try loader.components.lookup(self) + let value = try await loader.components.lookup(self) .externallyDereferenced(with: &loader) let key = try OpenAPI.ComponentKey.forceInit(rawValue: ref.name) loader.components[keyPath: ReferenceType.openAPIComponentsKeyPath][key] = value return self case .external(let url): - return try loader.store(type: ReferenceType.self, from: url).jsonReference + return try await loader.store(type: ReferenceType.self, from: url).jsonReference } } } @@ -577,8 +577,8 @@ extension OpenAPI.Reference: LocallyDereferenceable where ReferenceType: Locally ._dereferenced(in: components, following: newReferences, dereferencedFromComponentNamed: self.name) } - public func externallyDereferenced(with loader: inout ExternalLoader) throws -> Self where Context : ExternalLoaderContext { - return .init(try jsonReference.externallyDereferenced(with: &loader)) + public func externallyDereferenced(with loader: inout ExternalLoader) async throws -> Self where Context : ExternalLoaderContext { + return .init(try await jsonReference.externallyDereferenced(with: &loader)) } } diff --git a/Sources/OpenAPIKit/Link.swift b/Sources/OpenAPIKit/Link.swift index 903a09bba..fa30ee5e6 100644 --- a/Sources/OpenAPIKit/Link.swift +++ b/Sources/OpenAPIKit/Link.swift @@ -290,7 +290,7 @@ extension OpenAPI.Link: LocallyDereferenceable { public func externallyDereferenced( with loader: inout ExternalLoader - ) throws -> Self where Context : ExternalLoaderContext { + ) async throws -> Self where Context : ExternalLoaderContext { // TODO: externally dereference security, responses, requestBody, and parameters #warning("externally dereference security, responses, requestBody, and parameters") return self diff --git a/Sources/OpenAPIKit/Operation/DereferencedOperation.swift b/Sources/OpenAPIKit/Operation/DereferencedOperation.swift index 6f457e48e..f1f605a55 100644 --- a/Sources/OpenAPIKit/Operation/DereferencedOperation.swift +++ b/Sources/OpenAPIKit/Operation/DereferencedOperation.swift @@ -124,7 +124,7 @@ extension OpenAPI.Operation: LocallyDereferenceable { return try DereferencedOperation(self, resolvingIn: components, following: references) } - public func externallyDereferenced(with loader: inout ExternalLoader) throws -> OpenAPI.Operation where Context : ExternalLoaderContext { + public func externallyDereferenced(with loader: inout ExternalLoader) async throws -> OpenAPI.Operation where Context : ExternalLoaderContext { // TODO: externally dereference security, responses, requestBody, and parameters #warning("externally dereference security, responses, requestBody, and parameters") return self diff --git a/Sources/OpenAPIKit/OrderedDictionary+ExternallyDereference.swift b/Sources/OpenAPIKit/OrderedDictionary+ExternallyDereference.swift index 46bbd8ba4..172eeacf8 100644 --- a/Sources/OpenAPIKit/OrderedDictionary+ExternallyDereference.swift +++ b/Sources/OpenAPIKit/OrderedDictionary+ExternallyDereference.swift @@ -8,10 +8,10 @@ import OpenAPIKitCore extension OrderedDictionary where Value: LocallyDereferenceable { - public func externallyDereferenced(with loader: inout ExternalLoader) throws -> Self where Context: ExternalLoaderContext { + public func externallyDereferenced(with loader: inout ExternalLoader) async throws -> Self where Context: ExternalLoaderContext { var newDict = Self() for (key, value) in self { - let newRef = try value.externallyDereferenced(with: &loader) + let newRef = try await value.externallyDereferenced(with: &loader) newDict[key] = newRef } return newDict diff --git a/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift b/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift index e111d6518..b0ace8a29 100644 --- a/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift +++ b/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift @@ -82,7 +82,7 @@ extension OpenAPI.Parameter: LocallyDereferenceable { return try DereferencedParameter(self, resolvingIn: components, following: references, dereferencedFromComponentNamed: name) } - public func externallyDereferenced(with loader: inout ExternalLoader) throws -> Self { + public func externallyDereferenced(with loader: inout ExternalLoader) async throws -> Self { var parameter = self // TODO: externallyDerefence the schemaOrContent diff --git a/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift b/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift index 499b53185..1d98f46fa 100644 --- a/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift +++ b/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift @@ -69,7 +69,7 @@ extension OpenAPI.Parameter.SchemaContext: LocallyDereferenceable { return try DereferencedSchemaContext(self, resolvingIn: components, following: references) } - public func externallyDereferenced(with loader: inout ExternalLoader) throws -> OpenAPI.Parameter.SchemaContext where Context : ExternalLoaderContext { + public func externallyDereferenced(with loader: inout ExternalLoader) async throws -> OpenAPI.Parameter.SchemaContext where Context : ExternalLoaderContext { // TODO: externally dereference schema, examples, and example #warning("externally dereference schema, examples, and example") return self diff --git a/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift b/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift index 3da4d3bd3..d146ab419 100644 --- a/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift +++ b/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift @@ -138,13 +138,13 @@ extension OpenAPI.PathItem: LocallyDereferenceable { return try DereferencedPathItem(self, resolvingIn: components, following: references, dereferencedFromComponentNamed: name) } - public func externallyDereferenced(with loader: inout ExternalLoader) throws -> Self { + public func externallyDereferenced(with loader: inout ExternalLoader) async throws -> Self { var pathItem = self var newParameters = OpenAPI.Parameter.Array() for parameterRef in pathItem.parameters { newParameters.append( - try parameterRef.externallyDereferenced(with: &loader) + try await parameterRef.externallyDereferenced(with: &loader) ) } diff --git a/Sources/OpenAPIKit/Request/DereferencedRequest.swift b/Sources/OpenAPIKit/Request/DereferencedRequest.swift index 45a8174eb..6ce54c66b 100644 --- a/Sources/OpenAPIKit/Request/DereferencedRequest.swift +++ b/Sources/OpenAPIKit/Request/DereferencedRequest.swift @@ -61,7 +61,7 @@ extension OpenAPI.Request: LocallyDereferenceable { return try DereferencedRequest(self, resolvingIn: components, following: references, dereferencedFromComponentNamed: name) } - public func externallyDereferenced(with loader: inout ExternalLoader) throws -> OpenAPI.Request where Context : ExternalLoaderContext { + public func externallyDereferenced(with loader: inout ExternalLoader) async throws -> OpenAPI.Request where Context : ExternalLoaderContext { // TODO: externally dereference the content #warning("externally dereference the content") return self From 4a479e70c6b6af5cd2ab0cd8e9e64febf5b36dcf Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sat, 2 Mar 2024 17:38:53 -0600 Subject: [PATCH 014/110] update two tests that use the now-async external dereferencing --- Tests/OpenAPIKitTests/Document/DocumentTests.swift | 10 +++++----- Tests/OpenAPIKitTests/Path Item/PathItemTests.swift | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Tests/OpenAPIKitTests/Document/DocumentTests.swift b/Tests/OpenAPIKitTests/Document/DocumentTests.swift index 2a8236c34..92873e2ec 100644 --- a/Tests/OpenAPIKitTests/Document/DocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/DocumentTests.swift @@ -1175,14 +1175,14 @@ extension DocumentTests { // MARK: - External Dereferencing extension DocumentTests { // temporarily test with an example of the new interface - func test_example() throws { + func test_example() async throws { /// An example of implementing a loader context for loading external references /// into an OpenAPI document. struct ExampleLoaderContext: ExternalLoaderContext { - static func load(_ url: URL) throws -> T where T : Decodable { + static func load(_ url: URL) async throws -> T where T : Decodable { // load data from file, perhaps. we will just mock that up for the example: - let data = mockParameterData(url) + let data = await mockParameterData(url) let decoded = try JSONDecoder().decode(T.self, from: data) let finished: T @@ -1203,7 +1203,7 @@ extension DocumentTests { } /// Mock up some data, just for the example. - static func mockParameterData(_ url: URL) -> Data { + static func mockParameterData(_ url: URL) async -> Data { return """ { "name": "name", @@ -1259,7 +1259,7 @@ extension DocumentTests { */ let context = ExampleLoaderContext() - try document.externallyDereference(in: context) + try await document.externallyDereference(in: context) // - MARK: After print( diff --git a/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift b/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift index 0a39a6b13..7918972c4 100644 --- a/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift +++ b/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift @@ -451,7 +451,7 @@ extension PathItemTests { } } - func test_tmp() throws { + func test_tmp() async throws { let components = OpenAPI.Components( parameters: [ "already-internal": @@ -482,7 +482,7 @@ extension PathItemTests { print("------") let context = MockLoad() var loader = ExternalLoader(components: components, context: context) - let x = try pathItem.externallyDereferenced(with: &loader) + let x = try await pathItem.externallyDereferenced(with: &loader) print(x.parameters.debugDescription) print("=======") print(loader.components.parameters) From 0f426e86b4783e362b32f548918427926524896f Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Wed, 27 Mar 2024 09:56:10 -0500 Subject: [PATCH 015/110] CoreContext vendor extensions are writable, so just pass that along for setter --- Sources/OpenAPIKit/Schema Object/JSONSchema.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/OpenAPIKit/Schema Object/JSONSchema.swift b/Sources/OpenAPIKit/Schema Object/JSONSchema.swift index ae8cf16a6..8d74d8d40 100644 --- a/Sources/OpenAPIKit/Schema Object/JSONSchema.swift +++ b/Sources/OpenAPIKit/Schema Object/JSONSchema.swift @@ -431,7 +431,7 @@ extension JSONSchema: VendorExtendable { coreContext.vendorExtensions } set { - #warning("implement me") + coreContext.vendorExtensions } } From d283a87137ae9be8de0e8ac5fbe7271ccb6c8b8d Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Wed, 27 Mar 2024 10:02:42 -0500 Subject: [PATCH 016/110] simplify code by using OrderedDictionary helper --- Sources/OpenAPIKit/Components Object/Components.swift | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Sources/OpenAPIKit/Components Object/Components.swift b/Sources/OpenAPIKit/Components Object/Components.swift index a8bc3e1a4..32df7b357 100644 --- a/Sources/OpenAPIKit/Components Object/Components.swift +++ b/Sources/OpenAPIKit/Components Object/Components.swift @@ -288,11 +288,7 @@ extension OpenAPI.Components { headers = try await externallyDereference(dictionary: headers, with: &loader) securitySchemes = try await externallyDereference(dictionary: securitySchemes, with: &loader) - var newCallbacks = OpenAPI.ComponentDictionary() - for (key, value) in callbacks { - newCallbacks[key] = try await value.externallyDereferenced(with: &loader) - } - callbacks = newCallbacks + callbacks = try await callbacks.externallyDereferenced(with: &loader) return loader } From ce2a53a465cf83e6b009bd3b067f22adc76d1c01 Mon Sep 17 00:00:00 2001 From: Brandon Bloom Date: Sun, 31 Mar 2024 12:28:21 -0500 Subject: [PATCH 017/110] Raise minimum Yams version. Fixes #364 --- Package.resolved | 4 ++-- Package.swift | 2 +- .../Schema Object/SchemaObjectYamsTests.swift | 14 ++++++++++++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/Package.resolved b/Package.resolved index ac2140923..8ea187bda 100644 --- a/Package.resolved +++ b/Package.resolved @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/jpsim/Yams.git", "state" : { - "revision" : "9ff1cc9327586db4e0c8f46f064b6a82ec1566fa", - "version" : "4.0.6" + "revision" : "8a835d918245ca22f36663dd3862138805d7f707", + "version" : "5.1.0" } } ], diff --git a/Package.swift b/Package.swift index 8402adb61..03e1cfd05 100644 --- a/Package.swift +++ b/Package.swift @@ -21,7 +21,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"), - .package(url: "https://github.com/jpsim/Yams.git", "4.0.0"..<"6.0.0") // just for tests + .package(url: "https://github.com/jpsim/Yams.git", "5.1.0"..<"6.0.0") // just for tests ], targets: [ .target( diff --git a/Tests/OpenAPIKitTests/Schema Object/SchemaObjectYamsTests.swift b/Tests/OpenAPIKitTests/Schema Object/SchemaObjectYamsTests.swift index 12c7c10cd..1d32f5d72 100644 --- a/Tests/OpenAPIKitTests/Schema Object/SchemaObjectYamsTests.swift +++ b/Tests/OpenAPIKitTests/Schema Object/SchemaObjectYamsTests.swift @@ -16,6 +16,20 @@ import OpenAPIKit import Yams final class SchemaObjectYamsTests: XCTestCase { + func test_nullTypeDecode() throws { + let nullString = + """ + type: 'null' + """ + + let null = try YAMLDecoder().decode(JSONSchema.self, from: nullString) + + XCTAssertEqual( + null, + JSONSchema.null() + ) + } + func test_floatingPointWholeNumberIntegerDecode() throws { let integerString = """ From 79a505cfdedd5ced651e37aa777f0acf4204337a Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 2 Apr 2024 09:40:49 -0500 Subject: [PATCH 018/110] trying another design to allow for more concurrent file loading --- .../Components+Locatable.swift | 4 --- .../Either+LocallyDereferenceable.swift | 9 ------ Sources/OpenAPIKit/ExternalLoader.swift | 30 ++---------------- ...eredDictionary+ExternallyDereference.swift | 19 ------------ ...Dictionary+ExternallyDereferenceable.swift | 31 +++++++++++++++++++ .../Response/DereferencedResponse.swift | 12 ++++--- .../Components Object/Components.swift | 21 +++++++++++++ .../OrderedDictionary/OrderedDictionary.swift | 13 +++++++- 8 files changed, 75 insertions(+), 64 deletions(-) delete mode 100644 Sources/OpenAPIKit/OrderedDictionary+ExternallyDereference.swift create mode 100644 Sources/OpenAPIKit/OrderedDictionary+ExternallyDereferenceable.swift diff --git a/Sources/OpenAPIKit/Components Object/Components+Locatable.swift b/Sources/OpenAPIKit/Components Object/Components+Locatable.swift index b323db8ef..c1a56f0f0 100644 --- a/Sources/OpenAPIKit/Components Object/Components+Locatable.swift +++ b/Sources/OpenAPIKit/Components Object/Components+Locatable.swift @@ -98,10 +98,6 @@ public protocol LocallyDereferenceable { following references: Set, dereferencedFromComponentNamed name: String? ) throws -> DereferencedSelf - - func externallyDereferenced( - with loader: inout ExternalLoader - ) async throws -> Self } extension LocallyDereferenceable { diff --git a/Sources/OpenAPIKit/Either/Either+LocallyDereferenceable.swift b/Sources/OpenAPIKit/Either/Either+LocallyDereferenceable.swift index ab7658acd..75b0d0669 100644 --- a/Sources/OpenAPIKit/Either/Either+LocallyDereferenceable.swift +++ b/Sources/OpenAPIKit/Either/Either+LocallyDereferenceable.swift @@ -21,13 +21,4 @@ extension Either: LocallyDereferenceable where A: LocallyDereferenceable, B: Loc return try value._dereferenced(in: components, following: references, dereferencedFromComponentNamed: nil) } } - - public func externallyDereferenced(with loader: inout ExternalLoader) async throws -> Self where Context : ExternalLoaderContext { - switch self { - case .a(let a): - return .a(try await a.externallyDereferenced(with: &loader)) - case .b(let b): - return .b(try await b.externallyDereferenced(with: &loader)) - } - } } diff --git a/Sources/OpenAPIKit/ExternalLoader.swift b/Sources/OpenAPIKit/ExternalLoader.swift index 3d0fef9bf..58723d870 100644 --- a/Sources/OpenAPIKit/ExternalLoader.swift +++ b/Sources/OpenAPIKit/ExternalLoader.swift @@ -26,33 +26,9 @@ public protocol ExternalLoaderContext { /// but the same key for all equal objects. In practice, this probably means that any /// time the same type and URL pair are passed in the same `ComponentKey` should be /// returned. - mutating func nextComponentKey(type: T.Type, at: URL, given components: OpenAPI.Components) throws -> OpenAPI.ComponentKey + static func componentKey(type: T.Type, at: URL) throws -> OpenAPI.ComponentKey } -public struct ExternalLoader { - public init(components: OpenAPI.Components, context: Context) { - self.components = components - self.context = context - } - - /// External references are loaded into this Components Object. This allows for - /// loading external references into a single Document but also retaining the - /// identity of those refernces; that is, if three parts of a Document refer to - /// the same external reference, the external object will be loaded into this - /// Components Object and the three locations will still refer to the same - /// object (these are now internal references). - /// - /// In the most common use-cases, the starting place for this `components` property - /// should be the existing `Components` for some OpenAPI `Document`. This allows local - /// references to be followed while external references are loaded. - public internal(set) var components: OpenAPI.Components - - internal var context: Context - - internal mutating func store(type: T.Type, from url: URL) async throws -> OpenAPI.Reference where T: ComponentDictionaryLocatable & Equatable & Decodable & LocallyDereferenceable { - let key = try context.nextComponentKey(type: type, at: url, given: components) - let value: T = try await Context.load(url) - components[keyPath: T.openAPIComponentsKeyPath][key] = try await value.externallyDereferenced(with: &self) - return try components.reference(named: key.rawValue, ofType: T.self) - } +public protocol ExternallyDereferenceable { + func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) } diff --git a/Sources/OpenAPIKit/OrderedDictionary+ExternallyDereference.swift b/Sources/OpenAPIKit/OrderedDictionary+ExternallyDereference.swift deleted file mode 100644 index 172eeacf8..000000000 --- a/Sources/OpenAPIKit/OrderedDictionary+ExternallyDereference.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// OrderedDictionary+ExternallyDereference.swift -// OpenAPI -// -// Created by Mathew Polzin on 08/05/2023. -// - -import OpenAPIKitCore - -extension OrderedDictionary where Value: LocallyDereferenceable { - public func externallyDereferenced(with loader: inout ExternalLoader) async throws -> Self where Context: ExternalLoaderContext { - var newDict = Self() - for (key, value) in self { - let newRef = try await value.externallyDereferenced(with: &loader) - newDict[key] = newRef - } - return newDict - } -} diff --git a/Sources/OpenAPIKit/OrderedDictionary+ExternallyDereferenceable.swift b/Sources/OpenAPIKit/OrderedDictionary+ExternallyDereferenceable.swift new file mode 100644 index 000000000..94718b0a6 --- /dev/null +++ b/Sources/OpenAPIKit/OrderedDictionary+ExternallyDereferenceable.swift @@ -0,0 +1,31 @@ +// +// OrderedDictionary+ExternallyDereferenceable.swift +// OpenAPI +// +// Created by Mathew Polzin on 08/05/2023. +// + +import OpenAPIKitCore + +extension OrderedDictionary where Value: ExternallyDereferenceable { + + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + try await withThrowingTaskGroup(of: (Key, Value, OpenAPI.Components).self) { group in + for (key, value) in self { + group.addTask { + let (newRef, components) = try await value.externallyDereferenced(with: loader) + return (key, newRef, components) + } + } + + var newDict = Self() + var newComponents = OpenAPI.Components() + + for try await (key, newRef, components) in group { + newDict[key] = newRef + try newComponents.merge(components) + } + return (newDict, newComponents) + } + } +} diff --git a/Sources/OpenAPIKit/Response/DereferencedResponse.swift b/Sources/OpenAPIKit/Response/DereferencedResponse.swift index a3d3886c5..10c02f05a 100644 --- a/Sources/OpenAPIKit/Response/DereferencedResponse.swift +++ b/Sources/OpenAPIKit/Response/DereferencedResponse.swift @@ -75,10 +75,14 @@ extension OpenAPI.Response: LocallyDereferenceable { ) throws -> DereferencedResponse { return try DereferencedResponse(self, resolvingIn: components, following: references, dereferencedFromComponentNamed: name) } +} - public func externallyDereferenced(with loader: inout ExternalLoader) throws -> OpenAPI.Response where Context : ExternalLoaderContext { - // TODO: externally dereference the headers and content -#warning("externally dereference the headers and content") - return self +extension OpenAPI.Response: ExternallyDereferenceable { + public func externallyDereferenced(with loader: inout ExternalLoader) async throws -> OpenAPI.Response where Context : ExternalLoaderContext { + var response = self + response.headers = try await headers?.externallyDereferenced(with: &loader) + response.content = try await content.externallyDereferenced(with: &loader) + response.links = try await links.externallyDereferenced(with: &loader) + return response } } diff --git a/Sources/OpenAPIKit30/Components Object/Components.swift b/Sources/OpenAPIKit30/Components Object/Components.swift index ca03ff146..93bcad2f5 100644 --- a/Sources/OpenAPIKit30/Components Object/Components.swift +++ b/Sources/OpenAPIKit30/Components Object/Components.swift @@ -260,4 +260,25 @@ extension OpenAPI.Components { } } +public extension OpenAPI.Components { + struct ValueCollision: Swift.Error { + let value1: T + let value2: T + } + + mutating func merge(_ components: OpenAPI.Components) throws { + try schemas.merge(components.schemas, uniquingKeysWith: { (v1, v2) in throw OpenAPI.Components.ValueCollision(value1: v1, value2: v2) }) + try responses.merge(components.responses, uniquingKeysWith: { (v1, v2) in throw OpenAPI.Components.ValueCollision(value1: v1, value2: v2) }) + try parameters.merge(components.parameters, uniquingKeysWith: { (v1, v2) in throw OpenAPI.Components.ValueCollision(value1: v1, value2: v2) }) + try examples.merge(components.examples, uniquingKeysWith: { (v1, v2) in throw OpenAPI.Components.ValueCollision(value1: v1, value2: v2) }) + try requestBodies.merge(components.requestBodies, uniquingKeysWith: { (v1, v2) in throw OpenAPI.Components.ValueCollision(value1: v1, value2: v2) }) + try headers.merge(components.headers, uniquingKeysWith: { (v1, v2) in throw OpenAPI.Components.ValueCollision(value1: v1, value2: v2) }) + try securitySchemes.merge(components.securitySchemes, uniquingKeysWith: { (v1, v2) in throw OpenAPI.Components.ValueCollision(value1: v1, value2: v2) }) + try links.merge(components.links, uniquingKeysWith: { (v1, v2) in throw OpenAPI.Components.ValueCollision(value1: v1, value2: v2) }) + try callbacks.merge(components.callbacks, uniquingKeysWith: { (v1, v2) in throw OpenAPI.Components.ValueCollision(value1: v1, value2: v2) }) + try pathItems.merge(components.pathItems, uniquingKeysWith: { (v1, v2) in throw OpenAPI.Components.ValueCollision(value1: v1, value2: v2) }) + try vendorExtensions.merge(components.vendorExtensions, uniquingKeysWith: { (v1, v2) in throw OpenAPI.Components.ValueCollision(value1: v1, value2: v2) }) + } +} + extension OpenAPI.Components: Validatable {} diff --git a/Sources/OpenAPIKitCore/OrderedDictionary/OrderedDictionary.swift b/Sources/OpenAPIKitCore/OrderedDictionary/OrderedDictionary.swift index 8779f30ec..8b52db0c5 100644 --- a/Sources/OpenAPIKitCore/OrderedDictionary/OrderedDictionary.swift +++ b/Sources/OpenAPIKitCore/OrderedDictionary/OrderedDictionary.swift @@ -205,6 +205,18 @@ extension OrderedDictionary: Collection { } } +extension OrderedDictionary { + public mutating func merge(_ other: OrderedDictionary, uniquingKeysWith resolve: (Value, Value) throws -> Value) rethrows { + for (key, value) in other { + if let conflict = self[key] { + self[key] = try resolve(conflict, value) + } else { + self[key] = value + } + } + } +} + // MARK: - Iterator extension OrderedDictionary { public struct Iterator: Sequence, IteratorProtocol { @@ -239,7 +251,6 @@ extension OrderedDictionary: Equatable where Value: Equatable { } // MARK: - Codable - public struct AnyCodingKey: CodingKey { public let stringValue: String From 97c7afbddc938bdbb6f50e955127f850de32886d Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sat, 6 Apr 2024 15:27:38 -0500 Subject: [PATCH 019/110] external dereference conformance for either --- .../Either+ExternallyDereferenceable.swift | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Sources/OpenAPIKit/Either/Either+ExternallyDereferenceable.swift diff --git a/Sources/OpenAPIKit/Either/Either+ExternallyDereferenceable.swift b/Sources/OpenAPIKit/Either/Either+ExternallyDereferenceable.swift new file mode 100644 index 000000000..feee7ebb6 --- /dev/null +++ b/Sources/OpenAPIKit/Either/Either+ExternallyDereferenceable.swift @@ -0,0 +1,20 @@ +// +// Either+ExternallyDereferenceable.swift +// +// +// Created by Mathew Polzin on 2/28/21. +// + +import OpenAPIKitCore + +// MARK: - ExternallyDereferenceable +extension Either: ExternallyDereferenceable where B: ExternallyDereferenceable { + public func externallyDereferenced(with loader: inout ExternalLoader) async throws -> Self where Context : ExternalLoaderContext { + switch self { + case .a(let a): + return .a(a) + case .b(let b): + return .b(try await b.externallyDereferenced(with: &loader)) + } + } +} From 1b77de4628c41b24ebacc7a66f55b24bb226d411 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sat, 6 Apr 2024 17:20:50 -0500 Subject: [PATCH 020/110] Almost entirely switched to new external dereferencing framework --- Sources/OpenAPIKit/Callbacks.swift | 9 +- .../Components Object/Components.swift | 82 ++++++++++++++----- .../Content/DereferencedContent.swift | 8 +- .../Content/DereferencedContentEncoding.swift | 9 +- Sources/OpenAPIKit/Document/Document.swift | 17 ++-- .../Either+ExternallyDereferenceable.swift | 11 ++- Sources/OpenAPIKit/Example.swift | 6 +- .../Header/DereferencedHeader.swift | 6 +- Sources/OpenAPIKit/JSONReference.swift | 24 +++--- Sources/OpenAPIKit/Link.swift | 8 +- .../Operation/DereferencedOperation.swift | 6 +- .../Parameter/DereferencedParameter.swift | 8 +- .../Parameter/DereferencedSchemaContext.swift | 6 +- .../Path Item/DereferencedPathItem.swift | 15 ++-- .../Request/DereferencedRequest.swift | 6 +- .../Response/DereferencedResponse.swift | 25 ++++-- .../DereferencedJSONSchema.swift | 6 +- .../OpenAPIKit/Security/SecurityScheme.swift | 6 +- 18 files changed, 169 insertions(+), 89 deletions(-) diff --git a/Sources/OpenAPIKit/Callbacks.swift b/Sources/OpenAPIKit/Callbacks.swift index 1347da656..2f81e4a17 100644 --- a/Sources/OpenAPIKit/Callbacks.swift +++ b/Sources/OpenAPIKit/Callbacks.swift @@ -35,13 +35,12 @@ extension OpenAPI.CallbackURL: LocallyDereferenceable { ) throws -> OpenAPI.CallbackURL { self } +} - public func externallyDereferenced( - with loader: inout ExternalLoader - ) async throws -> Self where Context : ExternalLoaderContext { +extension OpenAPI.CallbackURL: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { // TODO: externally dereference security, responses, requestBody, and parameters #warning("externally dereference security, responses, requestBody, and parameters") - return self + return (self, .init()) } } - diff --git a/Sources/OpenAPIKit/Components Object/Components.swift b/Sources/OpenAPIKit/Components Object/Components.swift index 32df7b357..d08b9e672 100644 --- a/Sources/OpenAPIKit/Components Object/Components.swift +++ b/Sources/OpenAPIKit/Components Object/Components.swift @@ -71,6 +71,28 @@ extension OpenAPI { } } +extension OpenAPI.Components { + public struct ComponentCollision: Swift.Error { + public let componentType: String + public let existingComponent: String + public let newComponent: String + } + + public mutating func merge(_ other: OpenAPI.Components) throws { + try schemas.merge(other.schemas, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "schema", existingComponent: String(describing: a), newComponent: String(describing: b)) }) + try responses.merge(other.responses, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "responses", existingComponent: String(describing: a), newComponent: String(describing: b)) }) + try parameters.merge(other.parameters, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "parameters", existingComponent: String(describing: a), newComponent: String(describing: b)) }) + try examples.merge(other.examples, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "examples", existingComponent: String(describing: a), newComponent: String(describing: b)) }) + try requestBodies.merge(other.requestBodies, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "requestBodies", existingComponent: String(describing: a), newComponent: String(describing: b)) }) + try headers.merge(other.headers, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "headers", existingComponent: String(describing: a), newComponent: String(describing: b)) }) + try securitySchemes.merge(other.securitySchemes, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "securitySchemes", existingComponent: String(describing: a), newComponent: String(describing: b)) }) + try links.merge(other.links, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "links", existingComponent: String(describing: a), newComponent: String(describing: b)) }) + try callbacks.merge(other.callbacks, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "callbacks", existingComponent: String(describing: a), newComponent: String(describing: b)) }) + try pathItems.merge(other.pathItems, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "pathItems", existingComponent: String(describing: a), newComponent: String(describing: b)) }) + try vendorExtensions.merge(other.vendorExtensions, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "vendorExtensions", existingComponent: String(describing: a), newComponent: String(describing: b)) }) + } +} + extension OpenAPI.Components { /// The extension name used to store a Components Object name (the key something is stored under /// within the Components Object). This is used by OpenAPIKit to store the previous Component name @@ -269,28 +291,44 @@ extension OpenAPI.Components { } extension OpenAPI.Components { - private mutating func externallyDereference(dictionary: OpenAPI.ComponentDictionary, with loader: inout ExternalLoader) async throws -> OpenAPI.ComponentDictionary where Context: ExternalLoaderContext, T: LocallyDereferenceable { - var newValues = OpenAPI.ComponentDictionary() - for (key, value) in dictionary { - newValues[key] = try await value.externallyDereferenced(with: &loader) - } - return newValues - } - - internal mutating func externallyDereference(in context: Context) async throws -> ExternalLoader where Context: ExternalLoaderContext { - var loader = ExternalLoader(components: self, context: context) - - schemas = try await externallyDereference(dictionary: schemas, with: &loader) - responses = try await externallyDereference(dictionary: responses, with: &loader) - parameters = try await externallyDereference(dictionary: parameters, with: &loader) - examples = try await externallyDereference(dictionary: examples, with: &loader) - requestBodies = try await externallyDereference(dictionary: requestBodies, with: &loader) - headers = try await externallyDereference(dictionary: headers, with: &loader) - securitySchemes = try await externallyDereference(dictionary: securitySchemes, with: &loader) - - callbacks = try await callbacks.externallyDereferenced(with: &loader) - - return loader + internal mutating func externallyDereference(in context: Context.Type) async throws { + let oldSchemas = schemas + let oldResponses = responses + let oldParameters = parameters + let oldExamples = examples + let oldRequestBodies = requestBodies + let oldHeaders = headers + let oldSecuritySchemes = securitySchemes + + let oldCallbacks = callbacks + + async let (newSchemas, c1) = oldSchemas.externallyDereferenced(with: context) + async let (newResponses, c2) = oldResponses.externallyDereferenced(with: context) + async let (newParameters, c3) = oldParameters.externallyDereferenced(with: context) + async let (newExamples, c4) = oldExamples.externallyDereferenced(with: context) + async let (newRequestBodies, c5) = oldRequestBodies.externallyDereferenced(with: context) + async let (newHeaders, c6) = oldHeaders.externallyDereferenced(with: context) + async let (newSecuritySchemes, c7) = oldSecuritySchemes.externallyDereferenced(with: context) + + async let (newCallbacks, c8) = oldCallbacks.externallyDereferenced(with: context) + + schemas = try await newSchemas + responses = try await newResponses + parameters = try await newParameters + examples = try await newExamples + requestBodies = try await newRequestBodies + headers = try await newHeaders + securitySchemes = try await newSecuritySchemes + + callbacks = try await newCallbacks + + try merge(c1) + try merge(c2) + try merge(c3) + try merge(c4) + try merge(c5) + try merge(c6) + try merge(c7) } } diff --git a/Sources/OpenAPIKit/Content/DereferencedContent.swift b/Sources/OpenAPIKit/Content/DereferencedContent.swift index b60533ec0..27cfdc10e 100644 --- a/Sources/OpenAPIKit/Content/DereferencedContent.swift +++ b/Sources/OpenAPIKit/Content/DereferencedContent.swift @@ -74,12 +74,12 @@ extension OpenAPI.Content: LocallyDereferenceable { ) throws -> DereferencedContent { return try DereferencedContent(self, resolvingIn: components, following: references) } +} - public func externallyDereferenced(with loader: inout ExternalLoader) throws -> OpenAPI.Content where Context : ExternalLoaderContext { - var content = self - +extension OpenAPI.Content: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { // TOOD: need to locally dereference the schema, examples, and content encoding here. #warning("need to locally dereference the schema, examples, and content encoding here.") - return content + return (self, .init()) } } diff --git a/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift b/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift index 14fd45248..f68c51449 100644 --- a/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift +++ b/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift @@ -55,13 +55,12 @@ extension OpenAPI.Content.Encoding: LocallyDereferenceable { ) throws -> DereferencedContentEncoding { return try DereferencedContentEncoding(self, resolvingIn: components, following: references) } +} - public func externallyDereferenced(with loader: inout ExternalLoader) throws -> OpenAPI.Content.Encoding where Context : ExternalLoaderContext { - var contentEncoding = self - +extension OpenAPI.Content.Encoding: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { // TODO: need to externally dereference the headers here. #warning("need to externally dereference the headers here.") - - return self + return (self, .init()) } } diff --git a/Sources/OpenAPIKit/Document/Document.swift b/Sources/OpenAPIKit/Document/Document.swift index cabf8badb..5f5cf19c8 100644 --- a/Sources/OpenAPIKit/Document/Document.swift +++ b/Sources/OpenAPIKit/Document/Document.swift @@ -351,14 +351,19 @@ extension OpenAPI.Document { return try DereferencedDocument(self) } - public mutating func externallyDereference(in context: Context) async throws where Context: ExternalLoaderContext { - var loader: ExternalLoader = - try await components.externallyDereference(in: context) + public mutating func externallyDereference(in context: Context.Type) async throws { + let oldPaths = paths + let oldWebhooks = webhooks - paths = try await paths.externallyDereferenced(with: &loader) - webhooks = try await webhooks.externallyDereferenced(with: &loader) + try await components.externallyDereference(in: context) - components = loader.components + async let (newPaths, c1) = oldPaths.externallyDereferenced(with: context) + async let (newWebhooks, c2) = oldWebhooks.externallyDereferenced(with: context) + + paths = try await newPaths + webhooks = try await newWebhooks + try await components.merge(c1) + try await components.merge(c2) } } diff --git a/Sources/OpenAPIKit/Either/Either+ExternallyDereferenceable.swift b/Sources/OpenAPIKit/Either/Either+ExternallyDereferenceable.swift index feee7ebb6..10e56b368 100644 --- a/Sources/OpenAPIKit/Either/Either+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit/Either/Either+ExternallyDereferenceable.swift @@ -8,13 +8,16 @@ import OpenAPIKitCore // MARK: - ExternallyDereferenceable -extension Either: ExternallyDereferenceable where B: ExternallyDereferenceable { - public func externallyDereferenced(with loader: inout ExternalLoader) async throws -> Self where Context : ExternalLoaderContext { +extension Either: ExternallyDereferenceable where A: ExternallyDereferenceable, B: ExternallyDereferenceable { + + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { switch self { case .a(let a): - return .a(a) + let (newA, components) = try await a.externallyDereferenced(with: loader) + return (.a(newA), components) case .b(let b): - return .b(try await b.externallyDereferenced(with: &loader)) + let (newB, components) = try await b.externallyDereferenced(with: loader) + return (.b(newB), components) } } } diff --git a/Sources/OpenAPIKit/Example.swift b/Sources/OpenAPIKit/Example.swift index 4535010e3..8831c6d97 100644 --- a/Sources/OpenAPIKit/Example.swift +++ b/Sources/OpenAPIKit/Example.swift @@ -206,9 +206,11 @@ extension OpenAPI.Example: LocallyDereferenceable { vendorExtensions: vendorExtensions ) } +} - public func externallyDereferenced(with loader: inout ExternalLoader) throws -> OpenAPI.Example where Context : ExternalLoaderContext { - return self +extension OpenAPI.Example: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + return (self, .init()) } } diff --git a/Sources/OpenAPIKit/Header/DereferencedHeader.swift b/Sources/OpenAPIKit/Header/DereferencedHeader.swift index 8c8d2311f..d34a7374c 100644 --- a/Sources/OpenAPIKit/Header/DereferencedHeader.swift +++ b/Sources/OpenAPIKit/Header/DereferencedHeader.swift @@ -81,10 +81,12 @@ extension OpenAPI.Header: LocallyDereferenceable { ) throws -> DereferencedHeader { return try DereferencedHeader(self, resolvingIn: components, following: references, dereferencedFromComponentNamed: name) } +} - public func externallyDereferenced(with loader: inout ExternalLoader) throws -> OpenAPI.Header where Context : ExternalLoaderContext { +extension OpenAPI.Header: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { // TODO: externally dereference the schemaOrContent #warning("externally dereference the schemaOrContent") - return self + return (self, .init()) } } diff --git a/Sources/OpenAPIKit/JSONReference.swift b/Sources/OpenAPIKit/JSONReference.swift index 96a21ab23..d3f4fb72e 100644 --- a/Sources/OpenAPIKit/JSONReference.swift +++ b/Sources/OpenAPIKit/JSONReference.swift @@ -535,18 +535,19 @@ extension JSONReference: LocallyDereferenceable where ReferenceType: LocallyDere .lookup(self) ._dereferenced(in: components, following: newReferences, dereferencedFromComponentNamed: self.name) } +} - public func externallyDereferenced(with loader: inout ExternalLoader) async throws -> Self where Context : ExternalLoaderContext { +extension JSONReference: ExternallyDereferenceable where ReferenceType: ExternallyDereferenceable & Decodable & Equatable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { switch self { case .internal(let ref): - let value = try await loader.components.lookup(self) - .externallyDereferenced(with: &loader) - let key = try OpenAPI.ComponentKey.forceInit(rawValue: ref.name) - loader.components[keyPath: ReferenceType.openAPIComponentsKeyPath][key] = - value - return self + return (.internal(ref), .init()) case .external(let url): - return try await loader.store(type: ReferenceType.self, from: url).jsonReference + let componentKey = try loader.componentKey(type: ReferenceType.self, at: url) + let component: ReferenceType = try await loader.load(url) + var components = OpenAPI.Components() + components[keyPath: ReferenceType.openAPIComponentsKeyPath][componentKey] = component + return (try components.reference(named: componentKey.rawValue, ofType: ReferenceType.self).jsonReference, components) } } } @@ -576,9 +577,12 @@ extension OpenAPI.Reference: LocallyDereferenceable where ReferenceType: Locally .lookup(self) ._dereferenced(in: components, following: newReferences, dereferencedFromComponentNamed: self.name) } +} - public func externallyDereferenced(with loader: inout ExternalLoader) async throws -> Self where Context : ExternalLoaderContext { - return .init(try await jsonReference.externallyDereferenced(with: &loader)) +extension OpenAPI.Reference: ExternallyDereferenceable where ReferenceType: ExternallyDereferenceable & Decodable & Equatable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + let (newRef, components) = try await jsonReference.externallyDereferenced(with: loader) + return (.init(newRef), components) } } diff --git a/Sources/OpenAPIKit/Link.swift b/Sources/OpenAPIKit/Link.swift index fa30ee5e6..409b1ca4d 100644 --- a/Sources/OpenAPIKit/Link.swift +++ b/Sources/OpenAPIKit/Link.swift @@ -287,13 +287,13 @@ extension OpenAPI.Link: LocallyDereferenceable { vendorExtensions: vendorExtensions ) } +} - public func externallyDereferenced( - with loader: inout ExternalLoader - ) async throws -> Self where Context : ExternalLoaderContext { +extension OpenAPI.Link: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { // TODO: externally dereference security, responses, requestBody, and parameters #warning("externally dereference security, responses, requestBody, and parameters") - return self + return (self, .init()) } } diff --git a/Sources/OpenAPIKit/Operation/DereferencedOperation.swift b/Sources/OpenAPIKit/Operation/DereferencedOperation.swift index f1f605a55..e39a3f975 100644 --- a/Sources/OpenAPIKit/Operation/DereferencedOperation.swift +++ b/Sources/OpenAPIKit/Operation/DereferencedOperation.swift @@ -123,10 +123,12 @@ extension OpenAPI.Operation: LocallyDereferenceable { ) throws -> DereferencedOperation { return try DereferencedOperation(self, resolvingIn: components, following: references) } +} - public func externallyDereferenced(with loader: inout ExternalLoader) async throws -> OpenAPI.Operation where Context : ExternalLoaderContext { +extension OpenAPI.Operation: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { // TODO: externally dereference security, responses, requestBody, and parameters #warning("externally dereference security, responses, requestBody, and parameters") - return self + return (self, .init()) } } diff --git a/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift b/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift index b0ace8a29..5168ad22b 100644 --- a/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift +++ b/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift @@ -81,13 +81,13 @@ extension OpenAPI.Parameter: LocallyDereferenceable { ) throws -> DereferencedParameter { return try DereferencedParameter(self, resolvingIn: components, following: references, dereferencedFromComponentNamed: name) } +} - public func externallyDereferenced(with loader: inout ExternalLoader) async throws -> Self { - var parameter = self +extension OpenAPI.Parameter: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { // TODO: externallyDerefence the schemaOrContent #warning("need to externally dereference the schemaOrContent here") - - return parameter + return (self, .init()) } } diff --git a/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift b/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift index 1d98f46fa..81d01ef8f 100644 --- a/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift +++ b/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift @@ -68,10 +68,12 @@ extension OpenAPI.Parameter.SchemaContext: LocallyDereferenceable { ) throws -> DereferencedSchemaContext { return try DereferencedSchemaContext(self, resolvingIn: components, following: references) } +} - public func externallyDereferenced(with loader: inout ExternalLoader) async throws -> OpenAPI.Parameter.SchemaContext where Context : ExternalLoaderContext { +extension OpenAPI.Parameter.SchemaContext: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { // TODO: externally dereference schema, examples, and example #warning("externally dereference schema, examples, and example") - return self + return (self, .init()) } } diff --git a/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift b/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift index d146ab419..12608d57a 100644 --- a/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift +++ b/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift @@ -137,22 +137,25 @@ extension OpenAPI.PathItem: LocallyDereferenceable { ) throws -> DereferencedPathItem { return try DereferencedPathItem(self, resolvingIn: components, following: references, dereferencedFromComponentNamed: name) } +} - public func externallyDereferenced(with loader: inout ExternalLoader) async throws -> Self { +extension OpenAPI.PathItem: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { var pathItem = self + // TODO: Make the following into an async task list for parallel execution. var newParameters = OpenAPI.Parameter.Array() + var newComponents = OpenAPI.Components() for parameterRef in pathItem.parameters { - newParameters.append( - try await parameterRef.externallyDereferenced(with: &loader) - ) + let (newParameter, newComponent) = try await parameterRef.externallyDereferenced(with: loader) + newParameters.append(newParameter) + try newComponents.merge(newComponent) } pathItem.parameters = newParameters // TODO: load external references for entire PathItem object - return pathItem + return (pathItem, .init()) } } - diff --git a/Sources/OpenAPIKit/Request/DereferencedRequest.swift b/Sources/OpenAPIKit/Request/DereferencedRequest.swift index 6ce54c66b..97c947fde 100644 --- a/Sources/OpenAPIKit/Request/DereferencedRequest.swift +++ b/Sources/OpenAPIKit/Request/DereferencedRequest.swift @@ -60,10 +60,12 @@ extension OpenAPI.Request: LocallyDereferenceable { ) throws -> DereferencedRequest { return try DereferencedRequest(self, resolvingIn: components, following: references, dereferencedFromComponentNamed: name) } +} - public func externallyDereferenced(with loader: inout ExternalLoader) async throws -> OpenAPI.Request where Context : ExternalLoaderContext { +extension OpenAPI.Request: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { // TODO: externally dereference the content #warning("externally dereference the content") - return self + return (self, .init()) } } diff --git a/Sources/OpenAPIKit/Response/DereferencedResponse.swift b/Sources/OpenAPIKit/Response/DereferencedResponse.swift index 10c02f05a..3a8e7bdb6 100644 --- a/Sources/OpenAPIKit/Response/DereferencedResponse.swift +++ b/Sources/OpenAPIKit/Response/DereferencedResponse.swift @@ -78,11 +78,26 @@ extension OpenAPI.Response: LocallyDereferenceable { } extension OpenAPI.Response: ExternallyDereferenceable { - public func externallyDereferenced(with loader: inout ExternalLoader) async throws -> OpenAPI.Response where Context : ExternalLoaderContext { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + let oldContent = content + let oldLinks = links + + async let (newContent, c1) = oldContent.externallyDereferenced(with: loader) + async let (newLinks, c2) = oldLinks.externallyDereferenced(with: loader) + var response = self - response.headers = try await headers?.externallyDereferenced(with: &loader) - response.content = try await content.externallyDereferenced(with: &loader) - response.links = try await links.externallyDereferenced(with: &loader) - return response + response.content = try await newContent + response.links = try await newLinks + + var components = try await c1 + try await components.merge(c2) + + if let oldHeaders = headers { + let (newHeaders, c3) = try await oldHeaders.externallyDereferenced(with: loader) + response.headers = newHeaders + try components.merge(c3) + } + + return (response, components) } } diff --git a/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift b/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift index 523067dc0..8cac1722a 100644 --- a/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift +++ b/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift @@ -533,10 +533,12 @@ extension JSONSchema: LocallyDereferenceable { public func dereferenced() -> DereferencedJSONSchema? { return try? dereferenced(in: .noComponents) } +} - public func externallyDereferenced(with loader: inout ExternalLoader) throws -> Self where Context : ExternalLoaderContext { +extension JSONSchema: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { // TODO: externally dereference this schema #warning("need to externally dereference json schemas") - return self + return (self, .init()) } } diff --git a/Sources/OpenAPIKit/Security/SecurityScheme.swift b/Sources/OpenAPIKit/Security/SecurityScheme.swift index 0edcf03ee..2429466c1 100644 --- a/Sources/OpenAPIKit/Security/SecurityScheme.swift +++ b/Sources/OpenAPIKit/Security/SecurityScheme.swift @@ -272,8 +272,10 @@ extension OpenAPI.SecurityScheme: LocallyDereferenceable { } return ret } +} - public func externallyDereferenced(with loader: inout ExternalLoader) throws -> OpenAPI.SecurityScheme where Context : ExternalLoaderContext { - return self +extension OpenAPI.SecurityScheme: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + return (self, .init()) } } From 62758d39f4392654109f805872184599c341a774 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sat, 6 Apr 2024 18:21:46 -0500 Subject: [PATCH 021/110] Make path item parameter external dereferencing properly concurrent --- .../Path Item/DereferencedPathItem.swift | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift b/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift index 12608d57a..6cffe2f84 100644 --- a/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift +++ b/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift @@ -141,21 +141,29 @@ extension OpenAPI.PathItem: LocallyDereferenceable { extension OpenAPI.PathItem: ExternallyDereferenceable { public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { - var pathItem = self - - // TODO: Make the following into an async task list for parallel execution. - var newParameters = OpenAPI.Parameter.Array() - var newComponents = OpenAPI.Components() - for parameterRef in pathItem.parameters { - let (newParameter, newComponent) = try await parameterRef.externallyDereferenced(with: loader) - newParameters.append(newParameter) - try newComponents.merge(newComponent) + let (newParameters, newComponents) = try await withThrowingTaskGroup(of: (OpenAPI.Parameter.Array.Element, OpenAPI.Components).self) { group in + for elem in parameters { + group.addTask { + return try await elem.externallyDereferenced(with: loader) + } + } + + var newParameters = OpenAPI.Parameter.Array() + var newComponents = OpenAPI.Components() + + for try await (elem, components) in group { + newParameters.append(elem) + try newComponents.merge(components) + } + return (newParameters, newComponents) } + var pathItem = self pathItem.parameters = newParameters // TODO: load external references for entire PathItem object + // also merge components before returning newComponents! - return (pathItem, .init()) + return (pathItem, newComponents) } } From 8d90a6d6cfa28a1f5814cd48abf83814c576760d Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sat, 6 Apr 2024 18:22:07 -0500 Subject: [PATCH 022/110] make code compile. seems like a workaround for some compiler bug to me. --- .../Components Object/Components.swift | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/Sources/OpenAPIKit/Components Object/Components.swift b/Sources/OpenAPIKit/Components Object/Components.swift index d08b9e672..eec77534f 100644 --- a/Sources/OpenAPIKit/Components Object/Components.swift +++ b/Sources/OpenAPIKit/Components Object/Components.swift @@ -310,7 +310,14 @@ extension OpenAPI.Components { async let (newHeaders, c6) = oldHeaders.externallyDereferenced(with: context) async let (newSecuritySchemes, c7) = oldSecuritySchemes.externallyDereferenced(with: context) - async let (newCallbacks, c8) = oldCallbacks.externallyDereferenced(with: context) +// async let (newCallbacks, c8) = oldCallbacks.externallyDereferenced(with: context) + var c8 = OpenAPI.Components() + var newCallbacks = oldCallbacks + for (key, callback) in oldCallbacks { + let (newCallback, components) = try await callback.externallyDereferenced(with: context) + newCallbacks[key] = newCallback + try c8.merge(components) + } schemas = try await newSchemas responses = try await newResponses @@ -320,15 +327,17 @@ extension OpenAPI.Components { headers = try await newHeaders securitySchemes = try await newSecuritySchemes - callbacks = try await newCallbacks + callbacks = newCallbacks + + try await merge(c1) + try await merge(c2) + try await merge(c3) + try await merge(c4) + try await merge(c5) + try await merge(c6) + try await merge(c7) - try merge(c1) - try merge(c2) - try merge(c3) - try merge(c4) - try merge(c5) - try merge(c6) - try merge(c7) + try merge(c8) } } From 78638155208adc6cf34e3ac9624d9ef193dd2825 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sat, 6 Apr 2024 18:34:38 -0500 Subject: [PATCH 023/110] fix some tests --- Sources/OpenAPIKit/ExternalLoader.swift | 2 +- Tests/OpenAPIKitTests/Document/DocumentTests.swift | 5 ++--- .../OpenAPIKitTests/Path Item/PathItemTests.swift | 14 +++----------- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/Sources/OpenAPIKit/ExternalLoader.swift b/Sources/OpenAPIKit/ExternalLoader.swift index 58723d870..f937ac917 100644 --- a/Sources/OpenAPIKit/ExternalLoader.swift +++ b/Sources/OpenAPIKit/ExternalLoader.swift @@ -26,7 +26,7 @@ public protocol ExternalLoaderContext { /// but the same key for all equal objects. In practice, this probably means that any /// time the same type and URL pair are passed in the same `ComponentKey` should be /// returned. - static func componentKey(type: T.Type, at: URL) throws -> OpenAPI.ComponentKey + static func componentKey(type: T.Type, at url: URL) throws -> OpenAPI.ComponentKey } public protocol ExternallyDereferenceable { diff --git a/Tests/OpenAPIKitTests/Document/DocumentTests.swift b/Tests/OpenAPIKitTests/Document/DocumentTests.swift index 92873e2ec..1a5e0ae15 100644 --- a/Tests/OpenAPIKitTests/Document/DocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/DocumentTests.swift @@ -1195,7 +1195,7 @@ extension DocumentTests { return finished } - mutating func nextComponentKey(type: T.Type, at url: URL, given components: OpenAPIKit.OpenAPI.Components) throws -> OpenAPIKit.OpenAPI.ComponentKey { + static func componentKey(type: T.Type, at url: URL) throws -> OpenAPIKit.OpenAPI.ComponentKey { // do anything you want here to determine what key the new component should be stored at. // for the example, we will just transform the URL into a valid components key: let urlString = url.pathComponents.dropFirst().joined(separator: "_").replacingOccurrences(of: ".", with: "_") @@ -1258,8 +1258,7 @@ extension DocumentTests { } */ - let context = ExampleLoaderContext() - try await document.externallyDereference(in: context) + try await document.externallyDereference(in: ExampleLoaderContext.self) // - MARK: After print( diff --git a/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift b/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift index 7918972c4..2e55b6aec 100644 --- a/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift +++ b/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift @@ -436,7 +436,7 @@ extension PathItemTests { // MARK: External Dereferencing Tests extension PathItemTests { struct MockLoad: ExternalLoaderContext { - func nextComponentKey(type: T.Type, at url: URL, given components: OpenAPI.Components) -> OpenAPI.ComponentKey { + static func componentKey(type: T.Type, at url: URL) -> OpenAPI.ComponentKey { "hello-world" } @@ -452,12 +452,6 @@ extension PathItemTests { } func test_tmp() async throws { - let components = OpenAPI.Components( - parameters: [ - "already-internal": - .init(name: "internal-param", context: .query, schema: .string), - ] - ) let op = OpenAPI.Operation(responses: [:]) let pathItem = OpenAPI.PathItem( summary: "summary", @@ -480,11 +474,9 @@ extension PathItemTests { print(pathItem.parameters.debugDescription) print("------") - let context = MockLoad() - var loader = ExternalLoader(components: components, context: context) - let x = try await pathItem.externallyDereferenced(with: &loader) + let (x, loaderComponents) = try await pathItem.externallyDereferenced(with: MockLoad.self) print(x.parameters.debugDescription) print("=======") - print(loader.components.parameters) + print(loaderComponents.parameters) } } From d2aea20abdb5236e57ce0502899956481852f682 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sat, 6 Apr 2024 19:25:02 -0500 Subject: [PATCH 024/110] Add some external dereferencing convenience around array and optional --- .../Array+ExternallyDereferenceable.swift | 27 +++++++++++++++++++ .../Optional+ExternallyDereferenceable.swift | 13 +++++++++ ...Dictionary+ExternallyDereferenceable.swift | 0 3 files changed, 40 insertions(+) create mode 100644 Sources/OpenAPIKit/Utility/Array+ExternallyDereferenceable.swift create mode 100644 Sources/OpenAPIKit/Utility/Optional+ExternallyDereferenceable.swift rename Sources/OpenAPIKit/{ => Utility}/OrderedDictionary+ExternallyDereferenceable.swift (100%) diff --git a/Sources/OpenAPIKit/Utility/Array+ExternallyDereferenceable.swift b/Sources/OpenAPIKit/Utility/Array+ExternallyDereferenceable.swift new file mode 100644 index 000000000..59970df82 --- /dev/null +++ b/Sources/OpenAPIKit/Utility/Array+ExternallyDereferenceable.swift @@ -0,0 +1,27 @@ +// +// Array+ExternallyDereferenceable.swift +// + +import OpenAPIKitCore + +extension Array where Element: ExternallyDereferenceable { + + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + try await withThrowingTaskGroup(of: (Element, OpenAPI.Components).self) { group in + for elem in self { + group.addTask { + return try await elem.externallyDereferenced(with: loader) + } + } + + var newElems = Self() + var newComponents = OpenAPI.Components() + + for try await (elem, components) in group { + newElems.append(elem) + try newComponents.merge(components) + } + return (newElems, newComponents) + } + } +} diff --git a/Sources/OpenAPIKit/Utility/Optional+ExternallyDereferenceable.swift b/Sources/OpenAPIKit/Utility/Optional+ExternallyDereferenceable.swift new file mode 100644 index 000000000..671657f86 --- /dev/null +++ b/Sources/OpenAPIKit/Utility/Optional+ExternallyDereferenceable.swift @@ -0,0 +1,13 @@ +// +// Optional+ExternallyDereferenceable.swift +// + +import OpenAPIKitCore + +extension Optional where Wrapped: ExternallyDereferenceable { + + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + guard let wrapped = self else { return (nil, .init()) } + return try await wrapped.externallyDereferenced(with: loader) + } +} diff --git a/Sources/OpenAPIKit/OrderedDictionary+ExternallyDereferenceable.swift b/Sources/OpenAPIKit/Utility/OrderedDictionary+ExternallyDereferenceable.swift similarity index 100% rename from Sources/OpenAPIKit/OrderedDictionary+ExternallyDereferenceable.swift rename to Sources/OpenAPIKit/Utility/OrderedDictionary+ExternallyDereferenceable.swift From e1428901ede2bf58edc09d6a051e8815714f7e57 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sat, 6 Apr 2024 19:25:33 -0500 Subject: [PATCH 025/110] Make Server externally dereferenceable --- Sources/OpenAPIKit/Server.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Sources/OpenAPIKit/Server.swift b/Sources/OpenAPIKit/Server.swift index 2b7d17b2c..ad9bf6094 100644 --- a/Sources/OpenAPIKit/Server.swift +++ b/Sources/OpenAPIKit/Server.swift @@ -259,5 +259,11 @@ extension OpenAPI.Server.Variable { } } +extension OpenAPI.Server: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + return (self, .init()) + } +} + extension OpenAPI.Server: Validatable {} extension OpenAPI.Server.Variable: Validatable {} From 0a6500569e1ee7b58ba011f22cb4200dd07fa9f1 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sat, 6 Apr 2024 19:25:49 -0500 Subject: [PATCH 026/110] Finish PathItem external dereferencing implementation --- .../Path Item/DereferencedPathItem.swift | 99 +++++++++++++++---- 1 file changed, 80 insertions(+), 19 deletions(-) diff --git a/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift b/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift index 6cffe2f84..6f4727f45 100644 --- a/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift +++ b/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift @@ -141,28 +141,89 @@ extension OpenAPI.PathItem: LocallyDereferenceable { extension OpenAPI.PathItem: ExternallyDereferenceable { public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { - let (newParameters, newComponents) = try await withThrowingTaskGroup(of: (OpenAPI.Parameter.Array.Element, OpenAPI.Components).self) { group in - for elem in parameters { - group.addTask { - return try await elem.externallyDereferenced(with: loader) - } - } - - var newParameters = OpenAPI.Parameter.Array() - var newComponents = OpenAPI.Components() - - for try await (elem, components) in group { - newParameters.append(elem) - try newComponents.merge(components) - } - return (newParameters, newComponents) - } + let oldParameters = parameters + let oldServers = servers + let oldGet = get + let oldPut = put + let oldPost = post + let oldDelete = delete + let oldOptions = options + let oldHead = head + let oldPatch = patch + let oldTrace = trace + + async let (newParameters, c1) = oldParameters.externallyDereferenced(with: loader) +// async let (newServers, c2) = oldServers.externallyDereferenced(with: loader) +// async let (newGet, c3) = oldGet.externallyDereferenced(with: loader) +// async let (newPut, c4) = oldPut.externallyDereferenced(with: loader) +// async let (newPost, c5) = oldPost.externallyDereferenced(with: loader) +// async let (newDelete, c6) = oldDelete.externallyDereferenced(with: loader) +// async let (newOptions, c7) = oldOptions.externallyDereferenced(with: loader) +// async let (newHead, c8) = oldHead.externallyDereferenced(with: loader) +// async let (newPatch, c9) = oldPatch.externallyDereferenced(with: loader) +// async let (newTrace, c10) = oldTrace.externallyDereferenced(with: loader) var pathItem = self - pathItem.parameters = newParameters + var newComponents = try await c1 + + // ideally we would async let all of the props above and then set them here, + // but for now since there seems to be some sort of compiler bug we will do + // most of them in if lets below + pathItem.parameters = try await newParameters + + if let oldServers { + async let (newServers, c2) = oldServers.externallyDereferenced(with: loader) + pathItem.servers = try await newServers + try await newComponents.merge(c2) + } - // TODO: load external references for entire PathItem object - // also merge components before returning newComponents! + if let oldGet { + async let (newGet, c3) = oldGet.externallyDereferenced(with: loader) + pathItem.get = try await newGet + try await newComponents.merge(c3) + } + + if let oldPut { + async let (newPut, c4) = oldPut.externallyDereferenced(with: loader) + pathItem.put = try await newPut + try await newComponents.merge(c4) + } + + if let oldPost { + async let (newPost, c5) = oldPost.externallyDereferenced(with: loader) + pathItem.post = try await newPost + try await newComponents.merge(c5) + } + + if let oldDelete { + async let (newDelete, c6) = oldDelete.externallyDereferenced(with: loader) + pathItem.delete = try await newDelete + try await newComponents.merge(c6) + } + + if let oldOptions { + async let (newOptions, c7) = oldOptions.externallyDereferenced(with: loader) + pathItem.options = try await newOptions + try await newComponents.merge(c7) + } + + if let oldHead { + async let (newHead, c8) = oldHead.externallyDereferenced(with: loader) + pathItem.head = try await newHead + try await newComponents.merge(c8) + } + + if let oldPatch { + async let (newPatch, c9) = oldPatch.externallyDereferenced(with: loader) + pathItem.patch = try await newPatch + try await newComponents.merge(c9) + } + + if let oldTrace { + async let (newTrace, c10) = oldTrace.externallyDereferenced(with: loader) + pathItem.trace = try await newTrace + try await newComponents.merge(c10) + } return (pathItem, newComponents) } From f5177dc9ae6d2656a0de64cc0e39ca1381276aa1 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sat, 6 Apr 2024 19:47:02 -0500 Subject: [PATCH 027/110] a bit more work towards external dereferencing implementations --- .../Content/DereferencedContent.swift | 23 ++++++++++++++++--- .../Content/DereferencedContentEncoding.swift | 22 +++++++++++++++--- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/Sources/OpenAPIKit/Content/DereferencedContent.swift b/Sources/OpenAPIKit/Content/DereferencedContent.swift index 27cfdc10e..926572467 100644 --- a/Sources/OpenAPIKit/Content/DereferencedContent.swift +++ b/Sources/OpenAPIKit/Content/DereferencedContent.swift @@ -78,8 +78,25 @@ extension OpenAPI.Content: LocallyDereferenceable { extension OpenAPI.Content: ExternallyDereferenceable { public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { - // TOOD: need to locally dereference the schema, examples, and content encoding here. -#warning("need to locally dereference the schema, examples, and content encoding here.") - return (self, .init()) + let oldSchema = schema +// let oldExamples = examples +// let oldEncoding = encoding + + async let (newSchema, c1) = oldSchema.externallyDereferenced(with: loader) +// async let (newExamples, c2) = oldExamples.externallyDereferenced(with: loader) +// async let (newEncoding, c3) = oldEncoding.externallyDereferenced(with: loader) + + var newContent = self + var newComponents = try await c1 + + newContent.schema = try await newSchema +// newContext.examples = try await newExamples +// newComponents.merge(c2) +// newContext.encoding = try await newEncoding +// newComponents.merge(c3) + + // TOOD: need to locally dereference the examples, and encoding here. +#warning("need to locally dereference the examples, and encoding here.") + return (newContent, newComponents) } } diff --git a/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift b/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift index f68c51449..6100b01ce 100644 --- a/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift +++ b/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift @@ -59,8 +59,24 @@ extension OpenAPI.Content.Encoding: LocallyDereferenceable { extension OpenAPI.Content.Encoding: ExternallyDereferenceable { public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { - // TODO: need to externally dereference the headers here. -#warning("need to externally dereference the headers here.") - return (self, .init()) + let newHeaders: OpenAPI.Header.Map? + let newComponents: OpenAPI.Components + + if let oldHeaders = headers { + (newHeaders, newComponents) = try await oldHeaders.externallyDereferenced(with: loader) + } else { + newHeaders = nil + newComponents = .init() + } + + let newEncoding = OpenAPI.Content.Encoding( + contentType: contentType, + headers: newHeaders, + style: style, + explode: explode, + allowReserved: allowReserved + ) + + return (newEncoding, newComponents) } } From 2508f854444e6e57a300b5dfdebe19ecacb36eef Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sun, 7 Apr 2024 19:51:32 -0500 Subject: [PATCH 028/110] Add utility for externally dereferencing a regular non-ordered dictionary --- ...Dictionary+ExternallyDereferenceable.swift | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 Sources/OpenAPIKit/Utility/Dictionary+ExternallyDereferenceable.swift diff --git a/Sources/OpenAPIKit/Utility/Dictionary+ExternallyDereferenceable.swift b/Sources/OpenAPIKit/Utility/Dictionary+ExternallyDereferenceable.swift new file mode 100644 index 000000000..75a4cb0b0 --- /dev/null +++ b/Sources/OpenAPIKit/Utility/Dictionary+ExternallyDereferenceable.swift @@ -0,0 +1,29 @@ +// +// Dictionary+ExternallyDereferenceable.swift +// OpenAPI +// + +import OpenAPIKitCore + +extension Dictionary where Value: ExternallyDereferenceable { + + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + try await withThrowingTaskGroup(of: (Key, Value, OpenAPI.Components).self) { group in + for (key, value) in self { + group.addTask { + let (newRef, components) = try await value.externallyDereferenced(with: loader) + return (key, newRef, components) + } + } + + var newDict = Self() + var newComponents = OpenAPI.Components() + + for try await (key, newRef, components) in group { + newDict[key] = newRef + try newComponents.merge(components) + } + return (newDict, newComponents) + } + } +} From 2b90c7c55e6d8ff38bfe9699b472b2c584271997 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sun, 7 Apr 2024 19:51:54 -0500 Subject: [PATCH 029/110] a bit more work towards external dereferencing implementations --- .../Operation/DereferencedOperation.swift | 26 +++++++++++++++--- .../Parameter/DereferencedParameter.swift | 27 ++++++++++++++++--- .../Request/DereferencedRequest.swift | 9 ++++--- 3 files changed, 52 insertions(+), 10 deletions(-) diff --git a/Sources/OpenAPIKit/Operation/DereferencedOperation.swift b/Sources/OpenAPIKit/Operation/DereferencedOperation.swift index e39a3f975..4f7ccda18 100644 --- a/Sources/OpenAPIKit/Operation/DereferencedOperation.swift +++ b/Sources/OpenAPIKit/Operation/DereferencedOperation.swift @@ -127,8 +127,28 @@ extension OpenAPI.Operation: LocallyDereferenceable { extension OpenAPI.Operation: ExternallyDereferenceable { public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { - // TODO: externally dereference security, responses, requestBody, and parameters -#warning("externally dereference security, responses, requestBody, and parameters") - return (self, .init()) + let oldParameters = parameters + let oldRequestBody = requestBody + let oldResponses = responses + + async let (newParameters, c1) = oldParameters.externallyDereferenced(with: loader) + async let (newRequestBody, c2) = oldRequestBody.externallyDereferenced(with: loader) + async let (newResponses, c3) = oldResponses.externallyDereferenced(with: loader) +// let (newCallbacks, c4) = try await callbacks.externallyDereferenced(with: loader) +// let (newSecurtiy, c5) = try await security.externallyDereferenced(with: loader) +// let (newServers, c6) = try await servers.externallyDereferenced(with: loader) + + var newOperation = self + var newComponents = try await c1 + + newOperation.parameters = try await newParameters + newOperation.requestBody = try await newRequestBody + try await newComponents.merge(c2) + newOperation.responses = try await newResponses + try await newComponents.merge(c3) + + // TODO: externally dereference servers, callbacks, and security +#warning("externally dereference servers, callbacks, and security") + return (newOperation, newComponents) } } diff --git a/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift b/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift index 5168ad22b..2d3153847 100644 --- a/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift +++ b/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift @@ -85,9 +85,28 @@ extension OpenAPI.Parameter: LocallyDereferenceable { extension OpenAPI.Parameter: ExternallyDereferenceable { public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { - - // TODO: externallyDerefence the schemaOrContent -#warning("need to externally dereference the schemaOrContent here") - return (self, .init()) + + // if not for a Swift bug, this whole function would just be the + // next line: +// let (newSchemaOrContent, components) = try await schemaOrContent.externallyDereferenced(with: loader) + + let newSchemaOrContent: Either + let newComponents: OpenAPI.Components + + switch schemaOrContent { + case .a(let schemaContext): + let (context, components) = try await schemaContext.externallyDereferenced(with: loader) + newSchemaOrContent = .a(context) + newComponents = components + case .b(let contentMap): + let (map, components) = try await contentMap.externallyDereferenced(with: loader) + newSchemaOrContent = .b(map) + newComponents = components + } + + var newParameter = self + newParameter.schemaOrContent = newSchemaOrContent + + return (newParameter, newComponents) } } diff --git a/Sources/OpenAPIKit/Request/DereferencedRequest.swift b/Sources/OpenAPIKit/Request/DereferencedRequest.swift index 97c947fde..27ee92d0b 100644 --- a/Sources/OpenAPIKit/Request/DereferencedRequest.swift +++ b/Sources/OpenAPIKit/Request/DereferencedRequest.swift @@ -64,8 +64,11 @@ extension OpenAPI.Request: LocallyDereferenceable { extension OpenAPI.Request: ExternallyDereferenceable { public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { - // TODO: externally dereference the content -#warning("externally dereference the content") - return (self, .init()) + var newRequest = self + + let (newContent, components) = try await content.externallyDereferenced(with: loader) + + newRequest.content = newContent + return (newRequest, components) } } From fdfb40ec7fc9da25c4e6be9686fb27cebe803fd0 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 9 Apr 2024 08:39:09 -0500 Subject: [PATCH 030/110] Support external dereferencing for JSONSchema --- .../DereferencedJSONSchema.swift | 98 ++++++++++++++++++- 1 file changed, 95 insertions(+), 3 deletions(-) diff --git a/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift b/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift index 8cac1722a..6cfef7a2c 100644 --- a/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift +++ b/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift @@ -537,8 +537,100 @@ extension JSONSchema: LocallyDereferenceable { extension JSONSchema: ExternallyDereferenceable { public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { - // TODO: externally dereference this schema -#warning("need to externally dereference json schemas") - return (self, .init()) + let newSchema: JSONSchema + let newComponents: OpenAPI.Components + + switch value { + case .null(_): + newComponents = .noComponents + newSchema = self + case .boolean(_): + newComponents = .noComponents + newSchema = self + case .number(_, _): + newComponents = .noComponents + newSchema = self + case .integer(_, _): + newComponents = .noComponents + newSchema = self + case .string(_, _): + newComponents = .noComponents + newSchema = self + case .object(let core, let object): + var components = OpenAPI.Components() + + let (newProperties, c1) = try await object.properties.externallyDereferenced(with: loader) + try components.merge(c1) + + let newAdditionalProperties: Either? + if case .b(let schema) = object.additionalProperties { + let (additionalProperties, c2) = try await schema.externallyDereferenced(with: loader) + try components.merge(c2) + newAdditionalProperties = .b(additionalProperties) + } else { + newAdditionalProperties = object.additionalProperties + } + newComponents = components + newSchema = .init( + schema: .object( + core, + .init( + properties: newProperties, + additionalProperties: newAdditionalProperties, + maxProperties: object.maxProperties, + minProperties: object._minProperties + ) + ) + ) + case .array(let core, let array): + let (newItems, components) = try await array.items.externallyDereferenced(with: loader) + newComponents = components + newSchema = .init( + schema: .array( + core, + .init( + items: newItems, + maxItems: array.maxItems, + minItems: array._minItems, + uniqueItems: array._uniqueItems + ) + ) + ) + case .all(let schema, let core): + let (newSubschemas, components) = try await schema.externallyDereferenced(with: loader) + newComponents = components + newSchema = .init( + schema: .all(of: newSubschemas, core: core) + ) + case .one(let schema, let core): + let (newSubschemas, components) = try await schema.externallyDereferenced(with: loader) + newComponents = components + newSchema = .init( + schema: .one(of: newSubschemas, core: core) + ) + case .any(let schema, let core): + let (newSubschemas, components) = try await schema.externallyDereferenced(with: loader) + newComponents = components + newSchema = .init( + schema: .any(of: newSubschemas, core: core) + ) + case .not(let schema, let core): + let (newSubschema, components) = try await schema.externallyDereferenced(with: loader) + newComponents = components + newSchema = .init( + schema: .not(newSubschema, core: core) + ) + case .reference(let reference, let core): + let (newReference, components) = try await reference.externallyDereferenced(with: loader) + newComponents = components + newSchema = .init( + schema: .reference(newReference, core) + ) + case .fragment(_): + newComponents = .noComponents + newSchema = self + } + + return (newSchema, newComponents) } } From 3c1977789809e8b5b37027938d253b0efc5f42f5 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 9 Apr 2024 10:43:29 -0500 Subject: [PATCH 031/110] more external dereferencing conformances --- Sources/OpenAPIKit/Callbacks.swift | 9 +++- .../Content/DereferencedContent.swift | 22 +++++---- .../Header/DereferencedHeader.swift | 31 ++++++++++-- Sources/OpenAPIKit/Link.swift | 17 ++++--- .../Operation/DereferencedOperation.swift | 47 ++++++++++++++++++- Sources/OpenAPIKit/Operation/Operation.swift | 2 +- .../Parameter/DereferencedSchemaContext.swift | 19 ++++++-- .../Parameter/ParameterSchemaContext.swift | 12 ++--- 8 files changed, 125 insertions(+), 34 deletions(-) diff --git a/Sources/OpenAPIKit/Callbacks.swift b/Sources/OpenAPIKit/Callbacks.swift index 2f81e4a17..407c495f3 100644 --- a/Sources/OpenAPIKit/Callbacks.swift +++ b/Sources/OpenAPIKit/Callbacks.swift @@ -39,8 +39,13 @@ extension OpenAPI.CallbackURL: LocallyDereferenceable { extension OpenAPI.CallbackURL: ExternallyDereferenceable { public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { - // TODO: externally dereference security, responses, requestBody, and parameters -#warning("externally dereference security, responses, requestBody, and parameters") return (self, .init()) } } + +// The following conformance is theoretically unnecessary but the compiler is +// only able to find the conformance if we explicitly declare it here, though +// it is apparently able to determine the conformance is already satisfied here +// at least. +extension OpenAPI.Callbacks: ExternallyDereferenceable { } + diff --git a/Sources/OpenAPIKit/Content/DereferencedContent.swift b/Sources/OpenAPIKit/Content/DereferencedContent.swift index 926572467..18d02f624 100644 --- a/Sources/OpenAPIKit/Content/DereferencedContent.swift +++ b/Sources/OpenAPIKit/Content/DereferencedContent.swift @@ -79,24 +79,26 @@ extension OpenAPI.Content: LocallyDereferenceable { extension OpenAPI.Content: ExternallyDereferenceable { public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { let oldSchema = schema -// let oldExamples = examples -// let oldEncoding = encoding async let (newSchema, c1) = oldSchema.externallyDereferenced(with: loader) -// async let (newExamples, c2) = oldExamples.externallyDereferenced(with: loader) -// async let (newEncoding, c3) = oldEncoding.externallyDereferenced(with: loader) var newContent = self var newComponents = try await c1 newContent.schema = try await newSchema -// newContext.examples = try await newExamples -// newComponents.merge(c2) -// newContext.encoding = try await newEncoding -// newComponents.merge(c3) - // TOOD: need to locally dereference the examples, and encoding here. -#warning("need to locally dereference the examples, and encoding here.") + if let oldExamples = examples { + let (newExamples, c2) = try await oldExamples.externallyDereferenced(with: loader) + newContent.examples = newExamples + try newComponents.merge(c2) + } + + if let oldEncoding = encoding { + let (newEncoding, c3) = try await oldEncoding.externallyDereferenced(with: loader) + newContent.encoding = newEncoding + try newComponents.merge(c3) + } + return (newContent, newComponents) } } diff --git a/Sources/OpenAPIKit/Header/DereferencedHeader.swift b/Sources/OpenAPIKit/Header/DereferencedHeader.swift index d34a7374c..891331af9 100644 --- a/Sources/OpenAPIKit/Header/DereferencedHeader.swift +++ b/Sources/OpenAPIKit/Header/DereferencedHeader.swift @@ -85,8 +85,33 @@ extension OpenAPI.Header: LocallyDereferenceable { extension OpenAPI.Header: ExternallyDereferenceable { public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { - // TODO: externally dereference the schemaOrContent -#warning("externally dereference the schemaOrContent") - return (self, .init()) + + // if not for a Swift bug, this whole next bit would just be the + // next line: +// let (newSchemaOrContent, components) = try await schemaOrContent.externallyDereferenced(with: loader) + + let newSchemaOrContent: Either + let newComponents: OpenAPI.Components + + switch schemaOrContent { + case .a(let schemaContext): + let (context, components) = try await schemaContext.externallyDereferenced(with: loader) + newSchemaOrContent = .a(context) + newComponents = components + case .b(let contentMap): + let (map, components) = try await contentMap.externallyDereferenced(with: loader) + newSchemaOrContent = .b(map) + newComponents = components + } + + let newHeader = OpenAPI.Header( + schemaOrContent: newSchemaOrContent, + description: description, + required: required, + deprecated: deprecated, + vendorExtensions: vendorExtensions + ) + + return (newHeader, newComponents) } } diff --git a/Sources/OpenAPIKit/Link.swift b/Sources/OpenAPIKit/Link.swift index 409b1ca4d..d79316bad 100644 --- a/Sources/OpenAPIKit/Link.swift +++ b/Sources/OpenAPIKit/Link.swift @@ -20,18 +20,18 @@ extension OpenAPI { /// The **OpenAPI**` `operationRef` or `operationId` field, depending on whether /// a `URL` of a remote or local Operation Object or a `operationId` (String) of an /// operation defined in the same document is given. - public let operation: Either + public var operation: Either /// A map from parameter names to either runtime expressions that evaluate to values or /// constant values (`AnyCodable`). /// /// See the docuemntation for the [OpenAPI Link Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#link-object) for more details. /// /// Empty dictionaries will be omitted from encoding. - public let parameters: OrderedDictionary> + public var parameters: OrderedDictionary> /// A literal value or expression to use as a request body when calling the target operation. - public let requestBody: Either? + public var requestBody: Either? public var description: String? - public let server: Server? + public var server: Server? /// Dictionary of vendor extensions. /// @@ -291,9 +291,12 @@ extension OpenAPI.Link: LocallyDereferenceable { extension OpenAPI.Link: ExternallyDereferenceable { public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { - // TODO: externally dereference security, responses, requestBody, and parameters -#warning("externally dereference security, responses, requestBody, and parameters") - return (self, .init()) + let (newServer, newComponents) = try await server.externallyDereferenced(with: loader) + + var newLink = self + newLink.server = newServer + + return (newLink, newComponents) } } diff --git a/Sources/OpenAPIKit/Operation/DereferencedOperation.swift b/Sources/OpenAPIKit/Operation/DereferencedOperation.swift index 4f7ccda18..7e32c7f14 100644 --- a/Sources/OpenAPIKit/Operation/DereferencedOperation.swift +++ b/Sources/OpenAPIKit/Operation/DereferencedOperation.swift @@ -147,8 +147,51 @@ extension OpenAPI.Operation: ExternallyDereferenceable { newOperation.responses = try await newResponses try await newComponents.merge(c3) - // TODO: externally dereference servers, callbacks, and security -#warning("externally dereference servers, callbacks, and security") + // should not be necessary but current Swift compiler can't figure out conformance of ExternallyDereferenceable: + var newCallbacks = OpenAPI.CallbacksMap() + for (key, value) in callbacks { + switch value { + case .a(let callbackReference): + let oldJsonReference = callbackReference.jsonReference + + let (newJsonReference, components) = try await oldJsonReference.externallyDereferenced(with: loader) + try newComponents.merge(components) + + let newCallbackReference = OpenAPI.Reference( + newJsonReference, + summary: callbackReference.summary, + description: callbackReference.description + ) + + newCallbacks[key] = .a(newCallbackReference) + case .b(let callback): + let (newCallback, components) = try await callback.externallyDereferenced(with: loader) + newCallbacks[key] = .b(newCallback) + try newComponents.merge(components) + } + } + newOperation.callbacks = newCallbacks + + if let oldServers = servers { + let (newServers, c6) = try await oldServers.externallyDereferenced(with: loader) + newOperation.servers = newServers + try newComponents.merge(c6) + } + + // should not be necessary but current Swift compiler can't figure out conformance of ExternallyDereferenceable: + if let oldRequestBody = requestBody { + let (newRequestBody, c5) = try await oldRequestBody.externallyDereferenced(with: loader) + newOperation.requestBody = newRequestBody + try newComponents.merge(c5) + } + + // should not be necessary but current Swift compiler can't figure out conformance of ExternallyDereferenceable: + if let oldServers = servers { + let (newServers, c6) = try await oldServers.externallyDereferenced(with: loader) + newOperation.servers = newServers + try newComponents.merge(c6) + } + return (newOperation, newComponents) } } diff --git a/Sources/OpenAPIKit/Operation/Operation.swift b/Sources/OpenAPIKit/Operation/Operation.swift index 4efdc5899..c5adb49fa 100644 --- a/Sources/OpenAPIKit/Operation/Operation.swift +++ b/Sources/OpenAPIKit/Operation/Operation.swift @@ -76,7 +76,7 @@ extension OpenAPI { /// The key is a unique identifier for the Callback Object. Each value in the /// map is a Callback Object that describes a request that may be initiated /// by the API provider and the expected responses. - public let callbacks: OpenAPI.CallbacksMap + public var callbacks: OpenAPI.CallbacksMap /// Indicates that the operation is deprecated or not. /// diff --git a/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift b/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift index 81d01ef8f..00cfaa7af 100644 --- a/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift +++ b/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift @@ -72,8 +72,21 @@ extension OpenAPI.Parameter.SchemaContext: LocallyDereferenceable { extension OpenAPI.Parameter.SchemaContext: ExternallyDereferenceable { public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { - // TODO: externally dereference schema, examples, and example -#warning("externally dereference schema, examples, and example") - return (self, .init()) + let oldSchema = schema + + async let (newSchema, c1) = oldSchema.externallyDereferenced(with: loader) + + var newSchemaContext = self + var newComponents = try await c1 + + newSchemaContext.schema = try await newSchema + + if let oldExamples = examples { + let (newExamples, c2) = try await oldExamples.externallyDereferenced(with: loader) + newSchemaContext.examples = newExamples + try newComponents.merge(c2) + } + + return (newSchemaContext, newComponents) } } diff --git a/Sources/OpenAPIKit/Parameter/ParameterSchemaContext.swift b/Sources/OpenAPIKit/Parameter/ParameterSchemaContext.swift index 336305738..cd65f89c5 100644 --- a/Sources/OpenAPIKit/Parameter/ParameterSchemaContext.swift +++ b/Sources/OpenAPIKit/Parameter/ParameterSchemaContext.swift @@ -13,13 +13,13 @@ extension OpenAPI.Parameter { /// See [OpenAPI Parameter Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#parameter-object) /// and [OpenAPI Style Values](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#style-values). public struct SchemaContext: Equatable { - public let style: Style - public let explode: Bool - public let allowReserved: Bool //defaults to false - public let schema: Either, JSONSchema> + public var style: Style + public var explode: Bool + public var allowReserved: Bool //defaults to false + public var schema: Either, JSONSchema> - public let example: AnyCodable? - public let examples: OpenAPI.Example.Map? + public var example: AnyCodable? + public var examples: OpenAPI.Example.Map? public init(_ schema: JSONSchema, style: Style, From 93486b2a61a40d4c63f77d050235080913514b05 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Wed, 10 Apr 2024 08:09:38 -0500 Subject: [PATCH 032/110] fix temporary test. fix path operation external dereferencing. remove most over the top workaround code having found a better workaround for compiler bug. --- .../Operation/DereferencedOperation.swift | 37 ++----------------- .../Path Item/PathItemTests.swift | 11 ++++-- 2 files changed, 10 insertions(+), 38 deletions(-) diff --git a/Sources/OpenAPIKit/Operation/DereferencedOperation.swift b/Sources/OpenAPIKit/Operation/DereferencedOperation.swift index 7e32c7f14..8f7844694 100644 --- a/Sources/OpenAPIKit/Operation/DereferencedOperation.swift +++ b/Sources/OpenAPIKit/Operation/DereferencedOperation.swift @@ -134,8 +134,7 @@ extension OpenAPI.Operation: ExternallyDereferenceable { async let (newParameters, c1) = oldParameters.externallyDereferenced(with: loader) async let (newRequestBody, c2) = oldRequestBody.externallyDereferenced(with: loader) async let (newResponses, c3) = oldResponses.externallyDereferenced(with: loader) -// let (newCallbacks, c4) = try await callbacks.externallyDereferenced(with: loader) -// let (newSecurtiy, c5) = try await security.externallyDereferenced(with: loader) + async let (newCallbacks, c4) = callbacks.externallyDereferenced(with: loader) // let (newServers, c6) = try await servers.externallyDereferenced(with: loader) var newOperation = self @@ -146,44 +145,14 @@ extension OpenAPI.Operation: ExternallyDereferenceable { try await newComponents.merge(c2) newOperation.responses = try await newResponses try await newComponents.merge(c3) - - // should not be necessary but current Swift compiler can't figure out conformance of ExternallyDereferenceable: - var newCallbacks = OpenAPI.CallbacksMap() - for (key, value) in callbacks { - switch value { - case .a(let callbackReference): - let oldJsonReference = callbackReference.jsonReference - - let (newJsonReference, components) = try await oldJsonReference.externallyDereferenced(with: loader) - try newComponents.merge(components) - - let newCallbackReference = OpenAPI.Reference( - newJsonReference, - summary: callbackReference.summary, - description: callbackReference.description - ) - - newCallbacks[key] = .a(newCallbackReference) - case .b(let callback): - let (newCallback, components) = try await callback.externallyDereferenced(with: loader) - newCallbacks[key] = .b(newCallback) - try newComponents.merge(components) - } - } - newOperation.callbacks = newCallbacks + newOperation.callbacks = try await newCallbacks + try await newComponents.merge(c4) if let oldServers = servers { let (newServers, c6) = try await oldServers.externallyDereferenced(with: loader) newOperation.servers = newServers try newComponents.merge(c6) } - - // should not be necessary but current Swift compiler can't figure out conformance of ExternallyDereferenceable: - if let oldRequestBody = requestBody { - let (newRequestBody, c5) = try await oldRequestBody.externallyDereferenced(with: loader) - newOperation.requestBody = newRequestBody - try newComponents.merge(c5) - } // should not be necessary but current Swift compiler can't figure out conformance of ExternallyDereferenceable: if let oldServers = servers { diff --git a/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift b/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift index 2e55b6aec..ebd4188dc 100644 --- a/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift +++ b/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift @@ -7,6 +7,7 @@ import XCTest import OpenAPIKit +import Foundation final class PathItemTests: XCTestCase { func test_initializePathComponents() { @@ -436,8 +437,10 @@ extension PathItemTests { // MARK: External Dereferencing Tests extension PathItemTests { struct MockLoad: ExternalLoaderContext { - static func componentKey(type: T.Type, at url: URL) -> OpenAPI.ComponentKey { - "hello-world" + static func componentKey(type: T.Type, at url: URL) throws -> OpenAPI.ComponentKey { + let urlString = url.pathComponents.dropFirst().joined(separator: "_").replacingOccurrences(of: ".", with: "_") + + return try .forceInit(rawValue: urlString) } static func load(_: URL) throws -> T where T : Decodable { @@ -460,9 +463,9 @@ extension PathItemTests { parameters: [ .parameter(name: "hello", context: .query, schema: .string), .reference(.component(named: "already-internal")), - .reference(.external(URL(string: "https://some-param.com")!)) + .reference(.external(URL(string: "https://some-param.com/param")!)) ], - get: .init(requestBody: .reference(.external(URL(string: "https://website.com")!)), responses: [:]), + get: .init(requestBody: .reference(.external(URL(string: "https://website.com/request")!)), responses: [:]), put: op, post: op, delete: op, From 32c08660674246501ba3e48e63b9599a40536d68 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Wed, 10 Apr 2024 08:48:56 -0500 Subject: [PATCH 033/110] quick port of externally dereferenceable to OpenAPIKit30 module --- Sources/OpenAPIKit30/Callbacks.swift | 12 +++ .../Components+Locatable.swift | 22 ++--- .../Components Object/Components.swift | 86 ++++++++++++---- .../Content/DereferencedContent.swift | 27 ++++++ .../Content/DereferencedContentEncoding.swift | 24 +++++ Sources/OpenAPIKit30/Document/Document.swift | 11 +++ .../Either+ExternallyDereferenceable.swift | 23 +++++ Sources/OpenAPIKit30/Example.swift | 6 ++ Sources/OpenAPIKit30/ExternalLoader.swift | 34 +++++++ .../Header/DereferencedHeader.swift | 33 +++++++ Sources/OpenAPIKit30/JSONReference.swift | 16 +++ Sources/OpenAPIKit30/Link.swift | 19 +++- .../Operation/DereferencedOperation.swift | 40 ++++++++ .../OpenAPIKit30/Operation/Operation.swift | 2 +- .../Parameter/DereferencedParameter.swift | 28 ++++++ .../Parameter/DereferencedSchemaContext.swift | 21 ++++ .../Parameter/ParameterSchemaContext.swift | 12 +-- .../Path Item/DereferencedPathItem.swift | 90 +++++++++++++++++ .../Request/DereferencedRequest.swift | 11 +++ .../Response/DereferencedResponse.swift | 25 +++++ .../DereferencedJSONSchema.swift | 97 +++++++++++++++++++ .../Security/SecurityScheme.swift | 6 ++ Sources/OpenAPIKit30/Server.swift | 6 ++ .../Array+ExternallyDereferenceable.swift | 27 ++++++ ...Dictionary+ExternallyDereferenceable.swift | 29 ++++++ .../Optional+ExternallyDereferenceable.swift | 13 +++ ...Dictionary+ExternallyDereferenceable.swift | 31 ++++++ 27 files changed, 712 insertions(+), 39 deletions(-) create mode 100644 Sources/OpenAPIKit30/Either/Either+ExternallyDereferenceable.swift create mode 100644 Sources/OpenAPIKit30/ExternalLoader.swift create mode 100644 Sources/OpenAPIKit30/Utility/Array+ExternallyDereferenceable.swift create mode 100644 Sources/OpenAPIKit30/Utility/Dictionary+ExternallyDereferenceable.swift create mode 100644 Sources/OpenAPIKit30/Utility/Optional+ExternallyDereferenceable.swift create mode 100644 Sources/OpenAPIKit30/Utility/OrderedDictionary+ExternallyDereferenceable.swift diff --git a/Sources/OpenAPIKit30/Callbacks.swift b/Sources/OpenAPIKit30/Callbacks.swift index f0504fa25..29b1ff612 100644 --- a/Sources/OpenAPIKit30/Callbacks.swift +++ b/Sources/OpenAPIKit30/Callbacks.swift @@ -35,3 +35,15 @@ extension OpenAPI.CallbackURL: LocallyDereferenceable { self } } + +extension OpenAPI.CallbackURL: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + return (self, .init()) + } +} + +// The following conformance is theoretically unnecessary but the compiler is +// only able to find the conformance if we explicitly declare it here, though +// it is apparently able to determine the conformance is already satisfied here +// at least. +extension OpenAPI.Callbacks: ExternallyDereferenceable { } diff --git a/Sources/OpenAPIKit30/Components Object/Components+Locatable.swift b/Sources/OpenAPIKit30/Components Object/Components+Locatable.swift index ed2172e2b..cd6fcfd91 100644 --- a/Sources/OpenAPIKit30/Components Object/Components+Locatable.swift +++ b/Sources/OpenAPIKit30/Components Object/Components+Locatable.swift @@ -15,59 +15,59 @@ public protocol ComponentDictionaryLocatable { /// This can be used to create a JSON path /// like `#/name1/name2/name3` static var openAPIComponentsKey: String { get } - static var openAPIComponentsKeyPath: KeyPath> { get } + static var openAPIComponentsKeyPath: WritableKeyPath> { get } } extension JSONSchema: ComponentDictionaryLocatable { public static var openAPIComponentsKey: String { "schemas" } - public static var openAPIComponentsKeyPath: KeyPath> { \.schemas } + public static var openAPIComponentsKeyPath: WritableKeyPath> { \.schemas } } extension OpenAPI.Response: ComponentDictionaryLocatable { public static var openAPIComponentsKey: String { "responses" } - public static var openAPIComponentsKeyPath: KeyPath> { \.responses } + public static var openAPIComponentsKeyPath: WritableKeyPath> { \.responses } } extension OpenAPI.Callbacks: ComponentDictionaryLocatable { public static var openAPIComponentsKey: String { "callbacks" } - public static var openAPIComponentsKeyPath: KeyPath> { \.callbacks } + public static var openAPIComponentsKeyPath: WritableKeyPath> { \.callbacks } } extension OpenAPI.Parameter: ComponentDictionaryLocatable { public static var openAPIComponentsKey: String { "parameters" } - public static var openAPIComponentsKeyPath: KeyPath> { \.parameters } + public static var openAPIComponentsKeyPath: WritableKeyPath> { \.parameters } } extension OpenAPI.Example: ComponentDictionaryLocatable { public static var openAPIComponentsKey: String { "examples" } - public static var openAPIComponentsKeyPath: KeyPath> { \.examples } + public static var openAPIComponentsKeyPath: WritableKeyPath> { \.examples } } extension OpenAPI.Request: ComponentDictionaryLocatable { public static var openAPIComponentsKey: String { "requestBodies" } - public static var openAPIComponentsKeyPath: KeyPath> { \.requestBodies } + public static var openAPIComponentsKeyPath: WritableKeyPath> { \.requestBodies } } extension OpenAPI.Header: ComponentDictionaryLocatable { public static var openAPIComponentsKey: String { "headers" } - public static var openAPIComponentsKeyPath: KeyPath> { \.headers } + public static var openAPIComponentsKeyPath: WritableKeyPath> { \.headers } } extension OpenAPI.SecurityScheme: ComponentDictionaryLocatable { public static var openAPIComponentsKey: String { "securitySchemes" } - public static var openAPIComponentsKeyPath: KeyPath> { \.securitySchemes } + public static var openAPIComponentsKeyPath: WritableKeyPath> { \.securitySchemes } } extension OpenAPI.Link: ComponentDictionaryLocatable { public static var openAPIComponentsKey: String { "links" } - public static var openAPIComponentsKeyPath: KeyPath> { \.links } + public static var openAPIComponentsKeyPath: WritableKeyPath> { \.links } } // Until OpenAPI 3.1, path items cannot actually be stored in the Components Object. This is here to facilitate path item // references, albeit in a less than ideal way. extension OpenAPI.PathItem: ComponentDictionaryLocatable { public static var openAPIComponentsKey: String { "pathItems" } - public static var openAPIComponentsKeyPath: KeyPath> { \.pathItems } + public static var openAPIComponentsKeyPath: WritableKeyPath> { \.pathItems } } /// A dereferenceable type can be recursively looked up in diff --git a/Sources/OpenAPIKit30/Components Object/Components.swift b/Sources/OpenAPIKit30/Components Object/Components.swift index 93bcad2f5..c2762f3be 100644 --- a/Sources/OpenAPIKit30/Components Object/Components.swift +++ b/Sources/OpenAPIKit30/Components Object/Components.swift @@ -71,6 +71,28 @@ extension OpenAPI { } } +extension OpenAPI.Components { + public struct ComponentCollision: Swift.Error { + public let componentType: String + public let existingComponent: String + public let newComponent: String + } + + public mutating func merge(_ other: OpenAPI.Components) throws { + try schemas.merge(other.schemas, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "schema", existingComponent: String(describing: a), newComponent: String(describing: b)) }) + try responses.merge(other.responses, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "responses", existingComponent: String(describing: a), newComponent: String(describing: b)) }) + try parameters.merge(other.parameters, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "parameters", existingComponent: String(describing: a), newComponent: String(describing: b)) }) + try examples.merge(other.examples, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "examples", existingComponent: String(describing: a), newComponent: String(describing: b)) }) + try requestBodies.merge(other.requestBodies, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "requestBodies", existingComponent: String(describing: a), newComponent: String(describing: b)) }) + try headers.merge(other.headers, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "headers", existingComponent: String(describing: a), newComponent: String(describing: b)) }) + try securitySchemes.merge(other.securitySchemes, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "securitySchemes", existingComponent: String(describing: a), newComponent: String(describing: b)) }) + try links.merge(other.links, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "links", existingComponent: String(describing: a), newComponent: String(describing: b)) }) + try callbacks.merge(other.callbacks, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "callbacks", existingComponent: String(describing: a), newComponent: String(describing: b)) }) + try pathItems.merge(other.pathItems, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "pathItems", existingComponent: String(describing: a), newComponent: String(describing: b)) }) + try vendorExtensions.merge(other.vendorExtensions, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "vendorExtensions", existingComponent: String(describing: a), newComponent: String(describing: b)) }) + } +} + extension OpenAPI.Components { /// The extension name used to store a Components Object name (the key something is stored under /// within the Components Object). This is used by OpenAPIKit to store the previous Component name @@ -260,24 +282,54 @@ extension OpenAPI.Components { } } -public extension OpenAPI.Components { - struct ValueCollision: Swift.Error { - let value1: T - let value2: T - } +extension OpenAPI.Components { + internal mutating func externallyDereference(in context: Context.Type) async throws { + let oldSchemas = schemas + let oldResponses = responses + let oldParameters = parameters + let oldExamples = examples + let oldRequestBodies = requestBodies + let oldHeaders = headers + let oldSecuritySchemes = securitySchemes + + let oldCallbacks = callbacks + + async let (newSchemas, c1) = oldSchemas.externallyDereferenced(with: context) + async let (newResponses, c2) = oldResponses.externallyDereferenced(with: context) + async let (newParameters, c3) = oldParameters.externallyDereferenced(with: context) + async let (newExamples, c4) = oldExamples.externallyDereferenced(with: context) + async let (newRequestBodies, c5) = oldRequestBodies.externallyDereferenced(with: context) + async let (newHeaders, c6) = oldHeaders.externallyDereferenced(with: context) + async let (newSecuritySchemes, c7) = oldSecuritySchemes.externallyDereferenced(with: context) + +// async let (newCallbacks, c8) = oldCallbacks.externallyDereferenced(with: context) + var c8 = OpenAPI.Components() + var newCallbacks = oldCallbacks + for (key, callback) in oldCallbacks { + let (newCallback, components) = try await callback.externallyDereferenced(with: context) + newCallbacks[key] = newCallback + try c8.merge(components) + } - mutating func merge(_ components: OpenAPI.Components) throws { - try schemas.merge(components.schemas, uniquingKeysWith: { (v1, v2) in throw OpenAPI.Components.ValueCollision(value1: v1, value2: v2) }) - try responses.merge(components.responses, uniquingKeysWith: { (v1, v2) in throw OpenAPI.Components.ValueCollision(value1: v1, value2: v2) }) - try parameters.merge(components.parameters, uniquingKeysWith: { (v1, v2) in throw OpenAPI.Components.ValueCollision(value1: v1, value2: v2) }) - try examples.merge(components.examples, uniquingKeysWith: { (v1, v2) in throw OpenAPI.Components.ValueCollision(value1: v1, value2: v2) }) - try requestBodies.merge(components.requestBodies, uniquingKeysWith: { (v1, v2) in throw OpenAPI.Components.ValueCollision(value1: v1, value2: v2) }) - try headers.merge(components.headers, uniquingKeysWith: { (v1, v2) in throw OpenAPI.Components.ValueCollision(value1: v1, value2: v2) }) - try securitySchemes.merge(components.securitySchemes, uniquingKeysWith: { (v1, v2) in throw OpenAPI.Components.ValueCollision(value1: v1, value2: v2) }) - try links.merge(components.links, uniquingKeysWith: { (v1, v2) in throw OpenAPI.Components.ValueCollision(value1: v1, value2: v2) }) - try callbacks.merge(components.callbacks, uniquingKeysWith: { (v1, v2) in throw OpenAPI.Components.ValueCollision(value1: v1, value2: v2) }) - try pathItems.merge(components.pathItems, uniquingKeysWith: { (v1, v2) in throw OpenAPI.Components.ValueCollision(value1: v1, value2: v2) }) - try vendorExtensions.merge(components.vendorExtensions, uniquingKeysWith: { (v1, v2) in throw OpenAPI.Components.ValueCollision(value1: v1, value2: v2) }) + schemas = try await newSchemas + responses = try await newResponses + parameters = try await newParameters + examples = try await newExamples + requestBodies = try await newRequestBodies + headers = try await newHeaders + securitySchemes = try await newSecuritySchemes + + callbacks = newCallbacks + + try await merge(c1) + try await merge(c2) + try await merge(c3) + try await merge(c4) + try await merge(c5) + try await merge(c6) + try await merge(c7) + + try merge(c8) } } diff --git a/Sources/OpenAPIKit30/Content/DereferencedContent.swift b/Sources/OpenAPIKit30/Content/DereferencedContent.swift index e9ee1fef8..12281406d 100644 --- a/Sources/OpenAPIKit30/Content/DereferencedContent.swift +++ b/Sources/OpenAPIKit30/Content/DereferencedContent.swift @@ -75,3 +75,30 @@ extension OpenAPI.Content: LocallyDereferenceable { return try DereferencedContent(self, resolvingIn: components, following: references) } } + +extension OpenAPI.Content: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + let oldSchema = schema + + async let (newSchema, c1) = oldSchema.externallyDereferenced(with: loader) + + var newContent = self + var newComponents = try await c1 + + newContent.schema = try await newSchema + + if let oldExamples = examples { + let (newExamples, c2) = try await oldExamples.externallyDereferenced(with: loader) + newContent.examples = newExamples + try newComponents.merge(c2) + } + + if let oldEncoding = encoding { + let (newEncoding, c3) = try await oldEncoding.externallyDereferenced(with: loader) + newContent.encoding = newEncoding + try newComponents.merge(c3) + } + + return (newContent, newComponents) + } +} diff --git a/Sources/OpenAPIKit30/Content/DereferencedContentEncoding.swift b/Sources/OpenAPIKit30/Content/DereferencedContentEncoding.swift index fdd0b1bbc..6100b01ce 100644 --- a/Sources/OpenAPIKit30/Content/DereferencedContentEncoding.swift +++ b/Sources/OpenAPIKit30/Content/DereferencedContentEncoding.swift @@ -56,3 +56,27 @@ extension OpenAPI.Content.Encoding: LocallyDereferenceable { return try DereferencedContentEncoding(self, resolvingIn: components, following: references) } } + +extension OpenAPI.Content.Encoding: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + let newHeaders: OpenAPI.Header.Map? + let newComponents: OpenAPI.Components + + if let oldHeaders = headers { + (newHeaders, newComponents) = try await oldHeaders.externallyDereferenced(with: loader) + } else { + newHeaders = nil + newComponents = .init() + } + + let newEncoding = OpenAPI.Content.Encoding( + contentType: contentType, + headers: newHeaders, + style: style, + explode: explode, + allowReserved: allowReserved + ) + + return (newEncoding, newComponents) + } +} diff --git a/Sources/OpenAPIKit30/Document/Document.swift b/Sources/OpenAPIKit30/Document/Document.swift index 4f4baffd5..e2315bed0 100644 --- a/Sources/OpenAPIKit30/Document/Document.swift +++ b/Sources/OpenAPIKit30/Document/Document.swift @@ -334,6 +334,17 @@ extension OpenAPI.Document { public func locallyDereferenced() throws -> DereferencedDocument { return try DereferencedDocument(self) } + + public mutating func externallyDereference(in context: Context.Type) async throws { + let oldPaths = paths + + try await components.externallyDereference(in: context) + + async let (newPaths, c1) = oldPaths.externallyDereferenced(with: context) + + paths = try await newPaths + try await components.merge(c1) + } } extension OpenAPI { diff --git a/Sources/OpenAPIKit30/Either/Either+ExternallyDereferenceable.swift b/Sources/OpenAPIKit30/Either/Either+ExternallyDereferenceable.swift new file mode 100644 index 000000000..10e56b368 --- /dev/null +++ b/Sources/OpenAPIKit30/Either/Either+ExternallyDereferenceable.swift @@ -0,0 +1,23 @@ +// +// Either+ExternallyDereferenceable.swift +// +// +// Created by Mathew Polzin on 2/28/21. +// + +import OpenAPIKitCore + +// MARK: - ExternallyDereferenceable +extension Either: ExternallyDereferenceable where A: ExternallyDereferenceable, B: ExternallyDereferenceable { + + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + switch self { + case .a(let a): + let (newA, components) = try await a.externallyDereferenced(with: loader) + return (.a(newA), components) + case .b(let b): + let (newB, components) = try await b.externallyDereferenced(with: loader) + return (.b(newB), components) + } + } +} diff --git a/Sources/OpenAPIKit30/Example.swift b/Sources/OpenAPIKit30/Example.swift index ad0786843..e5a533187 100644 --- a/Sources/OpenAPIKit30/Example.swift +++ b/Sources/OpenAPIKit30/Example.swift @@ -184,4 +184,10 @@ extension OpenAPI.Example: LocallyDereferenceable { } } +extension OpenAPI.Example: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + return (self, .init()) + } +} + extension OpenAPI.Example: Validatable {} diff --git a/Sources/OpenAPIKit30/ExternalLoader.swift b/Sources/OpenAPIKit30/ExternalLoader.swift new file mode 100644 index 000000000..f937ac917 --- /dev/null +++ b/Sources/OpenAPIKit30/ExternalLoader.swift @@ -0,0 +1,34 @@ +// +// ExternalLoader.swift +// +// +// Created by Mathew Polzin on 7/30/2023. +// + +import OpenAPIKitCore +import Foundation + +/// An `ExternalLoaderContext` enables `OpenAPIKit` to load external references +/// without knowing the details of what decoder is being used or how new internal +/// references should be named. +public protocol ExternalLoaderContext { + /// Load the given URL and decode it as Type `T`. All Types `T` are `Decodable`, so + /// the only real responsibility of a `load` function is to locate and load the given + /// `URL` and pass its `Data` or `String` (depending on the decoder) to an appropriate + /// `Decoder` for the given file type. + static func load(_: URL) async throws -> T where T: Decodable + + /// Determine the next Component Key (where to store something in the + /// Components Object) for a new object of the given type that was loaded + /// at the given external URL. + /// + /// - Important: Ideally, this function returns distinct keys for all different objects + /// but the same key for all equal objects. In practice, this probably means that any + /// time the same type and URL pair are passed in the same `ComponentKey` should be + /// returned. + static func componentKey(type: T.Type, at url: URL) throws -> OpenAPI.ComponentKey +} + +public protocol ExternallyDereferenceable { + func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) +} diff --git a/Sources/OpenAPIKit30/Header/DereferencedHeader.swift b/Sources/OpenAPIKit30/Header/DereferencedHeader.swift index 18f453a6c..891331af9 100644 --- a/Sources/OpenAPIKit30/Header/DereferencedHeader.swift +++ b/Sources/OpenAPIKit30/Header/DereferencedHeader.swift @@ -82,3 +82,36 @@ extension OpenAPI.Header: LocallyDereferenceable { return try DereferencedHeader(self, resolvingIn: components, following: references, dereferencedFromComponentNamed: name) } } + +extension OpenAPI.Header: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + + // if not for a Swift bug, this whole next bit would just be the + // next line: +// let (newSchemaOrContent, components) = try await schemaOrContent.externallyDereferenced(with: loader) + + let newSchemaOrContent: Either + let newComponents: OpenAPI.Components + + switch schemaOrContent { + case .a(let schemaContext): + let (context, components) = try await schemaContext.externallyDereferenced(with: loader) + newSchemaOrContent = .a(context) + newComponents = components + case .b(let contentMap): + let (map, components) = try await contentMap.externallyDereferenced(with: loader) + newSchemaOrContent = .b(map) + newComponents = components + } + + let newHeader = OpenAPI.Header( + schemaOrContent: newSchemaOrContent, + description: description, + required: required, + deprecated: deprecated, + vendorExtensions: vendorExtensions + ) + + return (newHeader, newComponents) + } +} diff --git a/Sources/OpenAPIKit30/JSONReference.swift b/Sources/OpenAPIKit30/JSONReference.swift index a544ffd68..57555c304 100644 --- a/Sources/OpenAPIKit30/JSONReference.swift +++ b/Sources/OpenAPIKit30/JSONReference.swift @@ -373,4 +373,20 @@ extension JSONReference: LocallyDereferenceable where ReferenceType: LocallyDere } } +// MARK: - ExternallyDereferenceable +extension JSONReference: ExternallyDereferenceable where ReferenceType: ExternallyDereferenceable & Decodable & Equatable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + switch self { + case .internal(let ref): + return (.internal(ref), .init()) + case .external(let url): + let componentKey = try loader.componentKey(type: ReferenceType.self, at: url) + let component: ReferenceType = try await loader.load(url) + var components = OpenAPI.Components() + components[keyPath: ReferenceType.openAPIComponentsKeyPath][componentKey] = component + return (try components.reference(named: componentKey.rawValue, ofType: ReferenceType.self), components) + } + } +} + extension JSONReference: Validatable where ReferenceType: Validatable {} diff --git a/Sources/OpenAPIKit30/Link.swift b/Sources/OpenAPIKit30/Link.swift index b6585d98a..a2578ff21 100644 --- a/Sources/OpenAPIKit30/Link.swift +++ b/Sources/OpenAPIKit30/Link.swift @@ -20,18 +20,18 @@ extension OpenAPI { /// The **OpenAPI**` `operationRef` or `operationId` field, depending on whether /// a `URL` of a remote or local Operation Object or a `operationId` (String) of an /// operation defined in the same document is given. - public let operation: Either + public var operation: Either /// A map from parameter names to either runtime expressions that evaluate to values or /// constant values (`AnyCodable`). /// /// See the docuemntation for the [OpenAPI Link Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#link-object) for more details. /// /// Empty dictionaries will be omitted from encoding. - public let parameters: OrderedDictionary> + public var parameters: OrderedDictionary> /// A literal value or expression to use as a request body when calling the target operation. - public let requestBody: Either? + public var requestBody: Either? public var description: String? - public let server: Server? + public var server: Server? /// Dictionary of vendor extensions. /// @@ -278,4 +278,15 @@ extension OpenAPI.Link: LocallyDereferenceable { } } +extension OpenAPI.Link: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + let (newServer, newComponents) = try await server.externallyDereferenced(with: loader) + + var newLink = self + newLink.server = newServer + + return (newLink, newComponents) + } +} + extension OpenAPI.Link: Validatable {} diff --git a/Sources/OpenAPIKit30/Operation/DereferencedOperation.swift b/Sources/OpenAPIKit30/Operation/DereferencedOperation.swift index 05ef9dd37..f4983e6b6 100644 --- a/Sources/OpenAPIKit30/Operation/DereferencedOperation.swift +++ b/Sources/OpenAPIKit30/Operation/DereferencedOperation.swift @@ -124,3 +124,43 @@ extension OpenAPI.Operation: LocallyDereferenceable { return try DereferencedOperation(self, resolvingIn: components, following: references) } } + +extension OpenAPI.Operation: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + let oldParameters = parameters + let oldRequestBody = requestBody + let oldResponses = responses + + async let (newParameters, c1) = oldParameters.externallyDereferenced(with: loader) + async let (newRequestBody, c2) = oldRequestBody.externallyDereferenced(with: loader) + async let (newResponses, c3) = oldResponses.externallyDereferenced(with: loader) + async let (newCallbacks, c4) = callbacks.externallyDereferenced(with: loader) +// let (newServers, c6) = try await servers.externallyDereferenced(with: loader) + + var newOperation = self + var newComponents = try await c1 + + newOperation.parameters = try await newParameters + newOperation.requestBody = try await newRequestBody + try await newComponents.merge(c2) + newOperation.responses = try await newResponses + try await newComponents.merge(c3) + newOperation.callbacks = try await newCallbacks + try await newComponents.merge(c4) + + if let oldServers = servers { + let (newServers, c6) = try await oldServers.externallyDereferenced(with: loader) + newOperation.servers = newServers + try newComponents.merge(c6) + } + + // should not be necessary but current Swift compiler can't figure out conformance of ExternallyDereferenceable: + if let oldServers = servers { + let (newServers, c6) = try await oldServers.externallyDereferenced(with: loader) + newOperation.servers = newServers + try newComponents.merge(c6) + } + + return (newOperation, newComponents) + } +} diff --git a/Sources/OpenAPIKit30/Operation/Operation.swift b/Sources/OpenAPIKit30/Operation/Operation.swift index cea038bab..4ac2172e0 100644 --- a/Sources/OpenAPIKit30/Operation/Operation.swift +++ b/Sources/OpenAPIKit30/Operation/Operation.swift @@ -76,7 +76,7 @@ extension OpenAPI { /// The key is a unique identifier for the Callback Object. Each value in the /// map is a Callback Object that describes a request that may be initiated /// by the API provider and the expected responses. - public let callbacks: OpenAPI.CallbacksMap + public var callbacks: OpenAPI.CallbacksMap /// Indicates that the operation is deprecated or not. /// diff --git a/Sources/OpenAPIKit30/Parameter/DereferencedParameter.swift b/Sources/OpenAPIKit30/Parameter/DereferencedParameter.swift index f2cdf232a..2d3153847 100644 --- a/Sources/OpenAPIKit30/Parameter/DereferencedParameter.swift +++ b/Sources/OpenAPIKit30/Parameter/DereferencedParameter.swift @@ -82,3 +82,31 @@ extension OpenAPI.Parameter: LocallyDereferenceable { return try DereferencedParameter(self, resolvingIn: components, following: references, dereferencedFromComponentNamed: name) } } + +extension OpenAPI.Parameter: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + + // if not for a Swift bug, this whole function would just be the + // next line: +// let (newSchemaOrContent, components) = try await schemaOrContent.externallyDereferenced(with: loader) + + let newSchemaOrContent: Either + let newComponents: OpenAPI.Components + + switch schemaOrContent { + case .a(let schemaContext): + let (context, components) = try await schemaContext.externallyDereferenced(with: loader) + newSchemaOrContent = .a(context) + newComponents = components + case .b(let contentMap): + let (map, components) = try await contentMap.externallyDereferenced(with: loader) + newSchemaOrContent = .b(map) + newComponents = components + } + + var newParameter = self + newParameter.schemaOrContent = newSchemaOrContent + + return (newParameter, newComponents) + } +} diff --git a/Sources/OpenAPIKit30/Parameter/DereferencedSchemaContext.swift b/Sources/OpenAPIKit30/Parameter/DereferencedSchemaContext.swift index 299b728cd..70fb01f05 100644 --- a/Sources/OpenAPIKit30/Parameter/DereferencedSchemaContext.swift +++ b/Sources/OpenAPIKit30/Parameter/DereferencedSchemaContext.swift @@ -68,3 +68,24 @@ extension OpenAPI.Parameter.SchemaContext: LocallyDereferenceable { return try DereferencedSchemaContext(self, resolvingIn: components, following: references) } } + +extension OpenAPI.Parameter.SchemaContext: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + let oldSchema = schema + + async let (newSchema, c1) = oldSchema.externallyDereferenced(with: loader) + + var newSchemaContext = self + var newComponents = try await c1 + + newSchemaContext.schema = try await newSchema + + if let oldExamples = examples { + let (newExamples, c2) = try await oldExamples.externallyDereferenced(with: loader) + newSchemaContext.examples = newExamples + try newComponents.merge(c2) + } + + return (newSchemaContext, newComponents) + } +} diff --git a/Sources/OpenAPIKit30/Parameter/ParameterSchemaContext.swift b/Sources/OpenAPIKit30/Parameter/ParameterSchemaContext.swift index e44df7313..cab0963e8 100644 --- a/Sources/OpenAPIKit30/Parameter/ParameterSchemaContext.swift +++ b/Sources/OpenAPIKit30/Parameter/ParameterSchemaContext.swift @@ -13,13 +13,13 @@ extension OpenAPI.Parameter { /// See [OpenAPI Parameter Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#parameter-object) /// and [OpenAPI Style Values](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#style-values). public struct SchemaContext: Equatable { - public let style: Style - public let explode: Bool - public let allowReserved: Bool //defaults to false - public let schema: Either, JSONSchema> + public var style: Style + public var explode: Bool + public var allowReserved: Bool //defaults to false + public var schema: Either, JSONSchema> - public let example: AnyCodable? - public let examples: OpenAPI.Example.Map? + public var example: AnyCodable? + public var examples: OpenAPI.Example.Map? public init(_ schema: JSONSchema, style: Style, diff --git a/Sources/OpenAPIKit30/Path Item/DereferencedPathItem.swift b/Sources/OpenAPIKit30/Path Item/DereferencedPathItem.swift index 93767b025..24af18f91 100644 --- a/Sources/OpenAPIKit30/Path Item/DereferencedPathItem.swift +++ b/Sources/OpenAPIKit30/Path Item/DereferencedPathItem.swift @@ -137,3 +137,93 @@ extension OpenAPI.PathItem: LocallyDereferenceable { return try DereferencedPathItem(self, resolvingIn: components, following: references, dereferencedFromComponentNamed: name) } } + +extension OpenAPI.PathItem: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + let oldParameters = parameters + let oldServers = servers + let oldGet = get + let oldPut = put + let oldPost = post + let oldDelete = delete + let oldOptions = options + let oldHead = head + let oldPatch = patch + let oldTrace = trace + + async let (newParameters, c1) = oldParameters.externallyDereferenced(with: loader) +// async let (newServers, c2) = oldServers.externallyDereferenced(with: loader) +// async let (newGet, c3) = oldGet.externallyDereferenced(with: loader) +// async let (newPut, c4) = oldPut.externallyDereferenced(with: loader) +// async let (newPost, c5) = oldPost.externallyDereferenced(with: loader) +// async let (newDelete, c6) = oldDelete.externallyDereferenced(with: loader) +// async let (newOptions, c7) = oldOptions.externallyDereferenced(with: loader) +// async let (newHead, c8) = oldHead.externallyDereferenced(with: loader) +// async let (newPatch, c9) = oldPatch.externallyDereferenced(with: loader) +// async let (newTrace, c10) = oldTrace.externallyDereferenced(with: loader) + + var pathItem = self + var newComponents = try await c1 + + // ideally we would async let all of the props above and then set them here, + // but for now since there seems to be some sort of compiler bug we will do + // most of them in if lets below + pathItem.parameters = try await newParameters + + if let oldServers { + async let (newServers, c2) = oldServers.externallyDereferenced(with: loader) + pathItem.servers = try await newServers + try await newComponents.merge(c2) + } + + if let oldGet { + async let (newGet, c3) = oldGet.externallyDereferenced(with: loader) + pathItem.get = try await newGet + try await newComponents.merge(c3) + } + + if let oldPut { + async let (newPut, c4) = oldPut.externallyDereferenced(with: loader) + pathItem.put = try await newPut + try await newComponents.merge(c4) + } + + if let oldPost { + async let (newPost, c5) = oldPost.externallyDereferenced(with: loader) + pathItem.post = try await newPost + try await newComponents.merge(c5) + } + + if let oldDelete { + async let (newDelete, c6) = oldDelete.externallyDereferenced(with: loader) + pathItem.delete = try await newDelete + try await newComponents.merge(c6) + } + + if let oldOptions { + async let (newOptions, c7) = oldOptions.externallyDereferenced(with: loader) + pathItem.options = try await newOptions + try await newComponents.merge(c7) + } + + if let oldHead { + async let (newHead, c8) = oldHead.externallyDereferenced(with: loader) + pathItem.head = try await newHead + try await newComponents.merge(c8) + } + + if let oldPatch { + async let (newPatch, c9) = oldPatch.externallyDereferenced(with: loader) + pathItem.patch = try await newPatch + try await newComponents.merge(c9) + } + + if let oldTrace { + async let (newTrace, c10) = oldTrace.externallyDereferenced(with: loader) + pathItem.trace = try await newTrace + try await newComponents.merge(c10) + } + + return (pathItem, newComponents) + } +} diff --git a/Sources/OpenAPIKit30/Request/DereferencedRequest.swift b/Sources/OpenAPIKit30/Request/DereferencedRequest.swift index 454e4c7aa..48c067e3d 100644 --- a/Sources/OpenAPIKit30/Request/DereferencedRequest.swift +++ b/Sources/OpenAPIKit30/Request/DereferencedRequest.swift @@ -61,3 +61,14 @@ extension OpenAPI.Request: LocallyDereferenceable { return try DereferencedRequest(self, resolvingIn: components, following: references, dereferencedFromComponentNamed: name) } } + +extension OpenAPI.Request: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + var newRequest = self + + let (newContent, components) = try await content.externallyDereferenced(with: loader) + + newRequest.content = newContent + return (newRequest, components) + } +} diff --git a/Sources/OpenAPIKit30/Response/DereferencedResponse.swift b/Sources/OpenAPIKit30/Response/DereferencedResponse.swift index c61ae5c7b..3a8e7bdb6 100644 --- a/Sources/OpenAPIKit30/Response/DereferencedResponse.swift +++ b/Sources/OpenAPIKit30/Response/DereferencedResponse.swift @@ -76,3 +76,28 @@ extension OpenAPI.Response: LocallyDereferenceable { return try DereferencedResponse(self, resolvingIn: components, following: references, dereferencedFromComponentNamed: name) } } + +extension OpenAPI.Response: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + let oldContent = content + let oldLinks = links + + async let (newContent, c1) = oldContent.externallyDereferenced(with: loader) + async let (newLinks, c2) = oldLinks.externallyDereferenced(with: loader) + + var response = self + response.content = try await newContent + response.links = try await newLinks + + var components = try await c1 + try await components.merge(c2) + + if let oldHeaders = headers { + let (newHeaders, c3) = try await oldHeaders.externallyDereferenced(with: loader) + response.headers = newHeaders + try components.merge(c3) + } + + return (response, components) + } +} diff --git a/Sources/OpenAPIKit30/Schema Object/DereferencedJSONSchema.swift b/Sources/OpenAPIKit30/Schema Object/DereferencedJSONSchema.swift index 07af9bfb1..e841e0d6b 100644 --- a/Sources/OpenAPIKit30/Schema Object/DereferencedJSONSchema.swift +++ b/Sources/OpenAPIKit30/Schema Object/DereferencedJSONSchema.swift @@ -408,3 +408,100 @@ extension JSONSchema: LocallyDereferenceable { return try? dereferenced(in: .noComponents) } } + +extension JSONSchema: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + let newSchema: JSONSchema + let newComponents: OpenAPI.Components + + switch value { + case .boolean(_): + newComponents = .noComponents + newSchema = self + case .number(_, _): + newComponents = .noComponents + newSchema = self + case .integer(_, _): + newComponents = .noComponents + newSchema = self + case .string(_, _): + newComponents = .noComponents + newSchema = self + case .object(let core, let object): + var components = OpenAPI.Components() + + let (newProperties, c1) = try await object.properties.externallyDereferenced(with: loader) + try components.merge(c1) + + let newAdditionalProperties: Either? + if case .b(let schema) = object.additionalProperties { + let (additionalProperties, c2) = try await schema.externallyDereferenced(with: loader) + try components.merge(c2) + newAdditionalProperties = .b(additionalProperties) + } else { + newAdditionalProperties = object.additionalProperties + } + newComponents = components + newSchema = .init( + schema: .object( + core, + .init( + properties: newProperties, + additionalProperties: newAdditionalProperties, + maxProperties: object.maxProperties, + minProperties: object._minProperties + ) + ) + ) + case .array(let core, let array): + let (newItems, components) = try await array.items.externallyDereferenced(with: loader) + newComponents = components + newSchema = .init( + schema: .array( + core, + .init( + items: newItems, + maxItems: array.maxItems, + minItems: array._minItems, + uniqueItems: array._uniqueItems + ) + ) + ) + case .all(let schema, let core): + let (newSubschemas, components) = try await schema.externallyDereferenced(with: loader) + newComponents = components + newSchema = .init( + schema: .all(of: newSubschemas, core: core) + ) + case .one(let schema, let core): + let (newSubschemas, components) = try await schema.externallyDereferenced(with: loader) + newComponents = components + newSchema = .init( + schema: .one(of: newSubschemas, core: core) + ) + case .any(let schema, let core): + let (newSubschemas, components) = try await schema.externallyDereferenced(with: loader) + newComponents = components + newSchema = .init( + schema: .any(of: newSubschemas, core: core) + ) + case .not(let schema, let core): + let (newSubschema, components) = try await schema.externallyDereferenced(with: loader) + newComponents = components + newSchema = .init( + schema: .not(newSubschema, core: core) + ) + case .reference(let reference, let core): + let (newReference, components) = try await reference.externallyDereferenced(with: loader) + newComponents = components + newSchema = .init( + schema: .reference(newReference, core) + ) + case .fragment(_): + newComponents = .noComponents + newSchema = self + } + + return (newSchema, newComponents) + } +} diff --git a/Sources/OpenAPIKit30/Security/SecurityScheme.swift b/Sources/OpenAPIKit30/Security/SecurityScheme.swift index 8b883491e..38db6f488 100644 --- a/Sources/OpenAPIKit30/Security/SecurityScheme.swift +++ b/Sources/OpenAPIKit30/Security/SecurityScheme.swift @@ -251,4 +251,10 @@ extension OpenAPI.SecurityScheme: LocallyDereferenceable { } } +extension OpenAPI.SecurityScheme: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + return (self, .init()) + } +} + extension OpenAPI.SecurityScheme.SecurityType.Name: Validatable {} diff --git a/Sources/OpenAPIKit30/Server.swift b/Sources/OpenAPIKit30/Server.swift index f0073f827..94937a341 100644 --- a/Sources/OpenAPIKit30/Server.swift +++ b/Sources/OpenAPIKit30/Server.swift @@ -245,5 +245,11 @@ extension OpenAPI.Server.Variable { } } +extension OpenAPI.Server: ExternallyDereferenceable { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + return (self, .init()) + } +} + extension OpenAPI.Server: Validatable {} extension OpenAPI.Server.Variable: Validatable {} diff --git a/Sources/OpenAPIKit30/Utility/Array+ExternallyDereferenceable.swift b/Sources/OpenAPIKit30/Utility/Array+ExternallyDereferenceable.swift new file mode 100644 index 000000000..59970df82 --- /dev/null +++ b/Sources/OpenAPIKit30/Utility/Array+ExternallyDereferenceable.swift @@ -0,0 +1,27 @@ +// +// Array+ExternallyDereferenceable.swift +// + +import OpenAPIKitCore + +extension Array where Element: ExternallyDereferenceable { + + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + try await withThrowingTaskGroup(of: (Element, OpenAPI.Components).self) { group in + for elem in self { + group.addTask { + return try await elem.externallyDereferenced(with: loader) + } + } + + var newElems = Self() + var newComponents = OpenAPI.Components() + + for try await (elem, components) in group { + newElems.append(elem) + try newComponents.merge(components) + } + return (newElems, newComponents) + } + } +} diff --git a/Sources/OpenAPIKit30/Utility/Dictionary+ExternallyDereferenceable.swift b/Sources/OpenAPIKit30/Utility/Dictionary+ExternallyDereferenceable.swift new file mode 100644 index 000000000..75a4cb0b0 --- /dev/null +++ b/Sources/OpenAPIKit30/Utility/Dictionary+ExternallyDereferenceable.swift @@ -0,0 +1,29 @@ +// +// Dictionary+ExternallyDereferenceable.swift +// OpenAPI +// + +import OpenAPIKitCore + +extension Dictionary where Value: ExternallyDereferenceable { + + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + try await withThrowingTaskGroup(of: (Key, Value, OpenAPI.Components).self) { group in + for (key, value) in self { + group.addTask { + let (newRef, components) = try await value.externallyDereferenced(with: loader) + return (key, newRef, components) + } + } + + var newDict = Self() + var newComponents = OpenAPI.Components() + + for try await (key, newRef, components) in group { + newDict[key] = newRef + try newComponents.merge(components) + } + return (newDict, newComponents) + } + } +} diff --git a/Sources/OpenAPIKit30/Utility/Optional+ExternallyDereferenceable.swift b/Sources/OpenAPIKit30/Utility/Optional+ExternallyDereferenceable.swift new file mode 100644 index 000000000..671657f86 --- /dev/null +++ b/Sources/OpenAPIKit30/Utility/Optional+ExternallyDereferenceable.swift @@ -0,0 +1,13 @@ +// +// Optional+ExternallyDereferenceable.swift +// + +import OpenAPIKitCore + +extension Optional where Wrapped: ExternallyDereferenceable { + + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + guard let wrapped = self else { return (nil, .init()) } + return try await wrapped.externallyDereferenced(with: loader) + } +} diff --git a/Sources/OpenAPIKit30/Utility/OrderedDictionary+ExternallyDereferenceable.swift b/Sources/OpenAPIKit30/Utility/OrderedDictionary+ExternallyDereferenceable.swift new file mode 100644 index 000000000..94718b0a6 --- /dev/null +++ b/Sources/OpenAPIKit30/Utility/OrderedDictionary+ExternallyDereferenceable.swift @@ -0,0 +1,31 @@ +// +// OrderedDictionary+ExternallyDereferenceable.swift +// OpenAPI +// +// Created by Mathew Polzin on 08/05/2023. +// + +import OpenAPIKitCore + +extension OrderedDictionary where Value: ExternallyDereferenceable { + + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + try await withThrowingTaskGroup(of: (Key, Value, OpenAPI.Components).self) { group in + for (key, value) in self { + group.addTask { + let (newRef, components) = try await value.externallyDereferenced(with: loader) + return (key, newRef, components) + } + } + + var newDict = Self() + var newComponents = OpenAPI.Components() + + for try await (key, newRef, components) in group { + newDict[key] = newRef + try newComponents.merge(components) + } + return (newDict, newComponents) + } + } +} From 6d6be196945f651dfa74de6d94603ee14155b0a8 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 18 Apr 2024 09:00:46 -0500 Subject: [PATCH 034/110] remove unnecessary change --- Sources/OpenAPIKit/JSONReference.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/OpenAPIKit/JSONReference.swift b/Sources/OpenAPIKit/JSONReference.swift index d3f4fb72e..259ae77ca 100644 --- a/Sources/OpenAPIKit/JSONReference.swift +++ b/Sources/OpenAPIKit/JSONReference.swift @@ -511,7 +511,7 @@ extension OpenAPI.Reference: Decodable { } // MARK: - LocallyDereferenceable -extension JSONReference: LocallyDereferenceable where ReferenceType: LocallyDereferenceable & Decodable & Equatable { +extension JSONReference: LocallyDereferenceable where ReferenceType: LocallyDereferenceable { /// Look up the component this reference points to and then /// dereference it. /// @@ -552,7 +552,7 @@ extension JSONReference: ExternallyDereferenceable where ReferenceType: External } } -extension OpenAPI.Reference: LocallyDereferenceable where ReferenceType: LocallyDereferenceable & Decodable & Equatable { +extension OpenAPI.Reference: LocallyDereferenceable where ReferenceType: LocallyDereferenceable { /// Look up the component this reference points to and then /// dereference it. /// From 3fc8750d49af0603bed2fcc987451bf32e171e08 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 18 Apr 2024 10:37:04 -0500 Subject: [PATCH 035/110] rename ExternalLoaderContext to ExternalLoader --- Sources/OpenAPIKit/Callbacks.swift | 2 +- .../OpenAPIKit/Components Object/Components.swift | 2 +- .../OpenAPIKit/Content/DereferencedContent.swift | 2 +- .../Content/DereferencedContentEncoding.swift | 2 +- Sources/OpenAPIKit/Document/Document.swift | 2 +- .../Either/Either+ExternallyDereferenceable.swift | 2 +- Sources/OpenAPIKit/Example.swift | 2 +- Sources/OpenAPIKit/ExternalLoader.swift | 6 +++--- Sources/OpenAPIKit/Header/DereferencedHeader.swift | 2 +- Sources/OpenAPIKit/JSONReference.swift | 4 ++-- Sources/OpenAPIKit/Link.swift | 2 +- .../Operation/DereferencedOperation.swift | 2 +- .../Parameter/DereferencedParameter.swift | 2 +- .../Parameter/DereferencedSchemaContext.swift | 2 +- .../Path Item/DereferencedPathItem.swift | 2 +- .../OpenAPIKit/Request/DereferencedRequest.swift | 2 +- .../OpenAPIKit/Response/DereferencedResponse.swift | 2 +- .../Schema Object/DereferencedJSONSchema.swift | 2 +- Sources/OpenAPIKit/Security/SecurityScheme.swift | 2 +- Sources/OpenAPIKit/Server.swift | 2 +- .../Utility/Array+ExternallyDereferenceable.swift | 2 +- .../Dictionary+ExternallyDereferenceable.swift | 2 +- .../Optional+ExternallyDereferenceable.swift | 2 +- ...deredDictionary+ExternallyDereferenceable.swift | 2 +- Tests/OpenAPIKitTests/Document/DocumentTests.swift | 2 +- Tests/OpenAPIKitTests/JSONReferenceTests.swift | 14 ++++++++++++++ .../OpenAPIKitTests/Path Item/PathItemTests.swift | 2 +- 27 files changed, 43 insertions(+), 29 deletions(-) diff --git a/Sources/OpenAPIKit/Callbacks.swift b/Sources/OpenAPIKit/Callbacks.swift index 407c495f3..30baf3e3d 100644 --- a/Sources/OpenAPIKit/Callbacks.swift +++ b/Sources/OpenAPIKit/Callbacks.swift @@ -38,7 +38,7 @@ extension OpenAPI.CallbackURL: LocallyDereferenceable { } extension OpenAPI.CallbackURL: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { return (self, .init()) } } diff --git a/Sources/OpenAPIKit/Components Object/Components.swift b/Sources/OpenAPIKit/Components Object/Components.swift index eec77534f..0c2798b87 100644 --- a/Sources/OpenAPIKit/Components Object/Components.swift +++ b/Sources/OpenAPIKit/Components Object/Components.swift @@ -291,7 +291,7 @@ extension OpenAPI.Components { } extension OpenAPI.Components { - internal mutating func externallyDereference(in context: Context.Type) async throws { + internal mutating func externallyDereference(in context: Context.Type) async throws { let oldSchemas = schemas let oldResponses = responses let oldParameters = parameters diff --git a/Sources/OpenAPIKit/Content/DereferencedContent.swift b/Sources/OpenAPIKit/Content/DereferencedContent.swift index 18d02f624..992eea1a7 100644 --- a/Sources/OpenAPIKit/Content/DereferencedContent.swift +++ b/Sources/OpenAPIKit/Content/DereferencedContent.swift @@ -77,7 +77,7 @@ extension OpenAPI.Content: LocallyDereferenceable { } extension OpenAPI.Content: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { let oldSchema = schema async let (newSchema, c1) = oldSchema.externallyDereferenced(with: loader) diff --git a/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift b/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift index 6100b01ce..f6d43cb5e 100644 --- a/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift +++ b/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift @@ -58,7 +58,7 @@ extension OpenAPI.Content.Encoding: LocallyDereferenceable { } extension OpenAPI.Content.Encoding: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { let newHeaders: OpenAPI.Header.Map? let newComponents: OpenAPI.Components diff --git a/Sources/OpenAPIKit/Document/Document.swift b/Sources/OpenAPIKit/Document/Document.swift index 5f5cf19c8..5807b265f 100644 --- a/Sources/OpenAPIKit/Document/Document.swift +++ b/Sources/OpenAPIKit/Document/Document.swift @@ -351,7 +351,7 @@ extension OpenAPI.Document { return try DereferencedDocument(self) } - public mutating func externallyDereference(in context: Context.Type) async throws { + public mutating func externallyDereference(in context: Context.Type) async throws { let oldPaths = paths let oldWebhooks = webhooks diff --git a/Sources/OpenAPIKit/Either/Either+ExternallyDereferenceable.swift b/Sources/OpenAPIKit/Either/Either+ExternallyDereferenceable.swift index 10e56b368..5dfe12868 100644 --- a/Sources/OpenAPIKit/Either/Either+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit/Either/Either+ExternallyDereferenceable.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore // MARK: - ExternallyDereferenceable extension Either: ExternallyDereferenceable where A: ExternallyDereferenceable, B: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { switch self { case .a(let a): let (newA, components) = try await a.externallyDereferenced(with: loader) diff --git a/Sources/OpenAPIKit/Example.swift b/Sources/OpenAPIKit/Example.swift index 8831c6d97..d61fc8392 100644 --- a/Sources/OpenAPIKit/Example.swift +++ b/Sources/OpenAPIKit/Example.swift @@ -209,7 +209,7 @@ extension OpenAPI.Example: LocallyDereferenceable { } extension OpenAPI.Example: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { return (self, .init()) } } diff --git a/Sources/OpenAPIKit/ExternalLoader.swift b/Sources/OpenAPIKit/ExternalLoader.swift index f937ac917..2b62c1699 100644 --- a/Sources/OpenAPIKit/ExternalLoader.swift +++ b/Sources/OpenAPIKit/ExternalLoader.swift @@ -8,10 +8,10 @@ import OpenAPIKitCore import Foundation -/// An `ExternalLoaderContext` enables `OpenAPIKit` to load external references +/// An `ExternalLoader` enables `OpenAPIKit` to load external references /// without knowing the details of what decoder is being used or how new internal /// references should be named. -public protocol ExternalLoaderContext { +public protocol ExternalLoader { /// Load the given URL and decode it as Type `T`. All Types `T` are `Decodable`, so /// the only real responsibility of a `load` function is to locate and load the given /// `URL` and pass its `Data` or `String` (depending on the decoder) to an appropriate @@ -30,5 +30,5 @@ public protocol ExternalLoaderContext { } public protocol ExternallyDereferenceable { - func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) + func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) } diff --git a/Sources/OpenAPIKit/Header/DereferencedHeader.swift b/Sources/OpenAPIKit/Header/DereferencedHeader.swift index 891331af9..554a5b266 100644 --- a/Sources/OpenAPIKit/Header/DereferencedHeader.swift +++ b/Sources/OpenAPIKit/Header/DereferencedHeader.swift @@ -84,7 +84,7 @@ extension OpenAPI.Header: LocallyDereferenceable { } extension OpenAPI.Header: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { // if not for a Swift bug, this whole next bit would just be the // next line: diff --git a/Sources/OpenAPIKit/JSONReference.swift b/Sources/OpenAPIKit/JSONReference.swift index 259ae77ca..75621e00a 100644 --- a/Sources/OpenAPIKit/JSONReference.swift +++ b/Sources/OpenAPIKit/JSONReference.swift @@ -538,7 +538,7 @@ extension JSONReference: LocallyDereferenceable where ReferenceType: LocallyDere } extension JSONReference: ExternallyDereferenceable where ReferenceType: ExternallyDereferenceable & Decodable & Equatable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { switch self { case .internal(let ref): return (.internal(ref), .init()) @@ -580,7 +580,7 @@ extension OpenAPI.Reference: LocallyDereferenceable where ReferenceType: Locally } extension OpenAPI.Reference: ExternallyDereferenceable where ReferenceType: ExternallyDereferenceable & Decodable & Equatable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { let (newRef, components) = try await jsonReference.externallyDereferenced(with: loader) return (.init(newRef), components) } diff --git a/Sources/OpenAPIKit/Link.swift b/Sources/OpenAPIKit/Link.swift index d79316bad..be416dcff 100644 --- a/Sources/OpenAPIKit/Link.swift +++ b/Sources/OpenAPIKit/Link.swift @@ -290,7 +290,7 @@ extension OpenAPI.Link: LocallyDereferenceable { } extension OpenAPI.Link: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { let (newServer, newComponents) = try await server.externallyDereferenced(with: loader) var newLink = self diff --git a/Sources/OpenAPIKit/Operation/DereferencedOperation.swift b/Sources/OpenAPIKit/Operation/DereferencedOperation.swift index 8f7844694..dfdd14c9d 100644 --- a/Sources/OpenAPIKit/Operation/DereferencedOperation.swift +++ b/Sources/OpenAPIKit/Operation/DereferencedOperation.swift @@ -126,7 +126,7 @@ extension OpenAPI.Operation: LocallyDereferenceable { } extension OpenAPI.Operation: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { let oldParameters = parameters let oldRequestBody = requestBody let oldResponses = responses diff --git a/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift b/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift index 2d3153847..b2b6b9604 100644 --- a/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift +++ b/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift @@ -84,7 +84,7 @@ extension OpenAPI.Parameter: LocallyDereferenceable { } extension OpenAPI.Parameter: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { // if not for a Swift bug, this whole function would just be the // next line: diff --git a/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift b/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift index 00cfaa7af..cea39b921 100644 --- a/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift +++ b/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift @@ -71,7 +71,7 @@ extension OpenAPI.Parameter.SchemaContext: LocallyDereferenceable { } extension OpenAPI.Parameter.SchemaContext: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { let oldSchema = schema async let (newSchema, c1) = oldSchema.externallyDereferenced(with: loader) diff --git a/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift b/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift index 6f4727f45..fc284b1df 100644 --- a/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift +++ b/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift @@ -140,7 +140,7 @@ extension OpenAPI.PathItem: LocallyDereferenceable { } extension OpenAPI.PathItem: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { let oldParameters = parameters let oldServers = servers let oldGet = get diff --git a/Sources/OpenAPIKit/Request/DereferencedRequest.swift b/Sources/OpenAPIKit/Request/DereferencedRequest.swift index 27ee92d0b..a5dce43bd 100644 --- a/Sources/OpenAPIKit/Request/DereferencedRequest.swift +++ b/Sources/OpenAPIKit/Request/DereferencedRequest.swift @@ -63,7 +63,7 @@ extension OpenAPI.Request: LocallyDereferenceable { } extension OpenAPI.Request: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { var newRequest = self let (newContent, components) = try await content.externallyDereferenced(with: loader) diff --git a/Sources/OpenAPIKit/Response/DereferencedResponse.swift b/Sources/OpenAPIKit/Response/DereferencedResponse.swift index 3a8e7bdb6..363e33448 100644 --- a/Sources/OpenAPIKit/Response/DereferencedResponse.swift +++ b/Sources/OpenAPIKit/Response/DereferencedResponse.swift @@ -78,7 +78,7 @@ extension OpenAPI.Response: LocallyDereferenceable { } extension OpenAPI.Response: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { let oldContent = content let oldLinks = links diff --git a/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift b/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift index 6cfef7a2c..faeccde28 100644 --- a/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift +++ b/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift @@ -536,7 +536,7 @@ extension JSONSchema: LocallyDereferenceable { } extension JSONSchema: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { let newSchema: JSONSchema let newComponents: OpenAPI.Components diff --git a/Sources/OpenAPIKit/Security/SecurityScheme.swift b/Sources/OpenAPIKit/Security/SecurityScheme.swift index 2429466c1..a304bccfd 100644 --- a/Sources/OpenAPIKit/Security/SecurityScheme.swift +++ b/Sources/OpenAPIKit/Security/SecurityScheme.swift @@ -275,7 +275,7 @@ extension OpenAPI.SecurityScheme: LocallyDereferenceable { } extension OpenAPI.SecurityScheme: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { return (self, .init()) } } diff --git a/Sources/OpenAPIKit/Server.swift b/Sources/OpenAPIKit/Server.swift index ad9bf6094..4bea3607f 100644 --- a/Sources/OpenAPIKit/Server.swift +++ b/Sources/OpenAPIKit/Server.swift @@ -260,7 +260,7 @@ extension OpenAPI.Server.Variable { } extension OpenAPI.Server: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { return (self, .init()) } } diff --git a/Sources/OpenAPIKit/Utility/Array+ExternallyDereferenceable.swift b/Sources/OpenAPIKit/Utility/Array+ExternallyDereferenceable.swift index 59970df82..bb570c7b8 100644 --- a/Sources/OpenAPIKit/Utility/Array+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit/Utility/Array+ExternallyDereferenceable.swift @@ -6,7 +6,7 @@ import OpenAPIKitCore extension Array where Element: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { try await withThrowingTaskGroup(of: (Element, OpenAPI.Components).self) { group in for elem in self { group.addTask { diff --git a/Sources/OpenAPIKit/Utility/Dictionary+ExternallyDereferenceable.swift b/Sources/OpenAPIKit/Utility/Dictionary+ExternallyDereferenceable.swift index 75a4cb0b0..2ba0a8fcb 100644 --- a/Sources/OpenAPIKit/Utility/Dictionary+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit/Utility/Dictionary+ExternallyDereferenceable.swift @@ -7,7 +7,7 @@ import OpenAPIKitCore extension Dictionary where Value: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { try await withThrowingTaskGroup(of: (Key, Value, OpenAPI.Components).self) { group in for (key, value) in self { group.addTask { diff --git a/Sources/OpenAPIKit/Utility/Optional+ExternallyDereferenceable.swift b/Sources/OpenAPIKit/Utility/Optional+ExternallyDereferenceable.swift index 671657f86..adf2b1816 100644 --- a/Sources/OpenAPIKit/Utility/Optional+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit/Utility/Optional+ExternallyDereferenceable.swift @@ -6,7 +6,7 @@ import OpenAPIKitCore extension Optional where Wrapped: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { guard let wrapped = self else { return (nil, .init()) } return try await wrapped.externallyDereferenced(with: loader) } diff --git a/Sources/OpenAPIKit/Utility/OrderedDictionary+ExternallyDereferenceable.swift b/Sources/OpenAPIKit/Utility/OrderedDictionary+ExternallyDereferenceable.swift index 94718b0a6..037cdabc0 100644 --- a/Sources/OpenAPIKit/Utility/OrderedDictionary+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit/Utility/OrderedDictionary+ExternallyDereferenceable.swift @@ -9,7 +9,7 @@ import OpenAPIKitCore extension OrderedDictionary where Value: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { try await withThrowingTaskGroup(of: (Key, Value, OpenAPI.Components).self) { group in for (key, value) in self { group.addTask { diff --git a/Tests/OpenAPIKitTests/Document/DocumentTests.swift b/Tests/OpenAPIKitTests/Document/DocumentTests.swift index 1a5e0ae15..a2d02698b 100644 --- a/Tests/OpenAPIKitTests/Document/DocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/DocumentTests.swift @@ -1179,7 +1179,7 @@ extension DocumentTests { /// An example of implementing a loader context for loading external references /// into an OpenAPI document. - struct ExampleLoaderContext: ExternalLoaderContext { + struct ExampleLoaderContext: ExternalLoader { static func load(_ url: URL) async throws -> T where T : Decodable { // load data from file, perhaps. we will just mock that up for the example: let data = await mockParameterData(url) diff --git a/Tests/OpenAPIKitTests/JSONReferenceTests.swift b/Tests/OpenAPIKitTests/JSONReferenceTests.swift index 53c7a8b64..13b385f5c 100644 --- a/Tests/OpenAPIKitTests/JSONReferenceTests.swift +++ b/Tests/OpenAPIKitTests/JSONReferenceTests.swift @@ -377,9 +377,23 @@ extension JSONReferenceTests { } } +// MARK: - External Dereferencing +extension JSONReferenceTests { + +} + // MARK: - Test Types extension JSONReferenceTests { struct ReferenceWrapper: Codable, Equatable { let reference: JSONReference } + + struct SchemaLoader: ExternalLoader { + static func load(_ url: URL) -> T where T: Decodable { + return JSONSchema.string as! T + } + + static func componentKey(type: T.Type, at url: URL) throws -> OpenAPI.ComponentKey { + } + } } diff --git a/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift b/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift index ebd4188dc..62ddd9a83 100644 --- a/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift +++ b/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift @@ -436,7 +436,7 @@ extension PathItemTests { // MARK: External Dereferencing Tests extension PathItemTests { - struct MockLoad: ExternalLoaderContext { + struct MockLoad: ExternalLoader { static func componentKey(type: T.Type, at url: URL) throws -> OpenAPI.ComponentKey { let urlString = url.pathComponents.dropFirst().joined(separator: "_").replacingOccurrences(of: ".", with: "_") From 47af3484da9f04948f9c10cf0ff206c5e096bbcd Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 18 Apr 2024 11:01:12 -0500 Subject: [PATCH 036/110] fill in some actual external dereferencing tests for json references --- .../OpenAPIKitTests/JSONReferenceTests.swift | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/Tests/OpenAPIKitTests/JSONReferenceTests.swift b/Tests/OpenAPIKitTests/JSONReferenceTests.swift index 13b385f5c..c5e74f18f 100644 --- a/Tests/OpenAPIKitTests/JSONReferenceTests.swift +++ b/Tests/OpenAPIKitTests/JSONReferenceTests.swift @@ -379,7 +379,32 @@ extension JSONReferenceTests { // MARK: - External Dereferencing extension JSONReferenceTests { - + func test_externalDerefNoFragment() async throws { + let reference: JSONReference = .external(.init(string: "./schema.json")!) + + let (newReference, components) = try await reference.externallyDereferenced(with: SchemaLoader.self) + + XCTAssertEqual(newReference, .component(named: "__schema_json")) + XCTAssertEqual(components, .init(schemas: ["__schema_json": .string])) + } + + func test_externalDerefFragment() async throws { + let reference: JSONReference = .external(.init(string: "./schema.json#/test")!) + + let (newReference, components) = try await reference.externallyDereferenced(with: SchemaLoader.self) + + XCTAssertEqual(newReference, .component(named: "__schema_json__test")) + XCTAssertEqual(components, .init(schemas: ["__schema_json__test": .string])) + } + + func test_externalDerefExternalComponents() async throws { + let reference: JSONReference = .external(.init(string: "./schema.json#/components/schemas/test")!) + + let (newReference, components) = try await reference.externallyDereferenced(with: SchemaLoader.self) + + XCTAssertEqual(newReference, .component(named: "__schema_json__components_schemas_test")) + XCTAssertEqual(components, .init(schemas: ["__schema_json__components_schemas_test": .string])) + } } // MARK: - Test Types @@ -394,6 +419,10 @@ extension JSONReferenceTests { } static func componentKey(type: T.Type, at url: URL) throws -> OpenAPI.ComponentKey { + return try .forceInit(rawValue: url.absoluteString + .replacingOccurrences(of: "/", with: "_") + .replacingOccurrences(of: "#", with: "_") + .replacingOccurrences(of: ".", with: "_")) } } } From f11d2cd1f6fab0fc257efff5c59548f1e46c6fb9 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sun, 21 Apr 2024 13:51:34 -0500 Subject: [PATCH 037/110] start building a Document test for external dereferencing. fix component collisions to not occur for identical components --- .../Components Object/Components.swift | 29 +++++++----- Sources/OpenAPIKit/Parameter/Parameter.swift | 2 + .../Document/DocumentTests.swift | 46 ++++++++++++++----- 3 files changed, 54 insertions(+), 23 deletions(-) diff --git a/Sources/OpenAPIKit/Components Object/Components.swift b/Sources/OpenAPIKit/Components Object/Components.swift index 0c2798b87..0cc169a99 100644 --- a/Sources/OpenAPIKit/Components Object/Components.swift +++ b/Sources/OpenAPIKit/Components Object/Components.swift @@ -78,18 +78,25 @@ extension OpenAPI.Components { public let newComponent: String } + private func detectCollision(type: String) throws -> (_ old: T, _ new: T) throws -> T { + return { old, new in + if(old == new) { return old } + throw ComponentCollision(componentType: type, existingComponent: String(describing:old), newComponent: String(describing:new)) + } + } + public mutating func merge(_ other: OpenAPI.Components) throws { - try schemas.merge(other.schemas, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "schema", existingComponent: String(describing: a), newComponent: String(describing: b)) }) - try responses.merge(other.responses, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "responses", existingComponent: String(describing: a), newComponent: String(describing: b)) }) - try parameters.merge(other.parameters, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "parameters", existingComponent: String(describing: a), newComponent: String(describing: b)) }) - try examples.merge(other.examples, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "examples", existingComponent: String(describing: a), newComponent: String(describing: b)) }) - try requestBodies.merge(other.requestBodies, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "requestBodies", existingComponent: String(describing: a), newComponent: String(describing: b)) }) - try headers.merge(other.headers, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "headers", existingComponent: String(describing: a), newComponent: String(describing: b)) }) - try securitySchemes.merge(other.securitySchemes, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "securitySchemes", existingComponent: String(describing: a), newComponent: String(describing: b)) }) - try links.merge(other.links, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "links", existingComponent: String(describing: a), newComponent: String(describing: b)) }) - try callbacks.merge(other.callbacks, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "callbacks", existingComponent: String(describing: a), newComponent: String(describing: b)) }) - try pathItems.merge(other.pathItems, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "pathItems", existingComponent: String(describing: a), newComponent: String(describing: b)) }) - try vendorExtensions.merge(other.vendorExtensions, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "vendorExtensions", existingComponent: String(describing: a), newComponent: String(describing: b)) }) + try schemas.merge(other.schemas, uniquingKeysWith: detectCollision(type: "schema")) + try responses.merge(other.responses, uniquingKeysWith: detectCollision(type: "responses")) + try parameters.merge(other.parameters, uniquingKeysWith: detectCollision(type: "parameters")) + try examples.merge(other.examples, uniquingKeysWith: detectCollision(type: "examples")) + try requestBodies.merge(other.requestBodies, uniquingKeysWith: detectCollision(type: "requestBodies")) + try headers.merge(other.headers, uniquingKeysWith: detectCollision(type: "headers")) + try securitySchemes.merge(other.securitySchemes, uniquingKeysWith: detectCollision(type: "securitySchemes")) + try links.merge(other.links, uniquingKeysWith: detectCollision(type: "links")) + try callbacks.merge(other.callbacks, uniquingKeysWith: detectCollision(type: "callbacks")) + try pathItems.merge(other.pathItems, uniquingKeysWith: detectCollision(type: "pathItems")) + try vendorExtensions.merge(other.vendorExtensions, uniquingKeysWith: detectCollision(type: "vendorExtensions")) } } diff --git a/Sources/OpenAPIKit/Parameter/Parameter.swift b/Sources/OpenAPIKit/Parameter/Parameter.swift index a65dfcb0c..9c4cf5611 100644 --- a/Sources/OpenAPIKit/Parameter/Parameter.swift +++ b/Sources/OpenAPIKit/Parameter/Parameter.swift @@ -21,6 +21,8 @@ extension OpenAPI { /// parameters in the given location. public var context: Context public var description: String? + /// Whether or not the parameter is deprecated. Defaults to false + /// if unspecified and only gets encoded if true. public var deprecated: Bool // default is false /// OpenAPI Spec "content" or "schema" properties. diff --git a/Tests/OpenAPIKitTests/Document/DocumentTests.swift b/Tests/OpenAPIKitTests/Document/DocumentTests.swift index a2d02698b..e28ec4ea6 100644 --- a/Tests/OpenAPIKitTests/Document/DocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/DocumentTests.swift @@ -1179,13 +1179,16 @@ extension DocumentTests { /// An example of implementing a loader context for loading external references /// into an OpenAPI document. - struct ExampleLoaderContext: ExternalLoader { + struct ExampleLoader: ExternalLoader { static func load(_ url: URL) async throws -> T where T : Decodable { - // load data from file, perhaps. we will just mock that up for the example: - let data = await mockParameterData(url) + // load data from file, perhaps. we will just mock that up for the test: + let data = try await mockData(componentKey(type: T.self, at: url)) let decoded = try JSONDecoder().decode(T.self, from: data) let finished: T + // while unnecessary, a loader may likely want to attatch some extra info + // to keep track of where a reference was loaded from. This test makes sure + // the following strategy of using vendor extensions works. if var extendable = decoded as? VendorExtendable { extendable.vendorExtensions["x-source-url"] = AnyCodable(url) finished = extendable as! T @@ -1198,24 +1201,38 @@ extension DocumentTests { static func componentKey(type: T.Type, at url: URL) throws -> OpenAPIKit.OpenAPI.ComponentKey { // do anything you want here to determine what key the new component should be stored at. // for the example, we will just transform the URL into a valid components key: - let urlString = url.pathComponents.dropFirst().joined(separator: "_").replacingOccurrences(of: ".", with: "_") + let urlString = url.pathComponents.dropFirst() + .joined(separator: "_") + .replacingOccurrences(of: ".", with: "_") return try .forceInit(rawValue: urlString) } /// Mock up some data, just for the example. - static func mockParameterData(_ url: URL) async -> Data { - return """ + static func mockData(_ key: OpenAPIKit.OpenAPI.ComponentKey) async throws -> Data { + print("looking up \(key.rawValue)") + return try XCTUnwrap(files[key.rawValue]) + } + + static let files: [String: Data] = [ + "params_name_json": """ { "name": "name", + "description": "a lonely parameter", "in": "path", - "schema": { "type": "string" }, - "required": true + "required": true, + "schema": { + "$ref": "file://./schemas/name_param.json#" + } } - """.data(using: .utf8)! - } + """, + "schemas_name_param_json": """ + { + "type": "string" + } + """ + ].mapValues { $0.data(using: .utf8)! } } - var document = OpenAPI.Document( info: .init(title: "test document", version: "1.0.0"), servers: [], @@ -1224,6 +1241,11 @@ extension DocumentTests { parameters: [ .reference(.external(URL(string: "file://./params/name.json")!)) ] + ), + "/goodbye/{name}": .init( + parameters: [ + .reference(.external(URL(string: "file://./params/name.json")!)) + ] ) ], components: .init( @@ -1258,7 +1280,7 @@ extension DocumentTests { } */ - try await document.externallyDereference(in: ExampleLoaderContext.self) + try await document.externallyDereference(in: ExampleLoader.self) // - MARK: After print( From e17bfd84eca9cce02a9c5033652f58fe4178ecde Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sun, 21 Apr 2024 14:17:24 -0500 Subject: [PATCH 038/110] Add depth to external dereferencing of the whole document --- Sources/OpenAPIKit/Document/Document.swift | 6 +- .../Document/DocumentTests.swift | 88 +++++++++---------- 2 files changed, 49 insertions(+), 45 deletions(-) diff --git a/Sources/OpenAPIKit/Document/Document.swift b/Sources/OpenAPIKit/Document/Document.swift index 5807b265f..c8eaa0afa 100644 --- a/Sources/OpenAPIKit/Document/Document.swift +++ b/Sources/OpenAPIKit/Document/Document.swift @@ -351,7 +351,9 @@ extension OpenAPI.Document { return try DereferencedDocument(self) } - public mutating func externallyDereference(in context: Context.Type) async throws { + public mutating func externallyDereference(in context: Context.Type, depth: Int = 1) async throws { + guard depth > 0 else { return } + let oldPaths = paths let oldWebhooks = webhooks @@ -364,6 +366,8 @@ extension OpenAPI.Document { webhooks = try await newWebhooks try await components.merge(c1) try await components.merge(c2) + + try await externallyDereference(in: context, depth: depth - 1) } } diff --git a/Tests/OpenAPIKitTests/Document/DocumentTests.swift b/Tests/OpenAPIKitTests/Document/DocumentTests.swift index e28ec4ea6..6f1ba6761 100644 --- a/Tests/OpenAPIKitTests/Document/DocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/DocumentTests.swift @@ -1233,7 +1233,7 @@ extension DocumentTests { ].mapValues { $0.data(using: .utf8)! } } - var document = OpenAPI.Document( + let document = OpenAPI.Document( info: .init(title: "test document", version: "1.0.0"), servers: [], paths: [ @@ -1254,66 +1254,66 @@ extension DocumentTests { ) ) - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted + let encoder = JSONEncoder() + encoder.outputFormatting = .prettyPrinted + + var docCopy1 = document + try await docCopy1.externallyDereference(in: ExampleLoader.self) + try await docCopy1.externallyDereference(in: ExampleLoader.self) + + var docCopy2 = document + try await docCopy2.externallyDereference(in: ExampleLoader.self, depth: 2) + + XCTAssertEqual(String(describing: docCopy1), String(describing: docCopy2)) - // - MARK: Before + // - MARK: After + print( + String(data: try encoder.encode(docCopy1), encoding: .utf8)! + ) print( - String(data: try encoder.encode(document), encoding: .utf8)! + String(data: try encoder.encode(docCopy2), encoding: .utf8)! ) /* { - "openapi": "3.1.0", - "info": { - "title": "test document", - "version": "1.0.0" + "info" : { + "version" : "1.0.0", + "title" : "test document" }, - "paths": { - "\/hello\/{name}": { - "parameters": [ + "openapi" : "3.1.0", + "paths" : { + "\/goodbye\/{name}" : { + "parameters" : [ { - "$ref": "file:\/\/.\/params\/name.json" + "$ref" : "#\/components\/parameters\/params_name_json" } ] - } - } - } - */ - - try await document.externallyDereference(in: ExampleLoader.self) - - // - MARK: After - print( - String(data: try encoder.encode(document), encoding: .utf8)! - ) - /* - { - "paths": { - "\/hello\/{name}": { - "parameters": [ + }, + "\/hello\/{name}" : { + "parameters" : [ { - "$ref": "#\/components\/parameters\/params_name_json" + "$ref" : "#\/components\/parameters\/params_name_json" } ] } }, - "components": { - "parameters": { - "params_name_json": { - "x-source-url": "file:\/\/.\/params\/name.json", - "in": "path", - "name": "name", - "required": true, - "schema": { - "type": "string" + "components" : { + "parameters" : { + "params_name_json" : { + "description" : "a lonely parameter", + "in" : "path", + "name" : "name", + "x-source-url" : "file:\/\/.\/params\/name.json", + "required" : true, + "schema" : { + "$ref" : "#\/components\/schemas\/schemas_name_param_json" } } + }, + "schemas" : { + "schemas_name_param_json" : { + "type" : "string" + } } - }, - "openapi": "3.1.0", - "info": { - "title": "test document", - "version": "1.0.0" } } */ From b032ded5dc1a204eeb44986f76598a0d40ccb41f Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sun, 21 Apr 2024 15:16:43 -0500 Subject: [PATCH 039/110] support depth less naively. allow full depth --- .../Components Object/Components.swift | 53 +++++++++++++++---- Sources/OpenAPIKit/Document/Document.swift | 22 ++++++-- .../Document/DocumentTests.swift | 6 ++- 3 files changed, 65 insertions(+), 16 deletions(-) diff --git a/Sources/OpenAPIKit/Components Object/Components.swift b/Sources/OpenAPIKit/Components Object/Components.swift index 0cc169a99..b9193e6dd 100644 --- a/Sources/OpenAPIKit/Components Object/Components.swift +++ b/Sources/OpenAPIKit/Components Object/Components.swift @@ -298,7 +298,12 @@ extension OpenAPI.Components { } extension OpenAPI.Components { - internal mutating func externallyDereference(in context: Context.Type) async throws { + internal mutating func externallyDereference(in context: Context.Type, depth: ExternalDereferenceDepth = .iterations(1)) async throws { + if case let .iterations(number) = depth, + number <= 0 { + return + } + let oldSchemas = schemas let oldResponses = responses let oldParameters = parameters @@ -336,15 +341,43 @@ extension OpenAPI.Components { callbacks = newCallbacks - try await merge(c1) - try await merge(c2) - try await merge(c3) - try await merge(c4) - try await merge(c5) - try await merge(c6) - try await merge(c7) - - try merge(c8) + let c1Resolved = try await c1 + let c2Resolved = try await c2 + let c3Resolved = try await c3 + let c4Resolved = try await c4 + let c5Resolved = try await c5 + let c6Resolved = try await c6 + let c7Resolved = try await c7 + let c8Resolved = c8 + + let noNewComponents = + c1Resolved.isEmpty + && c2Resolved.isEmpty + && c3Resolved.isEmpty + && c4Resolved.isEmpty + && c5Resolved.isEmpty + && c6Resolved.isEmpty + && c7Resolved.isEmpty + && c8Resolved.isEmpty + + if noNewComponents { return } + + try merge(c1Resolved) + try merge(c2Resolved) + try merge(c3Resolved) + try merge(c4Resolved) + try merge(c5Resolved) + try merge(c6Resolved) + try merge(c7Resolved) + + try merge(c8Resolved) + + switch depth { + case .iterations(let number): + try await externallyDereference(in: context, depth: .iterations(number - 1)) + case .full: + try await externallyDereference(in: context, depth: .full) + } } } diff --git a/Sources/OpenAPIKit/Document/Document.swift b/Sources/OpenAPIKit/Document/Document.swift index c8eaa0afa..75a703363 100644 --- a/Sources/OpenAPIKit/Document/Document.swift +++ b/Sources/OpenAPIKit/Document/Document.swift @@ -324,6 +324,17 @@ extension OpenAPI.Document { } } +public enum ExternalDereferenceDepth { + case iterations(Int) + case full +} + +extension ExternalDereferenceDepth: ExpressibleByIntegerLiteral { + public init(integerLiteral value: Int) { + self = .iterations(value) + } +} + extension OpenAPI.Document { /// Create a locally-dereferenced OpenAPI /// Document. @@ -351,14 +362,15 @@ extension OpenAPI.Document { return try DereferencedDocument(self) } - public mutating func externallyDereference(in context: Context.Type, depth: Int = 1) async throws { - guard depth > 0 else { return } + public mutating func externallyDereference(in context: Context.Type, depth: ExternalDereferenceDepth = .iterations(1)) async throws { + if case let .iterations(number) = depth, + number <= 0 { + return + } let oldPaths = paths let oldWebhooks = webhooks - try await components.externallyDereference(in: context) - async let (newPaths, c1) = oldPaths.externallyDereferenced(with: context) async let (newWebhooks, c2) = oldWebhooks.externallyDereferenced(with: context) @@ -367,7 +379,7 @@ extension OpenAPI.Document { try await components.merge(c1) try await components.merge(c2) - try await externallyDereference(in: context, depth: depth - 1) + try await components.externallyDereference(in: context, depth: depth) } } diff --git a/Tests/OpenAPIKitTests/Document/DocumentTests.swift b/Tests/OpenAPIKitTests/Document/DocumentTests.swift index 6f1ba6761..7a57f642c 100644 --- a/Tests/OpenAPIKitTests/Document/DocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/DocumentTests.swift @@ -1264,7 +1264,11 @@ extension DocumentTests { var docCopy2 = document try await docCopy2.externallyDereference(in: ExampleLoader.self, depth: 2) - XCTAssertEqual(String(describing: docCopy1), String(describing: docCopy2)) + var docCopy3 = document + try await docCopy3.externallyDereference(in: ExampleLoader.self, depth: .full) + + XCTAssertEqual(docCopy1, docCopy2) + XCTAssertEqual(docCopy2, docCopy3) // - MARK: After print( From b80657eec68f0aa9d03af59302dc52d732ccd7b4 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sun, 21 Apr 2024 15:47:54 -0500 Subject: [PATCH 040/110] fix issue with async ordered dictionary code not retaining original order. --- ...deredDictionary+ExternallyDereferenceable.swift | 3 +++ .../OrderedDictionary/OrderedDictionary.swift | 14 ++++++++++++++ Tests/OpenAPIKitTests/Document/DocumentTests.swift | 3 --- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/Sources/OpenAPIKit/Utility/OrderedDictionary+ExternallyDereferenceable.swift b/Sources/OpenAPIKit/Utility/OrderedDictionary+ExternallyDereferenceable.swift index 037cdabc0..283aab817 100644 --- a/Sources/OpenAPIKit/Utility/OrderedDictionary+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit/Utility/OrderedDictionary+ExternallyDereferenceable.swift @@ -25,6 +25,9 @@ extension OrderedDictionary where Value: ExternallyDereferenceable { newDict[key] = newRef try newComponents.merge(components) } + // things may come in out of order because of concurrency + // so we reorder after completing all entries. + try newDict.applyOrder(self) return (newDict, newComponents) } } diff --git a/Sources/OpenAPIKitCore/OrderedDictionary/OrderedDictionary.swift b/Sources/OpenAPIKitCore/OrderedDictionary/OrderedDictionary.swift index 8b52db0c5..7ed7bd91a 100644 --- a/Sources/OpenAPIKitCore/OrderedDictionary/OrderedDictionary.swift +++ b/Sources/OpenAPIKitCore/OrderedDictionary/OrderedDictionary.swift @@ -159,6 +159,20 @@ public struct OrderedDictionary: HasWarnings where Key: Hashable { } return ret } + + struct KeysDontMatch : Swift.Error {} + + /// Given two ordered dictionaries with the exact same keys, + /// apply the ordering of one to the other. This will throw if + /// the dictionary keys are not the same. + public mutating func applyOrder(_ other: Self) throws { + guard other.orderedKeys.count == orderedKeys.count, + other.orderedKeys.allSatisfy({ orderedKeys.contains($0) }) else { + throw KeysDontMatch() + } + + orderedKeys = other.orderedKeys + } } // MARK: - Dictionary Literal diff --git a/Tests/OpenAPIKitTests/Document/DocumentTests.swift b/Tests/OpenAPIKitTests/Document/DocumentTests.swift index 7a57f642c..479ac8ae0 100644 --- a/Tests/OpenAPIKitTests/Document/DocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/DocumentTests.swift @@ -1274,9 +1274,6 @@ extension DocumentTests { print( String(data: try encoder.encode(docCopy1), encoding: .utf8)! ) - print( - String(data: try encoder.encode(docCopy2), encoding: .utf8)! - ) /* { "info" : { From 6ce5f4f18fbc344ff1aa24f4f426fe9f91b9e000 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Mon, 22 Apr 2024 09:54:40 -0500 Subject: [PATCH 041/110] continuing to fill out document external dereferencing test. fix bug with array ordering. fix bug with missing path items components --- .../Components Object/Components.swift | 9 +- .../Array+ExternallyDereferenceable.swift | 17 +- .../Document/DocumentTests.swift | 146 ++++++++++++------ 3 files changed, 111 insertions(+), 61 deletions(-) diff --git a/Sources/OpenAPIKit/Components Object/Components.swift b/Sources/OpenAPIKit/Components Object/Components.swift index b9193e6dd..0b17f0b56 100644 --- a/Sources/OpenAPIKit/Components Object/Components.swift +++ b/Sources/OpenAPIKit/Components Object/Components.swift @@ -311,8 +311,8 @@ extension OpenAPI.Components { let oldRequestBodies = requestBodies let oldHeaders = headers let oldSecuritySchemes = securitySchemes - let oldCallbacks = callbacks + let oldPathItems = pathItems async let (newSchemas, c1) = oldSchemas.externallyDereferenced(with: context) async let (newResponses, c2) = oldResponses.externallyDereferenced(with: context) @@ -321,6 +321,7 @@ extension OpenAPI.Components { async let (newRequestBodies, c5) = oldRequestBodies.externallyDereferenced(with: context) async let (newHeaders, c6) = oldHeaders.externallyDereferenced(with: context) async let (newSecuritySchemes, c7) = oldSecuritySchemes.externallyDereferenced(with: context) + async let (newPathItems, c9) = oldPathItems.externallyDereferenced(with: context) // async let (newCallbacks, c8) = oldCallbacks.externallyDereferenced(with: context) var c8 = OpenAPI.Components() @@ -338,8 +339,8 @@ extension OpenAPI.Components { requestBodies = try await newRequestBodies headers = try await newHeaders securitySchemes = try await newSecuritySchemes - callbacks = newCallbacks + pathItems = try await newPathItems let c1Resolved = try await c1 let c2Resolved = try await c2 @@ -349,6 +350,7 @@ extension OpenAPI.Components { let c6Resolved = try await c6 let c7Resolved = try await c7 let c8Resolved = c8 + let c9Resolved = try await c9 let noNewComponents = c1Resolved.isEmpty @@ -359,6 +361,7 @@ extension OpenAPI.Components { && c6Resolved.isEmpty && c7Resolved.isEmpty && c8Resolved.isEmpty + && c9Resolved.isEmpty if noNewComponents { return } @@ -369,8 +372,8 @@ extension OpenAPI.Components { try merge(c5Resolved) try merge(c6Resolved) try merge(c7Resolved) - try merge(c8Resolved) + try merge(c9Resolved) switch depth { case .iterations(let number): diff --git a/Sources/OpenAPIKit/Utility/Array+ExternallyDereferenceable.swift b/Sources/OpenAPIKit/Utility/Array+ExternallyDereferenceable.swift index bb570c7b8..1435c2c95 100644 --- a/Sources/OpenAPIKit/Utility/Array+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit/Utility/Array+ExternallyDereferenceable.swift @@ -7,21 +7,24 @@ import OpenAPIKitCore extension Array where Element: ExternallyDereferenceable { public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { - try await withThrowingTaskGroup(of: (Element, OpenAPI.Components).self) { group in - for elem in self { + try await withThrowingTaskGroup(of: (Int, (Element, OpenAPI.Components)).self) { group in + for (idx, elem) in zip(self.indices, self) { group.addTask { - return try await elem.externallyDereferenced(with: loader) + return try await (idx, elem.externallyDereferenced(with: loader)) } } - var newElems = Self() + var newElems = Array<(Int, Element)>() var newComponents = OpenAPI.Components() - for try await (elem, components) in group { - newElems.append(elem) + for try await (idx, (elem, components)) in group { + newElems.append((idx, elem)) try newComponents.merge(components) } - return (newElems, newComponents) + // things may come in out of order because of concurrency + // so we reorder after completing all entries. + newElems.sort { left, right in left.0 < right.0 } + return (newElems.map { $0.1 }, newComponents) } } } diff --git a/Tests/OpenAPIKitTests/Document/DocumentTests.swift b/Tests/OpenAPIKitTests/Document/DocumentTests.swift index 479ac8ae0..4f0e60040 100644 --- a/Tests/OpenAPIKitTests/Document/DocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/DocumentTests.swift @@ -1209,7 +1209,6 @@ extension DocumentTests { /// Mock up some data, just for the example. static func mockData(_ key: OpenAPIKit.OpenAPI.ComponentKey) async throws -> Data { - print("looking up \(key.rawValue)") return try XCTUnwrap(files[key.rawValue]) } @@ -1229,6 +1228,80 @@ extension DocumentTests { { "type": "string" } + """, + "paths_webhook_json": """ + { + "summary": "just a webhook", + "get": { + "requestBody": { + "$ref": "file://./requests/webhook.json" + }, + "responses": { + "200": { + "$ref": "file://./responses/webhook.json" + } + } + } + } + """, + "requests_webhook_json": """ + { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "body": { + "type": "string" + } + } + }, + "examples": { + "good": { + "$ref": "file://./examples/good.json" + } + } + } + } + } + """, + "responses_webhook_json": """ + { + "description": "webhook response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "body": { + "type": "string" + }, + "length": { + "type": "integer", + "minimum": 0 + } + } + } + } + }, + "headers": { + "X-Hello": { + "$ref": "file://./headers/webhook.json" + } + } + } + """, + "headers_webhook_json": """ + { + "schema": { + "$ref": "file://./schemas/name_param.json" + } + } + """, + "examples_good_json": """ + { + "value": "{\\"body\\": \\"request me\\"}" + } """ ].mapValues { $0.data(using: .utf8)! } } @@ -1246,9 +1319,16 @@ extension DocumentTests { parameters: [ .reference(.external(URL(string: "file://./params/name.json")!)) ] - ) + ), + "/webhook": .reference(.external(URL(string: "file://./paths/webhook.json")!)) ], + webhooks: [ + "webhook": .reference(.external(URL(string: "file://./paths/webhook.json")!)) + ], components: .init( + schemas: [ + "name_param": .reference(.external(URL(string: "file://./schemas/name_param.json")!)) + ], // just to show, no parameters defined within document components : parameters: [:] ) @@ -1260,63 +1340,27 @@ extension DocumentTests { var docCopy1 = document try await docCopy1.externallyDereference(in: ExampleLoader.self) try await docCopy1.externallyDereference(in: ExampleLoader.self) + try await docCopy1.externallyDereference(in: ExampleLoader.self) var docCopy2 = document - try await docCopy2.externallyDereference(in: ExampleLoader.self, depth: 2) + try await docCopy2.externallyDereference(in: ExampleLoader.self, depth: 3) var docCopy3 = document try await docCopy3.externallyDereference(in: ExampleLoader.self, depth: .full) - XCTAssertEqual(docCopy1, docCopy2) +// XCTAssertEqual(docCopy1, docCopy2) XCTAssertEqual(docCopy2, docCopy3) + XCTAssertEqual(String(describing: docCopy2), String(describing: docCopy3)) // - MARK: After - print( - String(data: try encoder.encode(docCopy1), encoding: .utf8)! - ) - /* - { - "info" : { - "version" : "1.0.0", - "title" : "test document" - }, - "openapi" : "3.1.0", - "paths" : { - "\/goodbye\/{name}" : { - "parameters" : [ - { - "$ref" : "#\/components\/parameters\/params_name_json" - } - ] - }, - "\/hello\/{name}" : { - "parameters" : [ - { - "$ref" : "#\/components\/parameters\/params_name_json" - } - ] - } - }, - "components" : { - "parameters" : { - "params_name_json" : { - "description" : "a lonely parameter", - "in" : "path", - "name" : "name", - "x-source-url" : "file:\/\/.\/params\/name.json", - "required" : true, - "schema" : { - "$ref" : "#\/components\/schemas\/schemas_name_param_json" - } - } - }, - "schemas" : { - "schemas_name_param_json" : { - "type" : "string" - } - } - } - } - */ +// print( +// String(data: try encoder.encode(docCopy1), encoding: .utf8)! +// ) +// print( +// String(data: try encoder.encode(docCopy2), encoding: .utf8)! +// ) +// print( +// String(data: try encoder.encode(docCopy3), encoding: .utf8)! +// ) } } From 773df590c7d500fa6b6bae923b64f63e652e0544 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Mon, 22 Apr 2024 10:36:37 -0500 Subject: [PATCH 042/110] work on fixing ordering bugs in code and test --- .../Components Object/Components.swift | 14 ++ .../OrderedDictionary/OrderedDictionary.swift | 10 + .../OpenAPIKitCore/Shared/ComponentKey.swift | 6 + .../Document/DocumentTests.swift | 193 --------------- .../ExternalDereferencingDocumentTests.swift | 224 ++++++++++++++++++ 5 files changed, 254 insertions(+), 193 deletions(-) create mode 100644 Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift diff --git a/Sources/OpenAPIKit/Components Object/Components.swift b/Sources/OpenAPIKit/Components Object/Components.swift index 0b17f0b56..bbafe0cdc 100644 --- a/Sources/OpenAPIKit/Components Object/Components.swift +++ b/Sources/OpenAPIKit/Components Object/Components.swift @@ -98,6 +98,20 @@ extension OpenAPI.Components { try pathItems.merge(other.pathItems, uniquingKeysWith: detectCollision(type: "pathItems")) try vendorExtensions.merge(other.vendorExtensions, uniquingKeysWith: detectCollision(type: "vendorExtensions")) } + + /// Sort the components within each type by the component key. + public mutating func sort() { + schemas.sortKeys() + responses.sortKeys() + parameters.sortKeys() + examples.sortKeys() + requestBodies.sortKeys() + headers.sortKeys() + securitySchemes.sortKeys() + links.sortKeys() + callbacks.sortKeys() + pathItems.sortKeys() + } } extension OpenAPI.Components { diff --git a/Sources/OpenAPIKitCore/OrderedDictionary/OrderedDictionary.swift b/Sources/OpenAPIKitCore/OrderedDictionary/OrderedDictionary.swift index 7ed7bd91a..8a9cd91a8 100644 --- a/Sources/OpenAPIKitCore/OrderedDictionary/OrderedDictionary.swift +++ b/Sources/OpenAPIKitCore/OrderedDictionary/OrderedDictionary.swift @@ -173,6 +173,16 @@ public struct OrderedDictionary: HasWarnings where Key: Hashable { orderedKeys = other.orderedKeys } + + public mutating func sortKeys(by sort: (Key, Key) throws -> Bool) rethrows { + try orderedKeys.sort(by: sort) + } +} + +extension OrderedDictionary where Key: Comparable { + public mutating func sortKeys() { + orderedKeys.sort() + } } // MARK: - Dictionary Literal diff --git a/Sources/OpenAPIKitCore/Shared/ComponentKey.swift b/Sources/OpenAPIKitCore/Shared/ComponentKey.swift index 5388e5d1b..251ad343a 100644 --- a/Sources/OpenAPIKitCore/Shared/ComponentKey.swift +++ b/Sources/OpenAPIKitCore/Shared/ComponentKey.swift @@ -90,3 +90,9 @@ extension Shared { } } } + +extension Shared.ComponentKey: Comparable { + public static func < (lhs: Shared.ComponentKey, rhs: Shared.ComponentKey) -> Bool { + lhs.rawValue < rhs.rawValue + } +} diff --git a/Tests/OpenAPIKitTests/Document/DocumentTests.swift b/Tests/OpenAPIKitTests/Document/DocumentTests.swift index 4f0e60040..c68703e8d 100644 --- a/Tests/OpenAPIKitTests/Document/DocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/DocumentTests.swift @@ -1171,196 +1171,3 @@ extension DocumentTests { ) } } - -// MARK: - External Dereferencing -extension DocumentTests { - // temporarily test with an example of the new interface - func test_example() async throws { - - /// An example of implementing a loader context for loading external references - /// into an OpenAPI document. - struct ExampleLoader: ExternalLoader { - static func load(_ url: URL) async throws -> T where T : Decodable { - // load data from file, perhaps. we will just mock that up for the test: - let data = try await mockData(componentKey(type: T.self, at: url)) - - let decoded = try JSONDecoder().decode(T.self, from: data) - let finished: T - // while unnecessary, a loader may likely want to attatch some extra info - // to keep track of where a reference was loaded from. This test makes sure - // the following strategy of using vendor extensions works. - if var extendable = decoded as? VendorExtendable { - extendable.vendorExtensions["x-source-url"] = AnyCodable(url) - finished = extendable as! T - } else { - finished = decoded - } - return finished - } - - static func componentKey(type: T.Type, at url: URL) throws -> OpenAPIKit.OpenAPI.ComponentKey { - // do anything you want here to determine what key the new component should be stored at. - // for the example, we will just transform the URL into a valid components key: - let urlString = url.pathComponents.dropFirst() - .joined(separator: "_") - .replacingOccurrences(of: ".", with: "_") - return try .forceInit(rawValue: urlString) - } - - /// Mock up some data, just for the example. - static func mockData(_ key: OpenAPIKit.OpenAPI.ComponentKey) async throws -> Data { - return try XCTUnwrap(files[key.rawValue]) - } - - static let files: [String: Data] = [ - "params_name_json": """ - { - "name": "name", - "description": "a lonely parameter", - "in": "path", - "required": true, - "schema": { - "$ref": "file://./schemas/name_param.json#" - } - } - """, - "schemas_name_param_json": """ - { - "type": "string" - } - """, - "paths_webhook_json": """ - { - "summary": "just a webhook", - "get": { - "requestBody": { - "$ref": "file://./requests/webhook.json" - }, - "responses": { - "200": { - "$ref": "file://./responses/webhook.json" - } - } - } - } - """, - "requests_webhook_json": """ - { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "body": { - "type": "string" - } - } - }, - "examples": { - "good": { - "$ref": "file://./examples/good.json" - } - } - } - } - } - """, - "responses_webhook_json": """ - { - "description": "webhook response", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "body": { - "type": "string" - }, - "length": { - "type": "integer", - "minimum": 0 - } - } - } - } - }, - "headers": { - "X-Hello": { - "$ref": "file://./headers/webhook.json" - } - } - } - """, - "headers_webhook_json": """ - { - "schema": { - "$ref": "file://./schemas/name_param.json" - } - } - """, - "examples_good_json": """ - { - "value": "{\\"body\\": \\"request me\\"}" - } - """ - ].mapValues { $0.data(using: .utf8)! } - } - - let document = OpenAPI.Document( - info: .init(title: "test document", version: "1.0.0"), - servers: [], - paths: [ - "/hello/{name}": .init( - parameters: [ - .reference(.external(URL(string: "file://./params/name.json")!)) - ] - ), - "/goodbye/{name}": .init( - parameters: [ - .reference(.external(URL(string: "file://./params/name.json")!)) - ] - ), - "/webhook": .reference(.external(URL(string: "file://./paths/webhook.json")!)) - ], - webhooks: [ - "webhook": .reference(.external(URL(string: "file://./paths/webhook.json")!)) - ], - components: .init( - schemas: [ - "name_param": .reference(.external(URL(string: "file://./schemas/name_param.json")!)) - ], - // just to show, no parameters defined within document components : - parameters: [:] - ) - ) - - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - - var docCopy1 = document - try await docCopy1.externallyDereference(in: ExampleLoader.self) - try await docCopy1.externallyDereference(in: ExampleLoader.self) - try await docCopy1.externallyDereference(in: ExampleLoader.self) - - var docCopy2 = document - try await docCopy2.externallyDereference(in: ExampleLoader.self, depth: 3) - - var docCopy3 = document - try await docCopy3.externallyDereference(in: ExampleLoader.self, depth: .full) - -// XCTAssertEqual(docCopy1, docCopy2) - XCTAssertEqual(docCopy2, docCopy3) - XCTAssertEqual(String(describing: docCopy2), String(describing: docCopy3)) - - // - MARK: After -// print( -// String(data: try encoder.encode(docCopy1), encoding: .utf8)! -// ) -// print( -// String(data: try encoder.encode(docCopy2), encoding: .utf8)! -// ) -// print( -// String(data: try encoder.encode(docCopy3), encoding: .utf8)! -// ) - } -} diff --git a/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift b/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift new file mode 100644 index 000000000..5747f2d05 --- /dev/null +++ b/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift @@ -0,0 +1,224 @@ +// +// ExternalDereferencingDocumentTests.swift +// + +import Foundation +import Yams +import OpenAPIKit +import XCTest + +final class ExternalDereferencingDocumentTests: XCTestCase { + // temporarily test with an example of the new interface + func test_example() async throws { + + /// An example of implementing a loader context for loading external references + /// into an OpenAPI document. + struct ExampleLoader: ExternalLoader { + static func load(_ url: URL) async throws -> T where T : Decodable { + // load data from file, perhaps. we will just mock that up for the test: + let data = try await mockData(componentKey(type: T.self, at: url)) + + // We use the YAML decoder purely for order-stability. + let decoded = try YAMLDecoder().decode(T.self, from: data) + let finished: T + // while unnecessary, a loader may likely want to attatch some extra info + // to keep track of where a reference was loaded from. This test makes sure + // the following strategy of using vendor extensions works. + if var extendable = decoded as? VendorExtendable { + extendable.vendorExtensions["x-source-url"] = AnyCodable(url) + finished = extendable as! T + } else { + finished = decoded + } + return finished + } + + static func componentKey(type: T.Type, at url: URL) throws -> OpenAPIKit.OpenAPI.ComponentKey { + // do anything you want here to determine what key the new component should be stored at. + // for the example, we will just transform the URL into a valid components key: + let urlString = url.pathComponents.dropFirst() + .joined(separator: "_") + .replacingOccurrences(of: ".", with: "_") + return try .forceInit(rawValue: urlString) + } + + /// Mock up some data, just for the example. + static func mockData(_ key: OpenAPIKit.OpenAPI.ComponentKey) async throws -> Data { + return try XCTUnwrap(files[key.rawValue]) + } + + static let files: [String: Data] = [ + "params_name_json": """ + { + "name": "name", + "description": "a lonely parameter", + "in": "path", + "required": true, + "schema": { + "$ref": "file://./schemas/name_param.json#" + } + } + """, + "schemas_name_param_json": """ + { + "type": "string" + } + """, + "paths_webhook_json": """ + { + "summary": "just a webhook", + "get": { + "requestBody": { + "$ref": "file://./requests/webhook.json" + }, + "responses": { + "200": { + "$ref": "file://./responses/webhook.json" + } + } + } + } + """, + "requests_webhook_json": """ + { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "body": { + "type": "string" + } + } + }, + "examples": { + "good": { + "$ref": "file://./examples/good.json" + } + } + } + } + } + """, + "responses_webhook_json": """ + { + "description": "webhook response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "body": { + "type": "string" + }, + "length": { + "type": "integer", + "minimum": 0 + } + } + } + } + }, + "headers": { + "X-Hello": { + "$ref": "file://./headers/webhook.json" + } + } + } + """, + "headers_webhook_json": """ + { + "schema": { + "$ref": "file://./schemas/name_param.json" + } + } + """, + "examples_good_json": """ + { + "value": "{\\"body\\": \\"request me\\"}" + } + """ + ].mapValues { $0.data(using: .utf8)! } + } + + let document = OpenAPI.Document( + info: .init(title: "test document", version: "1.0.0"), + servers: [], + paths: [ + "/hello/{name}": .init( + parameters: [ + .reference(.external(URL(string: "file://./params/name.json")!)) + ] + ), + "/goodbye/{name}": .init( + parameters: [ + .reference(.external(URL(string: "file://./params/name.json")!)) + ] + ), + "/webhook": .reference(.external(URL(string: "file://./paths/webhook.json")!)) + ], + webhooks: [ + "webhook": .reference(.external(URL(string: "file://./paths/webhook.json")!)) + ], + components: .init( + schemas: [ + "name_param": .reference(.external(URL(string: "file://./schemas/name_param.json")!)) + ], + // just to show, no parameters defined within document components : + parameters: [:] + ) + ) + + let encoder = JSONEncoder() + encoder.outputFormatting = .prettyPrinted + + var docCopy1 = document + try await docCopy1.externallyDereference(in: ExampleLoader.self) + try await docCopy1.externallyDereference(in: ExampleLoader.self) + try await docCopy1.externallyDereference(in: ExampleLoader.self) + docCopy1.components.sort() + + var docCopy2 = document + try await docCopy2.externallyDereference(in: ExampleLoader.self, depth: 3) + docCopy2.components.sort() + + var docCopy3 = document + try await docCopy3.externallyDereference(in: ExampleLoader.self, depth: .full) + docCopy3.components.sort() + +// print("-----") +// print(docCopy2 == docCopy3) +// print(docCopy2.components == docCopy3.components) +// print("+++++") +// print(docCopy2.components.schemas == docCopy3.components.schemas) +// print(docCopy2.components.responses == docCopy3.components.responses) +// print(docCopy2.components.parameters == docCopy3.components.parameters) +// print(docCopy2.components.examples == docCopy3.components.examples) +// print(docCopy2.components.requestBodies == docCopy3.components.requestBodies) +// print(docCopy2.components.headers == docCopy3.components.headers) +// print(docCopy2.components.securitySchemes == docCopy3.components.securitySchemes) +// print(docCopy2.components.links == docCopy3.components.links) +// print(docCopy2.components.callbacks == docCopy3.components.callbacks) +// print(docCopy2.components.pathItems == docCopy3.components.pathItems) +// print("=====") +// print(docCopy2.components.responses) +// print("&&&&&") +// print(docCopy3.components.responses) +// print("$$$$$") + + XCTAssertEqual(docCopy1, docCopy2) + XCTAssertEqual(docCopy2, docCopy3) +// XCTAssertEqual(String(describing: docCopy2), String(describing: docCopy3)) + + // - MARK: After +// print( +// String(data: try encoder.encode(docCopy1), encoding: .utf8)! +// ) +// print( +// String(data: try encoder.encode(docCopy2), encoding: .utf8)! +// ) +// print( +// String(data: try encoder.encode(docCopy3), encoding: .utf8)! +// ) + } +} From d092f487ae3b6cad5123604f89d6e25437095d10 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Mon, 22 Apr 2024 23:06:58 -0500 Subject: [PATCH 043/110] Add external reference to a Callbacks object. --- .../ExternalDereferencingDocumentTests.swift | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift b/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift index 5747f2d05..214c05a04 100644 --- a/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift @@ -137,6 +137,32 @@ final class ExternalDereferencingDocumentTests: XCTestCase { { "value": "{\\"body\\": \\"request me\\"}" } + """, + "callbacks_one_json": """ + { + "https://callback.site.com/callback": { + "$ref": "file://./paths/callback.json" + } + } + """, + "paths_callback_json": """ + { + "summary": "just a callback", + "get": { + "responses": { + "200": { + "description": "callback response", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + } """ ].mapValues { $0.data(using: .utf8)! } } @@ -148,7 +174,13 @@ final class ExternalDereferencingDocumentTests: XCTestCase { "/hello/{name}": .init( parameters: [ .reference(.external(URL(string: "file://./params/name.json")!)) - ] + ], + get: .init( + responses: [:], + callbacks: [ + "callback1": .reference(.external(URL(string: "file://./callbacks/one.json")!)) + ] + ) ), "/goodbye/{name}": .init( parameters: [ From e5c44b4e72abe06cb279de5a905cbd63f74e88a7 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Mon, 22 Apr 2024 23:15:58 -0500 Subject: [PATCH 044/110] Add links external reference --- .../Document/ExternalDereferencingDocumentTests.swift | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift b/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift index 214c05a04..5bbee8fcb 100644 --- a/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift @@ -158,11 +158,21 @@ final class ExternalDereferencingDocumentTests: XCTestCase { "type": "object" } } + }, + "links": { + "link1": { + "$ref": "file://./links/first.json" + } } } } } } + """, + "links_first_json": """ + { + "operationId": "helloOp" + } """ ].mapValues { $0.data(using: .utf8)! } } @@ -176,6 +186,7 @@ final class ExternalDereferencingDocumentTests: XCTestCase { .reference(.external(URL(string: "file://./params/name.json")!)) ], get: .init( + operationId: "helloOp", responses: [:], callbacks: [ "callback1": .reference(.external(URL(string: "file://./callbacks/one.json")!)) From bd97176ccbcaddfdf72f7cdf4e0f1016ebe7d3e9 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 23 Apr 2024 09:42:51 -0500 Subject: [PATCH 045/110] Add an external dereferencing case that proves the basic collision check doesn't work. remove collision check. --- .../Components Object/Components.swift | 12 +++++++++-- .../ExternalDereferencingDocumentTests.swift | 20 +++++++++++++------ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/Sources/OpenAPIKit/Components Object/Components.swift b/Sources/OpenAPIKit/Components Object/Components.swift index bbafe0cdc..d3d13cbdc 100644 --- a/Sources/OpenAPIKit/Components Object/Components.swift +++ b/Sources/OpenAPIKit/Components Object/Components.swift @@ -80,8 +80,16 @@ extension OpenAPI.Components { private func detectCollision(type: String) throws -> (_ old: T, _ new: T) throws -> T { return { old, new in - if(old == new) { return old } - throw ComponentCollision(componentType: type, existingComponent: String(describing:old), newComponent: String(describing:new)) + // theoretically we can detect collisions here, but we would need to compare + // for equality up-to but not including the difference between an external and + // internal reference which is not supported yet. +// if(old == new) { return old } +// throw ComponentCollision(componentType: type, existingComponent: String(describing:old), newComponent: String(describing:new)) + + // Given we aren't ensuring there are no collisions, the old version is going to be + // the one more likely to have been _further_ dereferenced than the new record, so + // we keep that version. + return old } } diff --git a/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift b/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift index 5bbee8fcb..1ffc7e2ed 100644 --- a/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift @@ -55,13 +55,21 @@ final class ExternalDereferencingDocumentTests: XCTestCase { "in": "path", "required": true, "schema": { - "$ref": "file://./schemas/name_param.json#" + "$ref": "file://./schemas/string_param.json#" } } """, - "schemas_name_param_json": """ + "schemas_string_param_json": """ { - "type": "string" + "oneOf": [ + { "type": "string" }, + { "$ref": "file://./schemas/basic_object.json" } + ] + } + """, + "schemas_basic_object_json": """ + { + "type": "object" } """, "paths_webhook_json": """ @@ -87,7 +95,7 @@ final class ExternalDereferencingDocumentTests: XCTestCase { "type": "object", "properties": { "body": { - "type": "string" + "$ref": "file://./schemas/string_param.json" } } }, @@ -129,7 +137,7 @@ final class ExternalDereferencingDocumentTests: XCTestCase { "headers_webhook_json": """ { "schema": { - "$ref": "file://./schemas/name_param.json" + "$ref": "file://./schemas/string_param.json" } } """, @@ -205,7 +213,7 @@ final class ExternalDereferencingDocumentTests: XCTestCase { ], components: .init( schemas: [ - "name_param": .reference(.external(URL(string: "file://./schemas/name_param.json")!)) + "name_param": .reference(.external(URL(string: "file://./schemas/string_param.json")!)) ], // just to show, no parameters defined within document components : parameters: [:] From 412ec1c2408c7cbfe412875b3967ea16351570ba Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 23 Apr 2024 09:55:52 -0500 Subject: [PATCH 046/110] remove unused external dereferencing code --- Sources/OpenAPIKit/Callbacks.swift | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Sources/OpenAPIKit/Callbacks.swift b/Sources/OpenAPIKit/Callbacks.swift index 30baf3e3d..7a671bdeb 100644 --- a/Sources/OpenAPIKit/Callbacks.swift +++ b/Sources/OpenAPIKit/Callbacks.swift @@ -37,12 +37,6 @@ extension OpenAPI.CallbackURL: LocallyDereferenceable { } } -extension OpenAPI.CallbackURL: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { - return (self, .init()) - } -} - // The following conformance is theoretically unnecessary but the compiler is // only able to find the conformance if we explicitly declare it here, though // it is apparently able to determine the conformance is already satisfied here From 45b8df9f9161f159d99ea891ca17c5956e12987a Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 23 Apr 2024 10:00:54 -0500 Subject: [PATCH 047/110] hit the encoding header external dereference codepath --- .../Document/ExternalDereferencingDocumentTests.swift | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift b/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift index 1ffc7e2ed..b063d048a 100644 --- a/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift @@ -103,6 +103,15 @@ final class ExternalDereferencingDocumentTests: XCTestCase { "good": { "$ref": "file://./examples/good.json" } + }, + "encoding": { + "enc1": { + "headers": { + "head1": { + "$ref": "file://./headers/webhook.json" + } + } + } } } } From 76d27d63b683d8768e01e6704084ff37d0bc9f50 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 23 Apr 2024 10:10:53 -0500 Subject: [PATCH 048/110] undo a workaround that is no longer needed --- .../OpenAPIKit/Components Object/Components.swift | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/Sources/OpenAPIKit/Components Object/Components.swift b/Sources/OpenAPIKit/Components Object/Components.swift index d3d13cbdc..a92862d76 100644 --- a/Sources/OpenAPIKit/Components Object/Components.swift +++ b/Sources/OpenAPIKit/Components Object/Components.swift @@ -343,17 +343,9 @@ extension OpenAPI.Components { async let (newRequestBodies, c5) = oldRequestBodies.externallyDereferenced(with: context) async let (newHeaders, c6) = oldHeaders.externallyDereferenced(with: context) async let (newSecuritySchemes, c7) = oldSecuritySchemes.externallyDereferenced(with: context) + async let (newCallbacks, c8) = oldCallbacks.externallyDereferenced(with: context) async let (newPathItems, c9) = oldPathItems.externallyDereferenced(with: context) -// async let (newCallbacks, c8) = oldCallbacks.externallyDereferenced(with: context) - var c8 = OpenAPI.Components() - var newCallbacks = oldCallbacks - for (key, callback) in oldCallbacks { - let (newCallback, components) = try await callback.externallyDereferenced(with: context) - newCallbacks[key] = newCallback - try c8.merge(components) - } - schemas = try await newSchemas responses = try await newResponses parameters = try await newParameters @@ -361,7 +353,7 @@ extension OpenAPI.Components { requestBodies = try await newRequestBodies headers = try await newHeaders securitySchemes = try await newSecuritySchemes - callbacks = newCallbacks + callbacks = try await newCallbacks pathItems = try await newPathItems let c1Resolved = try await c1 @@ -371,7 +363,7 @@ extension OpenAPI.Components { let c5Resolved = try await c5 let c6Resolved = try await c6 let c7Resolved = try await c7 - let c8Resolved = c8 + let c8Resolved = try await c8 let c9Resolved = try await c9 let noNewComponents = From 9ed957042fa781db9d81ea4936014f75693c2ff1 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 23 Apr 2024 10:29:29 -0500 Subject: [PATCH 049/110] clean up some temporary test code --- .../ExternalDereferencingDocumentTests.swift | 32 ------------ .../Path Item/PathItemTests.swift | 50 ------------------- 2 files changed, 82 deletions(-) diff --git a/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift b/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift index b063d048a..914e575cc 100644 --- a/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift @@ -246,39 +246,7 @@ final class ExternalDereferencingDocumentTests: XCTestCase { try await docCopy3.externallyDereference(in: ExampleLoader.self, depth: .full) docCopy3.components.sort() -// print("-----") -// print(docCopy2 == docCopy3) -// print(docCopy2.components == docCopy3.components) -// print("+++++") -// print(docCopy2.components.schemas == docCopy3.components.schemas) -// print(docCopy2.components.responses == docCopy3.components.responses) -// print(docCopy2.components.parameters == docCopy3.components.parameters) -// print(docCopy2.components.examples == docCopy3.components.examples) -// print(docCopy2.components.requestBodies == docCopy3.components.requestBodies) -// print(docCopy2.components.headers == docCopy3.components.headers) -// print(docCopy2.components.securitySchemes == docCopy3.components.securitySchemes) -// print(docCopy2.components.links == docCopy3.components.links) -// print(docCopy2.components.callbacks == docCopy3.components.callbacks) -// print(docCopy2.components.pathItems == docCopy3.components.pathItems) -// print("=====") -// print(docCopy2.components.responses) -// print("&&&&&") -// print(docCopy3.components.responses) -// print("$$$$$") - XCTAssertEqual(docCopy1, docCopy2) XCTAssertEqual(docCopy2, docCopy3) -// XCTAssertEqual(String(describing: docCopy2), String(describing: docCopy3)) - - // - MARK: After -// print( -// String(data: try encoder.encode(docCopy1), encoding: .utf8)! -// ) -// print( -// String(data: try encoder.encode(docCopy2), encoding: .utf8)! -// ) -// print( -// String(data: try encoder.encode(docCopy3), encoding: .utf8)! -// ) } } diff --git a/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift b/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift index 62ddd9a83..22c9e2601 100644 --- a/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift +++ b/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift @@ -433,53 +433,3 @@ extension PathItemTests { ) } } - -// MARK: External Dereferencing Tests -extension PathItemTests { - struct MockLoad: ExternalLoader { - static func componentKey(type: T.Type, at url: URL) throws -> OpenAPI.ComponentKey { - let urlString = url.pathComponents.dropFirst().joined(separator: "_").replacingOccurrences(of: ".", with: "_") - - return try .forceInit(rawValue: urlString) - } - - static func load(_: URL) throws -> T where T : Decodable { - if let ret = OpenAPI.Request(description: "hello", content: [:]) as? T { - return ret - } - if let ret = OpenAPI.Parameter(name: "other-param", context: .header, schema: .string) as? T { - return ret - } - throw ValidationError(reason: "", at: []) - } - } - - func test_tmp() async throws { - let op = OpenAPI.Operation(responses: [:]) - let pathItem = OpenAPI.PathItem( - summary: "summary", - description: "description", - servers: [OpenAPI.Server(url: URL(string: "http://google.com")!)], - parameters: [ - .parameter(name: "hello", context: .query, schema: .string), - .reference(.component(named: "already-internal")), - .reference(.external(URL(string: "https://some-param.com/param")!)) - ], - get: .init(requestBody: .reference(.external(URL(string: "https://website.com/request")!)), responses: [:]), - put: op, - post: op, - delete: op, - options: op, - head: op, - patch: op, - trace: op - ) - - print(pathItem.parameters.debugDescription) - print("------") - let (x, loaderComponents) = try await pathItem.externallyDereferenced(with: MockLoad.self) - print(x.parameters.debugDescription) - print("=======") - print(loaderComponents.parameters) - } -} From 728218ea50f17615e20827fe0cfa662d7437825f Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 23 Apr 2024 10:50:16 -0500 Subject: [PATCH 050/110] update the docs to talk about external dereferencing --- README.md | 46 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 07e427694..5f0eccb65 100644 --- a/README.md +++ b/README.md @@ -220,7 +220,7 @@ You can create an external reference with `JSONReference.external(URL)`. Interna You can check whether a given `JSONReference` exists in the Components Object with `document.components.contains()`. You can access a referenced object in the Components Object with `document.components[reference]`. -You can create references from the Components Object with `document.components.reference(named:ofType:)`. This method will throw an error if the given component does not exist in the ComponentsObject. +References can be created from the Components Object with `document.components.reference(named:ofType:)`. This method will throw an error if the given component does not exist in the ComponentsObject. You can use `document.components.lookup()` or the `Components` type's `subscript` to turn an `Either` containing either a reference or a component into an optional value of that component's type (having either pulled it out of the `Either` or looked it up in the Components Object). The `lookup()` method throws when it can't find an item whereas `subscript` returns `nil`. @@ -284,7 +284,7 @@ let document = OpenAPI.Document( ``` #### Specification Extensions -Many OpenAPIKit types support [Specification Extensions](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#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. +Many OpenAPIKit types support [Specification Extensions](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#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. 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. @@ -323,11 +323,51 @@ try encodeEqual(URL(string: "https://website.com"), AnyCodable(URL(string: "http ``` ### Dereferencing & Resolving +#### External References +This is currently only available for OAS 3.1 documents (supported by the `OpenAPIKit` module (as opposed to the `OpenAPIKit30` moudle). External dereferencing does not resolve any local (internal) references, it just loads external references into the Document. It does this by storing any loaded externally referenced objects in the Components Object and transforming the reference being resolved from an external reference to an internal one. That way, you can always run internal dereferencing as a second step if you want a fully dereferenced document, but if you simply wanted to load additional referenced files then you can stop after external dereferencing. + +OpenAPIKit leaves it to you to decide how to load external files and where to store the results in the Components Object. It does this by requiring that you provide an implementation of the `ExternalLoader` protocol. You provide a `load` function and a `componentKey` function, both of which accept as input the `URL` to load. A simple mock example implementation from the OpenAPIKit tests will go a long way to showing how the `ExternalLoader` can be set up: + +```swift +struct ExampleLoader: ExternalLoader { + static func load(_ url: URL) async throws -> T where T : Decodable { + // load data from file, perhaps. we will just mock that up for the test: + let data = try await mockData(componentKey(type: T.self, at: url)) + + // We use the YAML decoder mostly for order-stability in this case but it is + // also nice that it will handle both YAML and JSON data. + let decoded = try YAMLDecoder().decode(T.self, from: data) + let finished: T + // while unnecessary, a loader may likely want to attatch some extra info + // to keep track of where a reference was loaded from. + if var extendable = decoded as? VendorExtendable { + extendable.vendorExtensions["x-source-url"] = AnyCodable(url) + finished = extendable as! T + } else { + finished = decoded + } + return finished + } + + static func componentKey(type: T.Type, at url: URL) throws -> OpenAPIKit.OpenAPI.ComponentKey { + // do anything you want here to determine what key the new component should be stored at. + // for the example, we will just transform the URL path into a valid components key: + let urlString = url.pathComponents + .joined(separator: "_") + .replacingOccurrences(of: ".", with: "_") + return try .forceInit(rawValue: urlString) + } +} +``` + +Once you have an `ExternalLoader`, you can call an `OpenAPI.Document`'s `externallyDereference()` method to externally dereference it. You get to choose whether to only load references to a certain depth or to fully resolve references until you run out of them; any given referenced document may itself contain references and these references may point back to things loaded into the Document previously so dereferencing is done recursively up to a given depth (or until fully dereferenced if you use the `.full` depth). + +#### Internal References In addition to looking something up in the `Components` object, you can entirely derefererence many OpenAPIKit types. A dereferenced type has had all of its references looked up (and all of its properties' references, all the way down). You use a value's `dereferenced(in:)` method to fully dereference it. -You can even dereference the whole document with the `OpenAPI.Document` `locallyDereferenced()` method. As the name implies, you can only derefence whole documents that are contained within one file (which is another way of saying that all references are "local"). Specifically, all references must be located within the document's Components Object. +You can even dereference the whole document with the `OpenAPI.Document` `locallyDereferenced()` method. As the name implies, you can only derefence whole documents that are contained within one file (which is another way of saying that all references are "local"). Specifically, all references must be located within the document's Components Object. External dereferencing is done as a separeate step, but you can first dereference externally and then dereference internally if you'd like to perform both. Unlike what happens when you lookup an individual component using the `lookup()` method on `Components`, dereferencing a whole `OpenAPI.Document` will result in type-level changes that guarantee all references are removed. `OpenAPI.Document`'s `locallyDereferenced()` method returns a `DereferencedDocument` which exposes `DereferencedPathItem`s which have `DereferencedParameter`s and `DereferencedOperation`s and so on. From 2f622ce20d58b552acbd1fd681455c691647044d Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Wed, 24 Apr 2024 04:42:52 -0500 Subject: [PATCH 051/110] remove unneeded conformance --- Sources/OpenAPIKit30/Callbacks.swift | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Sources/OpenAPIKit30/Callbacks.swift b/Sources/OpenAPIKit30/Callbacks.swift index 29b1ff612..3e0341915 100644 --- a/Sources/OpenAPIKit30/Callbacks.swift +++ b/Sources/OpenAPIKit30/Callbacks.swift @@ -36,12 +36,6 @@ extension OpenAPI.CallbackURL: LocallyDereferenceable { } } -extension OpenAPI.CallbackURL: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { - return (self, .init()) - } -} - // The following conformance is theoretically unnecessary but the compiler is // only able to find the conformance if we explicitly declare it here, though // it is apparently able to determine the conformance is already satisfied here From e4a2b52a25cbafcb2c5f8a8cf6dce3c6a5599986 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Wed, 24 Apr 2024 05:08:26 -0500 Subject: [PATCH 052/110] go from ExternalLoaderContext to ExternalLoader in OAS 3.0 port. update parameter name in document function to be less awkward with Loader rather than Context. --- .../Components Object/Components.swift | 24 +- Sources/OpenAPIKit/Document/Document.swift | 8 +- Sources/OpenAPIKit/ExternalLoader.swift | 2 +- .../Components Object/Components.swift | 90 ++++--- .../Content/DereferencedContent.swift | 2 +- .../Content/DereferencedContentEncoding.swift | 2 +- Sources/OpenAPIKit30/Document/Document.swift | 24 +- .../Either+ExternallyDereferenceable.swift | 2 +- Sources/OpenAPIKit30/Example.swift | 2 +- Sources/OpenAPIKit30/ExternalLoader.swift | 6 +- .../Header/DereferencedHeader.swift | 2 +- Sources/OpenAPIKit30/JSONReference.swift | 2 +- Sources/OpenAPIKit30/Link.swift | 2 +- .../Operation/DereferencedOperation.swift | 2 +- .../Parameter/DereferencedParameter.swift | 2 +- .../Parameter/DereferencedSchemaContext.swift | 2 +- .../Path Item/DereferencedPathItem.swift | 2 +- .../Request/DereferencedRequest.swift | 2 +- .../Response/DereferencedResponse.swift | 2 +- .../DereferencedJSONSchema.swift | 2 +- .../Security/SecurityScheme.swift | 2 +- Sources/OpenAPIKit30/Server.swift | 2 +- .../Array+ExternallyDereferenceable.swift | 2 +- ...Dictionary+ExternallyDereferenceable.swift | 2 +- .../Optional+ExternallyDereferenceable.swift | 2 +- ...Dictionary+ExternallyDereferenceable.swift | 2 +- .../ExternalDereferencingDocumentTests.swift | 252 ++++++++++++++++++ .../JSONReferenceTests.swift | 50 ++++ 28 files changed, 421 insertions(+), 75 deletions(-) create mode 100644 Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift diff --git a/Sources/OpenAPIKit/Components Object/Components.swift b/Sources/OpenAPIKit/Components Object/Components.swift index a92862d76..e90d318b1 100644 --- a/Sources/OpenAPIKit/Components Object/Components.swift +++ b/Sources/OpenAPIKit/Components Object/Components.swift @@ -320,7 +320,7 @@ extension OpenAPI.Components { } extension OpenAPI.Components { - internal mutating func externallyDereference(in context: Context.Type, depth: ExternalDereferenceDepth = .iterations(1)) async throws { + internal mutating func externallyDereference(with loader: Loader.Type, depth: ExternalDereferenceDepth = .iterations(1)) async throws { if case let .iterations(number) = depth, number <= 0 { return @@ -336,15 +336,15 @@ extension OpenAPI.Components { let oldCallbacks = callbacks let oldPathItems = pathItems - async let (newSchemas, c1) = oldSchemas.externallyDereferenced(with: context) - async let (newResponses, c2) = oldResponses.externallyDereferenced(with: context) - async let (newParameters, c3) = oldParameters.externallyDereferenced(with: context) - async let (newExamples, c4) = oldExamples.externallyDereferenced(with: context) - async let (newRequestBodies, c5) = oldRequestBodies.externallyDereferenced(with: context) - async let (newHeaders, c6) = oldHeaders.externallyDereferenced(with: context) - async let (newSecuritySchemes, c7) = oldSecuritySchemes.externallyDereferenced(with: context) - async let (newCallbacks, c8) = oldCallbacks.externallyDereferenced(with: context) - async let (newPathItems, c9) = oldPathItems.externallyDereferenced(with: context) + async let (newSchemas, c1) = oldSchemas.externallyDereferenced(with: loader) + async let (newResponses, c2) = oldResponses.externallyDereferenced(with: loader) + async let (newParameters, c3) = oldParameters.externallyDereferenced(with: loader) + async let (newExamples, c4) = oldExamples.externallyDereferenced(with: loader) + async let (newRequestBodies, c5) = oldRequestBodies.externallyDereferenced(with: loader) + async let (newHeaders, c6) = oldHeaders.externallyDereferenced(with: loader) + async let (newSecuritySchemes, c7) = oldSecuritySchemes.externallyDereferenced(with: loader) + async let (newCallbacks, c8) = oldCallbacks.externallyDereferenced(with: loader) + async let (newPathItems, c9) = oldPathItems.externallyDereferenced(with: loader) schemas = try await newSchemas responses = try await newResponses @@ -391,9 +391,9 @@ extension OpenAPI.Components { switch depth { case .iterations(let number): - try await externallyDereference(in: context, depth: .iterations(number - 1)) + try await externallyDereference(with: loader, depth: .iterations(number - 1)) case .full: - try await externallyDereference(in: context, depth: .full) + try await externallyDereference(with: loader, depth: .full) } } } diff --git a/Sources/OpenAPIKit/Document/Document.swift b/Sources/OpenAPIKit/Document/Document.swift index 75a703363..9864d8280 100644 --- a/Sources/OpenAPIKit/Document/Document.swift +++ b/Sources/OpenAPIKit/Document/Document.swift @@ -362,7 +362,7 @@ extension OpenAPI.Document { return try DereferencedDocument(self) } - public mutating func externallyDereference(in context: Context.Type, depth: ExternalDereferenceDepth = .iterations(1)) async throws { + public mutating func externallyDereference(with loader: Loader.Type, depth: ExternalDereferenceDepth = .iterations(1)) async throws { if case let .iterations(number) = depth, number <= 0 { return @@ -371,15 +371,15 @@ extension OpenAPI.Document { let oldPaths = paths let oldWebhooks = webhooks - async let (newPaths, c1) = oldPaths.externallyDereferenced(with: context) - async let (newWebhooks, c2) = oldWebhooks.externallyDereferenced(with: context) + async let (newPaths, c1) = oldPaths.externallyDereferenced(with: loader) + async let (newWebhooks, c2) = oldWebhooks.externallyDereferenced(with: loader) paths = try await newPaths webhooks = try await newWebhooks try await components.merge(c1) try await components.merge(c2) - try await components.externallyDereference(in: context, depth: depth) + try await components.externallyDereference(with: loader, depth: depth) } } diff --git a/Sources/OpenAPIKit/ExternalLoader.swift b/Sources/OpenAPIKit/ExternalLoader.swift index 2b62c1699..257264995 100644 --- a/Sources/OpenAPIKit/ExternalLoader.swift +++ b/Sources/OpenAPIKit/ExternalLoader.swift @@ -30,5 +30,5 @@ public protocol ExternalLoader { } public protocol ExternallyDereferenceable { - func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) + func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) } diff --git a/Sources/OpenAPIKit30/Components Object/Components.swift b/Sources/OpenAPIKit30/Components Object/Components.swift index c2762f3be..e186c454b 100644 --- a/Sources/OpenAPIKit30/Components Object/Components.swift +++ b/Sources/OpenAPIKit30/Components Object/Components.swift @@ -283,7 +283,12 @@ extension OpenAPI.Components { } extension OpenAPI.Components { - internal mutating func externallyDereference(in context: Context.Type) async throws { + internal mutating func externallyDereference(with loader: Loader.Type, depth: ExternalDereferenceDepth = .iterations(1)) async throws { + if case let .iterations(number) = depth, + number <= 0 { + return + } + let oldSchemas = schemas let oldResponses = responses let oldParameters = parameters @@ -291,25 +296,18 @@ extension OpenAPI.Components { let oldRequestBodies = requestBodies let oldHeaders = headers let oldSecuritySchemes = securitySchemes - let oldCallbacks = callbacks - - async let (newSchemas, c1) = oldSchemas.externallyDereferenced(with: context) - async let (newResponses, c2) = oldResponses.externallyDereferenced(with: context) - async let (newParameters, c3) = oldParameters.externallyDereferenced(with: context) - async let (newExamples, c4) = oldExamples.externallyDereferenced(with: context) - async let (newRequestBodies, c5) = oldRequestBodies.externallyDereferenced(with: context) - async let (newHeaders, c6) = oldHeaders.externallyDereferenced(with: context) - async let (newSecuritySchemes, c7) = oldSecuritySchemes.externallyDereferenced(with: context) - -// async let (newCallbacks, c8) = oldCallbacks.externallyDereferenced(with: context) - var c8 = OpenAPI.Components() - var newCallbacks = oldCallbacks - for (key, callback) in oldCallbacks { - let (newCallback, components) = try await callback.externallyDereferenced(with: context) - newCallbacks[key] = newCallback - try c8.merge(components) - } + let oldPathItems = pathItems + + async let (newSchemas, c1) = oldSchemas.externallyDereferenced(with: loader) + async let (newResponses, c2) = oldResponses.externallyDereferenced(with: loader) + async let (newParameters, c3) = oldParameters.externallyDereferenced(with: loader) + async let (newExamples, c4) = oldExamples.externallyDereferenced(with: loader) + async let (newRequestBodies, c5) = oldRequestBodies.externallyDereferenced(with: loader) + async let (newHeaders, c6) = oldHeaders.externallyDereferenced(with: loader) + async let (newSecuritySchemes, c7) = oldSecuritySchemes.externallyDereferenced(with: loader) + async let (newCallbacks, c8) = oldCallbacks.externallyDereferenced(with: loader) + async let (newPathItems, c9) = oldPathItems.externallyDereferenced(with: loader) schemas = try await newSchemas responses = try await newResponses @@ -318,18 +316,48 @@ extension OpenAPI.Components { requestBodies = try await newRequestBodies headers = try await newHeaders securitySchemes = try await newSecuritySchemes - - callbacks = newCallbacks - - try await merge(c1) - try await merge(c2) - try await merge(c3) - try await merge(c4) - try await merge(c5) - try await merge(c6) - try await merge(c7) - - try merge(c8) + callbacks = try await newCallbacks + pathItems = try await newPathItems + + let c1Resolved = try await c1 + let c2Resolved = try await c2 + let c3Resolved = try await c3 + let c4Resolved = try await c4 + let c5Resolved = try await c5 + let c6Resolved = try await c6 + let c7Resolved = try await c7 + let c8Resolved = try await c8 + let c9Resolved = try await c9 + + let noNewComponents = + c1Resolved.isEmpty + && c2Resolved.isEmpty + && c3Resolved.isEmpty + && c4Resolved.isEmpty + && c5Resolved.isEmpty + && c6Resolved.isEmpty + && c7Resolved.isEmpty + && c8Resolved.isEmpty + && c9Resolved.isEmpty + + if noNewComponents { return } + + try merge(c1Resolved) + try merge(c2Resolved) + try merge(c3Resolved) + try merge(c4Resolved) + try merge(c5Resolved) + try merge(c6Resolved) + try merge(c7Resolved) + try merge(c8Resolved) + try merge(c9Resolved) + + switch depth { + case .iterations(let number): + try await externallyDereference(with: loader, depth: .iterations(number - 1)) + case .full: + try await externallyDereference(with: loader, depth: .full) + } } } diff --git a/Sources/OpenAPIKit30/Content/DereferencedContent.swift b/Sources/OpenAPIKit30/Content/DereferencedContent.swift index 12281406d..c7f256c1d 100644 --- a/Sources/OpenAPIKit30/Content/DereferencedContent.swift +++ b/Sources/OpenAPIKit30/Content/DereferencedContent.swift @@ -77,7 +77,7 @@ extension OpenAPI.Content: LocallyDereferenceable { } extension OpenAPI.Content: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { let oldSchema = schema async let (newSchema, c1) = oldSchema.externallyDereferenced(with: loader) diff --git a/Sources/OpenAPIKit30/Content/DereferencedContentEncoding.swift b/Sources/OpenAPIKit30/Content/DereferencedContentEncoding.swift index 6100b01ce..605ae7426 100644 --- a/Sources/OpenAPIKit30/Content/DereferencedContentEncoding.swift +++ b/Sources/OpenAPIKit30/Content/DereferencedContentEncoding.swift @@ -58,7 +58,7 @@ extension OpenAPI.Content.Encoding: LocallyDereferenceable { } extension OpenAPI.Content.Encoding: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { let newHeaders: OpenAPI.Header.Map? let newComponents: OpenAPI.Components diff --git a/Sources/OpenAPIKit30/Document/Document.swift b/Sources/OpenAPIKit30/Document/Document.swift index e2315bed0..29f7b0f2f 100644 --- a/Sources/OpenAPIKit30/Document/Document.swift +++ b/Sources/OpenAPIKit30/Document/Document.swift @@ -308,6 +308,17 @@ extension OpenAPI.Document { } } +public enum ExternalDereferenceDepth { + case iterations(Int) + case full +} + +extension ExternalDereferenceDepth: ExpressibleByIntegerLiteral { + public init(integerLiteral value: Int) { + self = .iterations(value) + } +} + extension OpenAPI.Document { /// Create a locally-dereferenced OpenAPI /// Document. @@ -335,15 +346,20 @@ extension OpenAPI.Document { return try DereferencedDocument(self) } - public mutating func externallyDereference(in context: Context.Type) async throws { - let oldPaths = paths + public mutating func externallyDereference(with loader: Loader.Type, depth: ExternalDereferenceDepth = .iterations(1)) async throws { + if case let .iterations(number) = depth, + number <= 0 { + return + } - try await components.externallyDereference(in: context) + let oldPaths = paths - async let (newPaths, c1) = oldPaths.externallyDereferenced(with: context) + async let (newPaths, c1) = oldPaths.externallyDereferenced(with: loader) paths = try await newPaths try await components.merge(c1) + + try await components.externallyDereference(with: loader, depth: depth) } } diff --git a/Sources/OpenAPIKit30/Either/Either+ExternallyDereferenceable.swift b/Sources/OpenAPIKit30/Either/Either+ExternallyDereferenceable.swift index 10e56b368..8c202bb5b 100644 --- a/Sources/OpenAPIKit30/Either/Either+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit30/Either/Either+ExternallyDereferenceable.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore // MARK: - ExternallyDereferenceable extension Either: ExternallyDereferenceable where A: ExternallyDereferenceable, B: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { switch self { case .a(let a): let (newA, components) = try await a.externallyDereferenced(with: loader) diff --git a/Sources/OpenAPIKit30/Example.swift b/Sources/OpenAPIKit30/Example.swift index e5a533187..cdb0b33b4 100644 --- a/Sources/OpenAPIKit30/Example.swift +++ b/Sources/OpenAPIKit30/Example.swift @@ -185,7 +185,7 @@ extension OpenAPI.Example: LocallyDereferenceable { } extension OpenAPI.Example: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { return (self, .init()) } } diff --git a/Sources/OpenAPIKit30/ExternalLoader.swift b/Sources/OpenAPIKit30/ExternalLoader.swift index f937ac917..257264995 100644 --- a/Sources/OpenAPIKit30/ExternalLoader.swift +++ b/Sources/OpenAPIKit30/ExternalLoader.swift @@ -8,10 +8,10 @@ import OpenAPIKitCore import Foundation -/// An `ExternalLoaderContext` enables `OpenAPIKit` to load external references +/// An `ExternalLoader` enables `OpenAPIKit` to load external references /// without knowing the details of what decoder is being used or how new internal /// references should be named. -public protocol ExternalLoaderContext { +public protocol ExternalLoader { /// Load the given URL and decode it as Type `T`. All Types `T` are `Decodable`, so /// the only real responsibility of a `load` function is to locate and load the given /// `URL` and pass its `Data` or `String` (depending on the decoder) to an appropriate @@ -30,5 +30,5 @@ public protocol ExternalLoaderContext { } public protocol ExternallyDereferenceable { - func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) + func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) } diff --git a/Sources/OpenAPIKit30/Header/DereferencedHeader.swift b/Sources/OpenAPIKit30/Header/DereferencedHeader.swift index 891331af9..ec9881c71 100644 --- a/Sources/OpenAPIKit30/Header/DereferencedHeader.swift +++ b/Sources/OpenAPIKit30/Header/DereferencedHeader.swift @@ -84,7 +84,7 @@ extension OpenAPI.Header: LocallyDereferenceable { } extension OpenAPI.Header: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { // if not for a Swift bug, this whole next bit would just be the // next line: diff --git a/Sources/OpenAPIKit30/JSONReference.swift b/Sources/OpenAPIKit30/JSONReference.swift index 57555c304..e6b425224 100644 --- a/Sources/OpenAPIKit30/JSONReference.swift +++ b/Sources/OpenAPIKit30/JSONReference.swift @@ -375,7 +375,7 @@ extension JSONReference: LocallyDereferenceable where ReferenceType: LocallyDere // MARK: - ExternallyDereferenceable extension JSONReference: ExternallyDereferenceable where ReferenceType: ExternallyDereferenceable & Decodable & Equatable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { switch self { case .internal(let ref): return (.internal(ref), .init()) diff --git a/Sources/OpenAPIKit30/Link.swift b/Sources/OpenAPIKit30/Link.swift index a2578ff21..c39d6c219 100644 --- a/Sources/OpenAPIKit30/Link.swift +++ b/Sources/OpenAPIKit30/Link.swift @@ -279,7 +279,7 @@ extension OpenAPI.Link: LocallyDereferenceable { } extension OpenAPI.Link: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { let (newServer, newComponents) = try await server.externallyDereferenced(with: loader) var newLink = self diff --git a/Sources/OpenAPIKit30/Operation/DereferencedOperation.swift b/Sources/OpenAPIKit30/Operation/DereferencedOperation.swift index f4983e6b6..80cfb5590 100644 --- a/Sources/OpenAPIKit30/Operation/DereferencedOperation.swift +++ b/Sources/OpenAPIKit30/Operation/DereferencedOperation.swift @@ -126,7 +126,7 @@ extension OpenAPI.Operation: LocallyDereferenceable { } extension OpenAPI.Operation: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { let oldParameters = parameters let oldRequestBody = requestBody let oldResponses = responses diff --git a/Sources/OpenAPIKit30/Parameter/DereferencedParameter.swift b/Sources/OpenAPIKit30/Parameter/DereferencedParameter.swift index 2d3153847..e0385e1c1 100644 --- a/Sources/OpenAPIKit30/Parameter/DereferencedParameter.swift +++ b/Sources/OpenAPIKit30/Parameter/DereferencedParameter.swift @@ -84,7 +84,7 @@ extension OpenAPI.Parameter: LocallyDereferenceable { } extension OpenAPI.Parameter: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { // if not for a Swift bug, this whole function would just be the // next line: diff --git a/Sources/OpenAPIKit30/Parameter/DereferencedSchemaContext.swift b/Sources/OpenAPIKit30/Parameter/DereferencedSchemaContext.swift index 70fb01f05..3e7d357f4 100644 --- a/Sources/OpenAPIKit30/Parameter/DereferencedSchemaContext.swift +++ b/Sources/OpenAPIKit30/Parameter/DereferencedSchemaContext.swift @@ -70,7 +70,7 @@ extension OpenAPI.Parameter.SchemaContext: LocallyDereferenceable { } extension OpenAPI.Parameter.SchemaContext: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { let oldSchema = schema async let (newSchema, c1) = oldSchema.externallyDereferenced(with: loader) diff --git a/Sources/OpenAPIKit30/Path Item/DereferencedPathItem.swift b/Sources/OpenAPIKit30/Path Item/DereferencedPathItem.swift index 24af18f91..73ac35526 100644 --- a/Sources/OpenAPIKit30/Path Item/DereferencedPathItem.swift +++ b/Sources/OpenAPIKit30/Path Item/DereferencedPathItem.swift @@ -139,7 +139,7 @@ extension OpenAPI.PathItem: LocallyDereferenceable { } extension OpenAPI.PathItem: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { let oldParameters = parameters let oldServers = servers let oldGet = get diff --git a/Sources/OpenAPIKit30/Request/DereferencedRequest.swift b/Sources/OpenAPIKit30/Request/DereferencedRequest.swift index 48c067e3d..e59647cde 100644 --- a/Sources/OpenAPIKit30/Request/DereferencedRequest.swift +++ b/Sources/OpenAPIKit30/Request/DereferencedRequest.swift @@ -63,7 +63,7 @@ extension OpenAPI.Request: LocallyDereferenceable { } extension OpenAPI.Request: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { var newRequest = self let (newContent, components) = try await content.externallyDereferenced(with: loader) diff --git a/Sources/OpenAPIKit30/Response/DereferencedResponse.swift b/Sources/OpenAPIKit30/Response/DereferencedResponse.swift index 3a8e7bdb6..bc11e1603 100644 --- a/Sources/OpenAPIKit30/Response/DereferencedResponse.swift +++ b/Sources/OpenAPIKit30/Response/DereferencedResponse.swift @@ -78,7 +78,7 @@ extension OpenAPI.Response: LocallyDereferenceable { } extension OpenAPI.Response: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { let oldContent = content let oldLinks = links diff --git a/Sources/OpenAPIKit30/Schema Object/DereferencedJSONSchema.swift b/Sources/OpenAPIKit30/Schema Object/DereferencedJSONSchema.swift index e841e0d6b..dcce05e05 100644 --- a/Sources/OpenAPIKit30/Schema Object/DereferencedJSONSchema.swift +++ b/Sources/OpenAPIKit30/Schema Object/DereferencedJSONSchema.swift @@ -410,7 +410,7 @@ extension JSONSchema: LocallyDereferenceable { } extension JSONSchema: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { let newSchema: JSONSchema let newComponents: OpenAPI.Components diff --git a/Sources/OpenAPIKit30/Security/SecurityScheme.swift b/Sources/OpenAPIKit30/Security/SecurityScheme.swift index 38db6f488..364196b67 100644 --- a/Sources/OpenAPIKit30/Security/SecurityScheme.swift +++ b/Sources/OpenAPIKit30/Security/SecurityScheme.swift @@ -252,7 +252,7 @@ extension OpenAPI.SecurityScheme: LocallyDereferenceable { } extension OpenAPI.SecurityScheme: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { return (self, .init()) } } diff --git a/Sources/OpenAPIKit30/Server.swift b/Sources/OpenAPIKit30/Server.swift index 94937a341..5990e297b 100644 --- a/Sources/OpenAPIKit30/Server.swift +++ b/Sources/OpenAPIKit30/Server.swift @@ -246,7 +246,7 @@ extension OpenAPI.Server.Variable { } extension OpenAPI.Server: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { return (self, .init()) } } diff --git a/Sources/OpenAPIKit30/Utility/Array+ExternallyDereferenceable.swift b/Sources/OpenAPIKit30/Utility/Array+ExternallyDereferenceable.swift index 59970df82..262fe4b6a 100644 --- a/Sources/OpenAPIKit30/Utility/Array+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit30/Utility/Array+ExternallyDereferenceable.swift @@ -6,7 +6,7 @@ import OpenAPIKitCore extension Array where Element: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { try await withThrowingTaskGroup(of: (Element, OpenAPI.Components).self) { group in for elem in self { group.addTask { diff --git a/Sources/OpenAPIKit30/Utility/Dictionary+ExternallyDereferenceable.swift b/Sources/OpenAPIKit30/Utility/Dictionary+ExternallyDereferenceable.swift index 75a4cb0b0..d15b123b2 100644 --- a/Sources/OpenAPIKit30/Utility/Dictionary+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit30/Utility/Dictionary+ExternallyDereferenceable.swift @@ -7,7 +7,7 @@ import OpenAPIKitCore extension Dictionary where Value: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { try await withThrowingTaskGroup(of: (Key, Value, OpenAPI.Components).self) { group in for (key, value) in self { group.addTask { diff --git a/Sources/OpenAPIKit30/Utility/Optional+ExternallyDereferenceable.swift b/Sources/OpenAPIKit30/Utility/Optional+ExternallyDereferenceable.swift index 671657f86..79b055b52 100644 --- a/Sources/OpenAPIKit30/Utility/Optional+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit30/Utility/Optional+ExternallyDereferenceable.swift @@ -6,7 +6,7 @@ import OpenAPIKitCore extension Optional where Wrapped: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { guard let wrapped = self else { return (nil, .init()) } return try await wrapped.externallyDereferenced(with: loader) } diff --git a/Sources/OpenAPIKit30/Utility/OrderedDictionary+ExternallyDereferenceable.swift b/Sources/OpenAPIKit30/Utility/OrderedDictionary+ExternallyDereferenceable.swift index 94718b0a6..7636b5e13 100644 --- a/Sources/OpenAPIKit30/Utility/OrderedDictionary+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit30/Utility/OrderedDictionary+ExternallyDereferenceable.swift @@ -9,7 +9,7 @@ import OpenAPIKitCore extension OrderedDictionary where Value: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { try await withThrowingTaskGroup(of: (Key, Value, OpenAPI.Components).self) { group in for (key, value) in self { group.addTask { diff --git a/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift b/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift new file mode 100644 index 000000000..914e575cc --- /dev/null +++ b/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift @@ -0,0 +1,252 @@ +// +// ExternalDereferencingDocumentTests.swift +// + +import Foundation +import Yams +import OpenAPIKit +import XCTest + +final class ExternalDereferencingDocumentTests: XCTestCase { + // temporarily test with an example of the new interface + func test_example() async throws { + + /// An example of implementing a loader context for loading external references + /// into an OpenAPI document. + struct ExampleLoader: ExternalLoader { + static func load(_ url: URL) async throws -> T where T : Decodable { + // load data from file, perhaps. we will just mock that up for the test: + let data = try await mockData(componentKey(type: T.self, at: url)) + + // We use the YAML decoder purely for order-stability. + let decoded = try YAMLDecoder().decode(T.self, from: data) + let finished: T + // while unnecessary, a loader may likely want to attatch some extra info + // to keep track of where a reference was loaded from. This test makes sure + // the following strategy of using vendor extensions works. + if var extendable = decoded as? VendorExtendable { + extendable.vendorExtensions["x-source-url"] = AnyCodable(url) + finished = extendable as! T + } else { + finished = decoded + } + return finished + } + + static func componentKey(type: T.Type, at url: URL) throws -> OpenAPIKit.OpenAPI.ComponentKey { + // do anything you want here to determine what key the new component should be stored at. + // for the example, we will just transform the URL into a valid components key: + let urlString = url.pathComponents.dropFirst() + .joined(separator: "_") + .replacingOccurrences(of: ".", with: "_") + return try .forceInit(rawValue: urlString) + } + + /// Mock up some data, just for the example. + static func mockData(_ key: OpenAPIKit.OpenAPI.ComponentKey) async throws -> Data { + return try XCTUnwrap(files[key.rawValue]) + } + + static let files: [String: Data] = [ + "params_name_json": """ + { + "name": "name", + "description": "a lonely parameter", + "in": "path", + "required": true, + "schema": { + "$ref": "file://./schemas/string_param.json#" + } + } + """, + "schemas_string_param_json": """ + { + "oneOf": [ + { "type": "string" }, + { "$ref": "file://./schemas/basic_object.json" } + ] + } + """, + "schemas_basic_object_json": """ + { + "type": "object" + } + """, + "paths_webhook_json": """ + { + "summary": "just a webhook", + "get": { + "requestBody": { + "$ref": "file://./requests/webhook.json" + }, + "responses": { + "200": { + "$ref": "file://./responses/webhook.json" + } + } + } + } + """, + "requests_webhook_json": """ + { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "body": { + "$ref": "file://./schemas/string_param.json" + } + } + }, + "examples": { + "good": { + "$ref": "file://./examples/good.json" + } + }, + "encoding": { + "enc1": { + "headers": { + "head1": { + "$ref": "file://./headers/webhook.json" + } + } + } + } + } + } + } + """, + "responses_webhook_json": """ + { + "description": "webhook response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "body": { + "type": "string" + }, + "length": { + "type": "integer", + "minimum": 0 + } + } + } + } + }, + "headers": { + "X-Hello": { + "$ref": "file://./headers/webhook.json" + } + } + } + """, + "headers_webhook_json": """ + { + "schema": { + "$ref": "file://./schemas/string_param.json" + } + } + """, + "examples_good_json": """ + { + "value": "{\\"body\\": \\"request me\\"}" + } + """, + "callbacks_one_json": """ + { + "https://callback.site.com/callback": { + "$ref": "file://./paths/callback.json" + } + } + """, + "paths_callback_json": """ + { + "summary": "just a callback", + "get": { + "responses": { + "200": { + "description": "callback response", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + }, + "links": { + "link1": { + "$ref": "file://./links/first.json" + } + } + } + } + } + } + """, + "links_first_json": """ + { + "operationId": "helloOp" + } + """ + ].mapValues { $0.data(using: .utf8)! } + } + + let document = OpenAPI.Document( + info: .init(title: "test document", version: "1.0.0"), + servers: [], + paths: [ + "/hello/{name}": .init( + parameters: [ + .reference(.external(URL(string: "file://./params/name.json")!)) + ], + get: .init( + operationId: "helloOp", + responses: [:], + callbacks: [ + "callback1": .reference(.external(URL(string: "file://./callbacks/one.json")!)) + ] + ) + ), + "/goodbye/{name}": .init( + parameters: [ + .reference(.external(URL(string: "file://./params/name.json")!)) + ] + ), + "/webhook": .reference(.external(URL(string: "file://./paths/webhook.json")!)) + ], + webhooks: [ + "webhook": .reference(.external(URL(string: "file://./paths/webhook.json")!)) + ], + components: .init( + schemas: [ + "name_param": .reference(.external(URL(string: "file://./schemas/string_param.json")!)) + ], + // just to show, no parameters defined within document components : + parameters: [:] + ) + ) + + let encoder = JSONEncoder() + encoder.outputFormatting = .prettyPrinted + + var docCopy1 = document + try await docCopy1.externallyDereference(in: ExampleLoader.self) + try await docCopy1.externallyDereference(in: ExampleLoader.self) + try await docCopy1.externallyDereference(in: ExampleLoader.self) + docCopy1.components.sort() + + var docCopy2 = document + try await docCopy2.externallyDereference(in: ExampleLoader.self, depth: 3) + docCopy2.components.sort() + + var docCopy3 = document + try await docCopy3.externallyDereference(in: ExampleLoader.self, depth: .full) + docCopy3.components.sort() + + XCTAssertEqual(docCopy1, docCopy2) + XCTAssertEqual(docCopy2, docCopy3) + } +} diff --git a/Tests/OpenAPIKit30Tests/JSONReferenceTests.swift b/Tests/OpenAPIKit30Tests/JSONReferenceTests.swift index 60fee797e..516e8e639 100644 --- a/Tests/OpenAPIKit30Tests/JSONReferenceTests.swift +++ b/Tests/OpenAPIKit30Tests/JSONReferenceTests.swift @@ -340,3 +340,53 @@ extension JSONReferenceTests { let reference: JSONReference } } + +// MARK: - External Dereferencing +extension JSONReferenceTests { + func test_externalDerefNoFragment() async throws { + let reference: JSONReference = .external(.init(string: "./schema.json")!) + + let (newReference, components) = try await reference.externallyDereferenced(with: SchemaLoader.self) + + XCTAssertEqual(newReference, .component(named: "__schema_json")) + XCTAssertEqual(components, .init(schemas: ["__schema_json": .string])) + } + + func test_externalDerefFragment() async throws { + let reference: JSONReference = .external(.init(string: "./schema.json#/test")!) + + let (newReference, components) = try await reference.externallyDereferenced(with: SchemaLoader.self) + + XCTAssertEqual(newReference, .component(named: "__schema_json__test")) + XCTAssertEqual(components, .init(schemas: ["__schema_json__test": .string])) + } + + func test_externalDerefExternalComponents() async throws { + let reference: JSONReference = .external(.init(string: "./schema.json#/components/schemas/test")!) + + let (newReference, components) = try await reference.externallyDereferenced(with: SchemaLoader.self) + + XCTAssertEqual(newReference, .component(named: "__schema_json__components_schemas_test")) + XCTAssertEqual(components, .init(schemas: ["__schema_json__components_schemas_test": .string])) + } +} + +// MARK: - Test Types +extension JSONReferenceTests { + struct ReferenceWrapper: Codable, Equatable { + let reference: JSONReference + } + + struct SchemaLoader: ExternalLoader { + static func load(_ url: URL) -> T where T: Decodable { + return JSONSchema.string as! T + } + + static func componentKey(type: T.Type, at url: URL) throws -> OpenAPI.ComponentKey { + return try .forceInit(rawValue: url.absoluteString + .replacingOccurrences(of: "/", with: "_") + .replacingOccurrences(of: "#", with: "_") + .replacingOccurrences(of: ".", with: "_")) + } + } +} From 0df6960c25e7b616cfcd7b496d73b31e418aae83 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Wed, 24 Apr 2024 05:30:22 -0500 Subject: [PATCH 053/110] update OAS 3.0 components merge function. fix tests after refactor. --- .../Components Object/Components.swift | 51 +++++++++++++++---- .../ExternalDereferencingDocumentTests.swift | 23 ++++----- .../JSONReferenceTests.swift | 33 +++++------- .../ExternalDereferencingDocumentTests.swift | 10 ++-- 4 files changed, 69 insertions(+), 48 deletions(-) diff --git a/Sources/OpenAPIKit30/Components Object/Components.swift b/Sources/OpenAPIKit30/Components Object/Components.swift index e186c454b..12b79fced 100644 --- a/Sources/OpenAPIKit30/Components Object/Components.swift +++ b/Sources/OpenAPIKit30/Components Object/Components.swift @@ -78,18 +78,47 @@ extension OpenAPI.Components { public let newComponent: String } + private func detectCollision(type: String) throws -> (_ old: T, _ new: T) throws -> T { + return { old, new in + // theoretically we can detect collisions here, but we would need to compare + // for equality up-to but not including the difference between an external and + // internal reference which is not supported yet. +// if(old == new) { return old } +// throw ComponentCollision(componentType: type, existingComponent: String(describing:old), newComponent: String(describing:new)) + + // Given we aren't ensuring there are no collisions, the old version is going to be + // the one more likely to have been _further_ dereferenced than the new record, so + // we keep that version. + return old + } + } + public mutating func merge(_ other: OpenAPI.Components) throws { - try schemas.merge(other.schemas, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "schema", existingComponent: String(describing: a), newComponent: String(describing: b)) }) - try responses.merge(other.responses, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "responses", existingComponent: String(describing: a), newComponent: String(describing: b)) }) - try parameters.merge(other.parameters, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "parameters", existingComponent: String(describing: a), newComponent: String(describing: b)) }) - try examples.merge(other.examples, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "examples", existingComponent: String(describing: a), newComponent: String(describing: b)) }) - try requestBodies.merge(other.requestBodies, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "requestBodies", existingComponent: String(describing: a), newComponent: String(describing: b)) }) - try headers.merge(other.headers, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "headers", existingComponent: String(describing: a), newComponent: String(describing: b)) }) - try securitySchemes.merge(other.securitySchemes, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "securitySchemes", existingComponent: String(describing: a), newComponent: String(describing: b)) }) - try links.merge(other.links, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "links", existingComponent: String(describing: a), newComponent: String(describing: b)) }) - try callbacks.merge(other.callbacks, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "callbacks", existingComponent: String(describing: a), newComponent: String(describing: b)) }) - try pathItems.merge(other.pathItems, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "pathItems", existingComponent: String(describing: a), newComponent: String(describing: b)) }) - try vendorExtensions.merge(other.vendorExtensions, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "vendorExtensions", existingComponent: String(describing: a), newComponent: String(describing: b)) }) + try schemas.merge(other.schemas, uniquingKeysWith: detectCollision(type: "schema")) + try responses.merge(other.responses, uniquingKeysWith: detectCollision(type: "responses")) + try parameters.merge(other.parameters, uniquingKeysWith: detectCollision(type: "parameters")) + try examples.merge(other.examples, uniquingKeysWith: detectCollision(type: "examples")) + try requestBodies.merge(other.requestBodies, uniquingKeysWith: detectCollision(type: "requestBodies")) + try headers.merge(other.headers, uniquingKeysWith: detectCollision(type: "headers")) + try securitySchemes.merge(other.securitySchemes, uniquingKeysWith: detectCollision(type: "securitySchemes")) + try links.merge(other.links, uniquingKeysWith: detectCollision(type: "links")) + try callbacks.merge(other.callbacks, uniquingKeysWith: detectCollision(type: "callbacks")) + try pathItems.merge(other.pathItems, uniquingKeysWith: detectCollision(type: "pathItems")) + try vendorExtensions.merge(other.vendorExtensions, uniquingKeysWith: detectCollision(type: "vendorExtensions")) + } + + /// Sort the components within each type by the component key. + public mutating func sort() { + schemas.sortKeys() + responses.sortKeys() + parameters.sortKeys() + examples.sortKeys() + requestBodies.sortKeys() + headers.sortKeys() + securitySchemes.sortKeys() + links.sortKeys() + callbacks.sortKeys() + pathItems.sortKeys() } } diff --git a/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift b/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift index 914e575cc..527fa2a5c 100644 --- a/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift +++ b/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift @@ -4,7 +4,7 @@ import Foundation import Yams -import OpenAPIKit +import OpenAPIKit30 import XCTest final class ExternalDereferencingDocumentTests: XCTestCase { @@ -25,7 +25,9 @@ final class ExternalDereferencingDocumentTests: XCTestCase { // to keep track of where a reference was loaded from. This test makes sure // the following strategy of using vendor extensions works. if var extendable = decoded as? VendorExtendable { - extendable.vendorExtensions["x-source-url"] = AnyCodable(url) + //TODO: revisit vendor extensions mutability +#warning("revisit vendor extensions mutability") +// extendable.vendorExtensions["x-source-url"] = AnyCodable(url) finished = extendable as! T } else { finished = decoded @@ -33,7 +35,7 @@ final class ExternalDereferencingDocumentTests: XCTestCase { return finished } - static func componentKey(type: T.Type, at url: URL) throws -> OpenAPIKit.OpenAPI.ComponentKey { + static func componentKey(type: T.Type, at url: URL) throws -> OpenAPIKit30.OpenAPI.ComponentKey { // do anything you want here to determine what key the new component should be stored at. // for the example, we will just transform the URL into a valid components key: let urlString = url.pathComponents.dropFirst() @@ -43,7 +45,7 @@ final class ExternalDereferencingDocumentTests: XCTestCase { } /// Mock up some data, just for the example. - static func mockData(_ key: OpenAPIKit.OpenAPI.ComponentKey) async throws -> Data { + static func mockData(_ key: OpenAPIKit30.OpenAPI.ComponentKey) async throws -> Data { return try XCTUnwrap(files[key.rawValue]) } @@ -217,9 +219,6 @@ final class ExternalDereferencingDocumentTests: XCTestCase { ), "/webhook": .reference(.external(URL(string: "file://./paths/webhook.json")!)) ], - webhooks: [ - "webhook": .reference(.external(URL(string: "file://./paths/webhook.json")!)) - ], components: .init( schemas: [ "name_param": .reference(.external(URL(string: "file://./schemas/string_param.json")!)) @@ -233,17 +232,17 @@ final class ExternalDereferencingDocumentTests: XCTestCase { encoder.outputFormatting = .prettyPrinted var docCopy1 = document - try await docCopy1.externallyDereference(in: ExampleLoader.self) - try await docCopy1.externallyDereference(in: ExampleLoader.self) - try await docCopy1.externallyDereference(in: ExampleLoader.self) + try await docCopy1.externallyDereference(with: ExampleLoader.self) + try await docCopy1.externallyDereference(with: ExampleLoader.self) + try await docCopy1.externallyDereference(with: ExampleLoader.self) docCopy1.components.sort() var docCopy2 = document - try await docCopy2.externallyDereference(in: ExampleLoader.self, depth: 3) + try await docCopy2.externallyDereference(with: ExampleLoader.self, depth: 3) docCopy2.components.sort() var docCopy3 = document - try await docCopy3.externallyDereference(in: ExampleLoader.self, depth: .full) + try await docCopy3.externallyDereference(with: ExampleLoader.self, depth: .full) docCopy3.components.sort() XCTAssertEqual(docCopy1, docCopy2) diff --git a/Tests/OpenAPIKit30Tests/JSONReferenceTests.swift b/Tests/OpenAPIKit30Tests/JSONReferenceTests.swift index 516e8e639..5aca879c0 100644 --- a/Tests/OpenAPIKit30Tests/JSONReferenceTests.swift +++ b/Tests/OpenAPIKit30Tests/JSONReferenceTests.swift @@ -339,6 +339,19 @@ extension JSONReferenceTests { struct ReferenceWrapper: Codable, Equatable { let reference: JSONReference } + + struct SchemaLoader: ExternalLoader { + static func load(_ url: URL) -> T where T: Decodable { + return JSONSchema.string as! T + } + + static func componentKey(type: T.Type, at url: URL) throws -> OpenAPI.ComponentKey { + return try .forceInit(rawValue: url.absoluteString + .replacingOccurrences(of: "/", with: "_") + .replacingOccurrences(of: "#", with: "_") + .replacingOccurrences(of: ".", with: "_")) + } + } } // MARK: - External Dereferencing @@ -370,23 +383,3 @@ extension JSONReferenceTests { XCTAssertEqual(components, .init(schemas: ["__schema_json__components_schemas_test": .string])) } } - -// MARK: - Test Types -extension JSONReferenceTests { - struct ReferenceWrapper: Codable, Equatable { - let reference: JSONReference - } - - struct SchemaLoader: ExternalLoader { - static func load(_ url: URL) -> T where T: Decodable { - return JSONSchema.string as! T - } - - static func componentKey(type: T.Type, at url: URL) throws -> OpenAPI.ComponentKey { - return try .forceInit(rawValue: url.absoluteString - .replacingOccurrences(of: "/", with: "_") - .replacingOccurrences(of: "#", with: "_") - .replacingOccurrences(of: ".", with: "_")) - } - } -} diff --git a/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift b/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift index 914e575cc..35bf2429d 100644 --- a/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift @@ -233,17 +233,17 @@ final class ExternalDereferencingDocumentTests: XCTestCase { encoder.outputFormatting = .prettyPrinted var docCopy1 = document - try await docCopy1.externallyDereference(in: ExampleLoader.self) - try await docCopy1.externallyDereference(in: ExampleLoader.self) - try await docCopy1.externallyDereference(in: ExampleLoader.self) + try await docCopy1.externallyDereference(with: ExampleLoader.self) + try await docCopy1.externallyDereference(with: ExampleLoader.self) + try await docCopy1.externallyDereference(with: ExampleLoader.self) docCopy1.components.sort() var docCopy2 = document - try await docCopy2.externallyDereference(in: ExampleLoader.self, depth: 3) + try await docCopy2.externallyDereference(with: ExampleLoader.self, depth: 3) docCopy2.components.sort() var docCopy3 = document - try await docCopy3.externallyDereference(in: ExampleLoader.self, depth: .full) + try await docCopy3.externallyDereference(with: ExampleLoader.self, depth: .full) docCopy3.components.sort() XCTAssertEqual(docCopy1, docCopy2) From 8047fe7656c9984b4fb692026155c3e61fc2932d Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Wed, 24 Apr 2024 05:35:36 -0500 Subject: [PATCH 054/110] finish renaming Context to Loader internally --- Sources/OpenAPIKit/Content/DereferencedContent.swift | 2 +- Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift | 2 +- .../OpenAPIKit/Either/Either+ExternallyDereferenceable.swift | 2 +- Sources/OpenAPIKit/Example.swift | 2 +- Sources/OpenAPIKit/Header/DereferencedHeader.swift | 2 +- Sources/OpenAPIKit/JSONReference.swift | 4 ++-- Sources/OpenAPIKit/Link.swift | 2 +- Sources/OpenAPIKit/Operation/DereferencedOperation.swift | 2 +- Sources/OpenAPIKit/Parameter/DereferencedParameter.swift | 2 +- Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift | 2 +- Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift | 2 +- Sources/OpenAPIKit/Request/DereferencedRequest.swift | 2 +- Sources/OpenAPIKit/Response/DereferencedResponse.swift | 2 +- Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift | 2 +- Sources/OpenAPIKit/Security/SecurityScheme.swift | 2 +- Sources/OpenAPIKit/Server.swift | 2 +- .../OpenAPIKit/Utility/Array+ExternallyDereferenceable.swift | 2 +- .../Utility/Dictionary+ExternallyDereferenceable.swift | 2 +- .../Utility/Optional+ExternallyDereferenceable.swift | 2 +- .../Utility/OrderedDictionary+ExternallyDereferenceable.swift | 2 +- 20 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Sources/OpenAPIKit/Content/DereferencedContent.swift b/Sources/OpenAPIKit/Content/DereferencedContent.swift index 992eea1a7..afdcd582e 100644 --- a/Sources/OpenAPIKit/Content/DereferencedContent.swift +++ b/Sources/OpenAPIKit/Content/DereferencedContent.swift @@ -77,7 +77,7 @@ extension OpenAPI.Content: LocallyDereferenceable { } extension OpenAPI.Content: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { let oldSchema = schema async let (newSchema, c1) = oldSchema.externallyDereferenced(with: loader) diff --git a/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift b/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift index f6d43cb5e..605ae7426 100644 --- a/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift +++ b/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift @@ -58,7 +58,7 @@ extension OpenAPI.Content.Encoding: LocallyDereferenceable { } extension OpenAPI.Content.Encoding: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { let newHeaders: OpenAPI.Header.Map? let newComponents: OpenAPI.Components diff --git a/Sources/OpenAPIKit/Either/Either+ExternallyDereferenceable.swift b/Sources/OpenAPIKit/Either/Either+ExternallyDereferenceable.swift index 5dfe12868..8c202bb5b 100644 --- a/Sources/OpenAPIKit/Either/Either+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit/Either/Either+ExternallyDereferenceable.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore // MARK: - ExternallyDereferenceable extension Either: ExternallyDereferenceable where A: ExternallyDereferenceable, B: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { switch self { case .a(let a): let (newA, components) = try await a.externallyDereferenced(with: loader) diff --git a/Sources/OpenAPIKit/Example.swift b/Sources/OpenAPIKit/Example.swift index d61fc8392..74c9f7226 100644 --- a/Sources/OpenAPIKit/Example.swift +++ b/Sources/OpenAPIKit/Example.swift @@ -209,7 +209,7 @@ extension OpenAPI.Example: LocallyDereferenceable { } extension OpenAPI.Example: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { return (self, .init()) } } diff --git a/Sources/OpenAPIKit/Header/DereferencedHeader.swift b/Sources/OpenAPIKit/Header/DereferencedHeader.swift index 554a5b266..ec9881c71 100644 --- a/Sources/OpenAPIKit/Header/DereferencedHeader.swift +++ b/Sources/OpenAPIKit/Header/DereferencedHeader.swift @@ -84,7 +84,7 @@ extension OpenAPI.Header: LocallyDereferenceable { } extension OpenAPI.Header: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { // if not for a Swift bug, this whole next bit would just be the // next line: diff --git a/Sources/OpenAPIKit/JSONReference.swift b/Sources/OpenAPIKit/JSONReference.swift index 75621e00a..7d59430f5 100644 --- a/Sources/OpenAPIKit/JSONReference.swift +++ b/Sources/OpenAPIKit/JSONReference.swift @@ -538,7 +538,7 @@ extension JSONReference: LocallyDereferenceable where ReferenceType: LocallyDere } extension JSONReference: ExternallyDereferenceable where ReferenceType: ExternallyDereferenceable & Decodable & Equatable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { switch self { case .internal(let ref): return (.internal(ref), .init()) @@ -580,7 +580,7 @@ extension OpenAPI.Reference: LocallyDereferenceable where ReferenceType: Locally } extension OpenAPI.Reference: ExternallyDereferenceable where ReferenceType: ExternallyDereferenceable & Decodable & Equatable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { let (newRef, components) = try await jsonReference.externallyDereferenced(with: loader) return (.init(newRef), components) } diff --git a/Sources/OpenAPIKit/Link.swift b/Sources/OpenAPIKit/Link.swift index be416dcff..dde15299e 100644 --- a/Sources/OpenAPIKit/Link.swift +++ b/Sources/OpenAPIKit/Link.swift @@ -290,7 +290,7 @@ extension OpenAPI.Link: LocallyDereferenceable { } extension OpenAPI.Link: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { let (newServer, newComponents) = try await server.externallyDereferenced(with: loader) var newLink = self diff --git a/Sources/OpenAPIKit/Operation/DereferencedOperation.swift b/Sources/OpenAPIKit/Operation/DereferencedOperation.swift index dfdd14c9d..f4139fcd5 100644 --- a/Sources/OpenAPIKit/Operation/DereferencedOperation.swift +++ b/Sources/OpenAPIKit/Operation/DereferencedOperation.swift @@ -126,7 +126,7 @@ extension OpenAPI.Operation: LocallyDereferenceable { } extension OpenAPI.Operation: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { let oldParameters = parameters let oldRequestBody = requestBody let oldResponses = responses diff --git a/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift b/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift index b2b6b9604..e0385e1c1 100644 --- a/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift +++ b/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift @@ -84,7 +84,7 @@ extension OpenAPI.Parameter: LocallyDereferenceable { } extension OpenAPI.Parameter: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { // if not for a Swift bug, this whole function would just be the // next line: diff --git a/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift b/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift index cea39b921..68f9bb772 100644 --- a/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift +++ b/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift @@ -71,7 +71,7 @@ extension OpenAPI.Parameter.SchemaContext: LocallyDereferenceable { } extension OpenAPI.Parameter.SchemaContext: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { let oldSchema = schema async let (newSchema, c1) = oldSchema.externallyDereferenced(with: loader) diff --git a/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift b/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift index fc284b1df..cc577e53f 100644 --- a/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift +++ b/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift @@ -140,7 +140,7 @@ extension OpenAPI.PathItem: LocallyDereferenceable { } extension OpenAPI.PathItem: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { let oldParameters = parameters let oldServers = servers let oldGet = get diff --git a/Sources/OpenAPIKit/Request/DereferencedRequest.swift b/Sources/OpenAPIKit/Request/DereferencedRequest.swift index a5dce43bd..39d0c2409 100644 --- a/Sources/OpenAPIKit/Request/DereferencedRequest.swift +++ b/Sources/OpenAPIKit/Request/DereferencedRequest.swift @@ -63,7 +63,7 @@ extension OpenAPI.Request: LocallyDereferenceable { } extension OpenAPI.Request: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { var newRequest = self let (newContent, components) = try await content.externallyDereferenced(with: loader) diff --git a/Sources/OpenAPIKit/Response/DereferencedResponse.swift b/Sources/OpenAPIKit/Response/DereferencedResponse.swift index 363e33448..bc11e1603 100644 --- a/Sources/OpenAPIKit/Response/DereferencedResponse.swift +++ b/Sources/OpenAPIKit/Response/DereferencedResponse.swift @@ -78,7 +78,7 @@ extension OpenAPI.Response: LocallyDereferenceable { } extension OpenAPI.Response: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { let oldContent = content let oldLinks = links diff --git a/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift b/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift index faeccde28..dfe1af9e3 100644 --- a/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift +++ b/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift @@ -536,7 +536,7 @@ extension JSONSchema: LocallyDereferenceable { } extension JSONSchema: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { let newSchema: JSONSchema let newComponents: OpenAPI.Components diff --git a/Sources/OpenAPIKit/Security/SecurityScheme.swift b/Sources/OpenAPIKit/Security/SecurityScheme.swift index a304bccfd..c4af0ff78 100644 --- a/Sources/OpenAPIKit/Security/SecurityScheme.swift +++ b/Sources/OpenAPIKit/Security/SecurityScheme.swift @@ -275,7 +275,7 @@ extension OpenAPI.SecurityScheme: LocallyDereferenceable { } extension OpenAPI.SecurityScheme: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { return (self, .init()) } } diff --git a/Sources/OpenAPIKit/Server.swift b/Sources/OpenAPIKit/Server.swift index 4bea3607f..80f4bec07 100644 --- a/Sources/OpenAPIKit/Server.swift +++ b/Sources/OpenAPIKit/Server.swift @@ -260,7 +260,7 @@ extension OpenAPI.Server.Variable { } extension OpenAPI.Server: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { return (self, .init()) } } diff --git a/Sources/OpenAPIKit/Utility/Array+ExternallyDereferenceable.swift b/Sources/OpenAPIKit/Utility/Array+ExternallyDereferenceable.swift index 1435c2c95..a67002587 100644 --- a/Sources/OpenAPIKit/Utility/Array+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit/Utility/Array+ExternallyDereferenceable.swift @@ -6,7 +6,7 @@ import OpenAPIKitCore extension Array where Element: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { try await withThrowingTaskGroup(of: (Int, (Element, OpenAPI.Components)).self) { group in for (idx, elem) in zip(self.indices, self) { group.addTask { diff --git a/Sources/OpenAPIKit/Utility/Dictionary+ExternallyDereferenceable.swift b/Sources/OpenAPIKit/Utility/Dictionary+ExternallyDereferenceable.swift index 2ba0a8fcb..d15b123b2 100644 --- a/Sources/OpenAPIKit/Utility/Dictionary+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit/Utility/Dictionary+ExternallyDereferenceable.swift @@ -7,7 +7,7 @@ import OpenAPIKitCore extension Dictionary where Value: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { try await withThrowingTaskGroup(of: (Key, Value, OpenAPI.Components).self) { group in for (key, value) in self { group.addTask { diff --git a/Sources/OpenAPIKit/Utility/Optional+ExternallyDereferenceable.swift b/Sources/OpenAPIKit/Utility/Optional+ExternallyDereferenceable.swift index adf2b1816..79b055b52 100644 --- a/Sources/OpenAPIKit/Utility/Optional+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit/Utility/Optional+ExternallyDereferenceable.swift @@ -6,7 +6,7 @@ import OpenAPIKitCore extension Optional where Wrapped: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { guard let wrapped = self else { return (nil, .init()) } return try await wrapped.externallyDereferenced(with: loader) } diff --git a/Sources/OpenAPIKit/Utility/OrderedDictionary+ExternallyDereferenceable.swift b/Sources/OpenAPIKit/Utility/OrderedDictionary+ExternallyDereferenceable.swift index 283aab817..38ab9bbc3 100644 --- a/Sources/OpenAPIKit/Utility/OrderedDictionary+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit/Utility/OrderedDictionary+ExternallyDereferenceable.swift @@ -9,7 +9,7 @@ import OpenAPIKitCore extension OrderedDictionary where Value: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Context.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { try await withThrowingTaskGroup(of: (Key, Value, OpenAPI.Components).self) { group in for (key, value) in self { group.addTask { From 67f57a5576b0d32e1096092e02f90ae915ccde12 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Wed, 24 Apr 2024 05:51:32 -0500 Subject: [PATCH 055/110] remove now unneeded workaround in path item dereferencing --- .../Path Item/DereferencedPathItem.swift | 83 ++++++------------- .../Response/DereferencedResponse.swift | 4 +- .../Path Item/DereferencedPathItem.swift | 83 ++++++------------- .../Response/DereferencedResponse.swift | 4 +- 4 files changed, 58 insertions(+), 116 deletions(-) diff --git a/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift b/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift index cc577e53f..48d9b038a 100644 --- a/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift +++ b/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift @@ -154,22 +154,39 @@ extension OpenAPI.PathItem: ExternallyDereferenceable { async let (newParameters, c1) = oldParameters.externallyDereferenced(with: loader) // async let (newServers, c2) = oldServers.externallyDereferenced(with: loader) -// async let (newGet, c3) = oldGet.externallyDereferenced(with: loader) -// async let (newPut, c4) = oldPut.externallyDereferenced(with: loader) -// async let (newPost, c5) = oldPost.externallyDereferenced(with: loader) -// async let (newDelete, c6) = oldDelete.externallyDereferenced(with: loader) -// async let (newOptions, c7) = oldOptions.externallyDereferenced(with: loader) -// async let (newHead, c8) = oldHead.externallyDereferenced(with: loader) -// async let (newPatch, c9) = oldPatch.externallyDereferenced(with: loader) -// async let (newTrace, c10) = oldTrace.externallyDereferenced(with: loader) + async let (newGet, c3) = oldGet.externallyDereferenced(with: loader) + async let (newPut, c4) = oldPut.externallyDereferenced(with: loader) + async let (newPost, c5) = oldPost.externallyDereferenced(with: loader) + async let (newDelete, c6) = oldDelete.externallyDereferenced(with: loader) + async let (newOptions, c7) = oldOptions.externallyDereferenced(with: loader) + async let (newHead, c8) = oldHead.externallyDereferenced(with: loader) + async let (newPatch, c9) = oldPatch.externallyDereferenced(with: loader) + async let (newTrace, c10) = oldTrace.externallyDereferenced(with: loader) var pathItem = self var newComponents = try await c1 // ideally we would async let all of the props above and then set them here, // but for now since there seems to be some sort of compiler bug we will do - // most of them in if lets below + // newServers in an if let below pathItem.parameters = try await newParameters + pathItem.get = try await newGet + pathItem.put = try await newPut + pathItem.post = try await newPost + pathItem.delete = try await newDelete + pathItem.options = try await newOptions + pathItem.head = try await newHead + pathItem.patch = try await newPatch + pathItem.trace = try await newTrace + + try await newComponents.merge(c3) + try await newComponents.merge(c4) + try await newComponents.merge(c5) + try await newComponents.merge(c6) + try await newComponents.merge(c7) + try await newComponents.merge(c8) + try await newComponents.merge(c9) + try await newComponents.merge(c10) if let oldServers { async let (newServers, c2) = oldServers.externallyDereferenced(with: loader) @@ -177,54 +194,6 @@ extension OpenAPI.PathItem: ExternallyDereferenceable { try await newComponents.merge(c2) } - if let oldGet { - async let (newGet, c3) = oldGet.externallyDereferenced(with: loader) - pathItem.get = try await newGet - try await newComponents.merge(c3) - } - - if let oldPut { - async let (newPut, c4) = oldPut.externallyDereferenced(with: loader) - pathItem.put = try await newPut - try await newComponents.merge(c4) - } - - if let oldPost { - async let (newPost, c5) = oldPost.externallyDereferenced(with: loader) - pathItem.post = try await newPost - try await newComponents.merge(c5) - } - - if let oldDelete { - async let (newDelete, c6) = oldDelete.externallyDereferenced(with: loader) - pathItem.delete = try await newDelete - try await newComponents.merge(c6) - } - - if let oldOptions { - async let (newOptions, c7) = oldOptions.externallyDereferenced(with: loader) - pathItem.options = try await newOptions - try await newComponents.merge(c7) - } - - if let oldHead { - async let (newHead, c8) = oldHead.externallyDereferenced(with: loader) - pathItem.head = try await newHead - try await newComponents.merge(c8) - } - - if let oldPatch { - async let (newPatch, c9) = oldPatch.externallyDereferenced(with: loader) - pathItem.patch = try await newPatch - try await newComponents.merge(c9) - } - - if let oldTrace { - async let (newTrace, c10) = oldTrace.externallyDereferenced(with: loader) - pathItem.trace = try await newTrace - try await newComponents.merge(c10) - } - return (pathItem, newComponents) } } diff --git a/Sources/OpenAPIKit/Response/DereferencedResponse.swift b/Sources/OpenAPIKit/Response/DereferencedResponse.swift index bc11e1603..add208773 100644 --- a/Sources/OpenAPIKit/Response/DereferencedResponse.swift +++ b/Sources/OpenAPIKit/Response/DereferencedResponse.swift @@ -81,9 +81,11 @@ extension OpenAPI.Response: ExternallyDereferenceable { public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { let oldContent = content let oldLinks = links + let oldHeaders = headers async let (newContent, c1) = oldContent.externallyDereferenced(with: loader) async let (newLinks, c2) = oldLinks.externallyDereferenced(with: loader) +// async let (newHeaders, c3) = oldHeaders.externallyDereferenced(with: loader) var response = self response.content = try await newContent @@ -92,7 +94,7 @@ extension OpenAPI.Response: ExternallyDereferenceable { var components = try await c1 try await components.merge(c2) - if let oldHeaders = headers { + if let oldHeaders { let (newHeaders, c3) = try await oldHeaders.externallyDereferenced(with: loader) response.headers = newHeaders try components.merge(c3) diff --git a/Sources/OpenAPIKit30/Path Item/DereferencedPathItem.swift b/Sources/OpenAPIKit30/Path Item/DereferencedPathItem.swift index 73ac35526..9b7e5ac49 100644 --- a/Sources/OpenAPIKit30/Path Item/DereferencedPathItem.swift +++ b/Sources/OpenAPIKit30/Path Item/DereferencedPathItem.swift @@ -153,22 +153,39 @@ extension OpenAPI.PathItem: ExternallyDereferenceable { async let (newParameters, c1) = oldParameters.externallyDereferenced(with: loader) // async let (newServers, c2) = oldServers.externallyDereferenced(with: loader) -// async let (newGet, c3) = oldGet.externallyDereferenced(with: loader) -// async let (newPut, c4) = oldPut.externallyDereferenced(with: loader) -// async let (newPost, c5) = oldPost.externallyDereferenced(with: loader) -// async let (newDelete, c6) = oldDelete.externallyDereferenced(with: loader) -// async let (newOptions, c7) = oldOptions.externallyDereferenced(with: loader) -// async let (newHead, c8) = oldHead.externallyDereferenced(with: loader) -// async let (newPatch, c9) = oldPatch.externallyDereferenced(with: loader) -// async let (newTrace, c10) = oldTrace.externallyDereferenced(with: loader) + async let (newGet, c3) = oldGet.externallyDereferenced(with: loader) + async let (newPut, c4) = oldPut.externallyDereferenced(with: loader) + async let (newPost, c5) = oldPost.externallyDereferenced(with: loader) + async let (newDelete, c6) = oldDelete.externallyDereferenced(with: loader) + async let (newOptions, c7) = oldOptions.externallyDereferenced(with: loader) + async let (newHead, c8) = oldHead.externallyDereferenced(with: loader) + async let (newPatch, c9) = oldPatch.externallyDereferenced(with: loader) + async let (newTrace, c10) = oldTrace.externallyDereferenced(with: loader) var pathItem = self var newComponents = try await c1 // ideally we would async let all of the props above and then set them here, // but for now since there seems to be some sort of compiler bug we will do - // most of them in if lets below + // newServers in an if let below pathItem.parameters = try await newParameters + pathItem.get = try await newGet + pathItem.put = try await newPut + pathItem.post = try await newPost + pathItem.delete = try await newDelete + pathItem.options = try await newOptions + pathItem.head = try await newHead + pathItem.patch = try await newPatch + pathItem.trace = try await newTrace + + try await newComponents.merge(c3) + try await newComponents.merge(c4) + try await newComponents.merge(c5) + try await newComponents.merge(c6) + try await newComponents.merge(c7) + try await newComponents.merge(c8) + try await newComponents.merge(c9) + try await newComponents.merge(c10) if let oldServers { async let (newServers, c2) = oldServers.externallyDereferenced(with: loader) @@ -176,54 +193,6 @@ extension OpenAPI.PathItem: ExternallyDereferenceable { try await newComponents.merge(c2) } - if let oldGet { - async let (newGet, c3) = oldGet.externallyDereferenced(with: loader) - pathItem.get = try await newGet - try await newComponents.merge(c3) - } - - if let oldPut { - async let (newPut, c4) = oldPut.externallyDereferenced(with: loader) - pathItem.put = try await newPut - try await newComponents.merge(c4) - } - - if let oldPost { - async let (newPost, c5) = oldPost.externallyDereferenced(with: loader) - pathItem.post = try await newPost - try await newComponents.merge(c5) - } - - if let oldDelete { - async let (newDelete, c6) = oldDelete.externallyDereferenced(with: loader) - pathItem.delete = try await newDelete - try await newComponents.merge(c6) - } - - if let oldOptions { - async let (newOptions, c7) = oldOptions.externallyDereferenced(with: loader) - pathItem.options = try await newOptions - try await newComponents.merge(c7) - } - - if let oldHead { - async let (newHead, c8) = oldHead.externallyDereferenced(with: loader) - pathItem.head = try await newHead - try await newComponents.merge(c8) - } - - if let oldPatch { - async let (newPatch, c9) = oldPatch.externallyDereferenced(with: loader) - pathItem.patch = try await newPatch - try await newComponents.merge(c9) - } - - if let oldTrace { - async let (newTrace, c10) = oldTrace.externallyDereferenced(with: loader) - pathItem.trace = try await newTrace - try await newComponents.merge(c10) - } - return (pathItem, newComponents) } } diff --git a/Sources/OpenAPIKit30/Response/DereferencedResponse.swift b/Sources/OpenAPIKit30/Response/DereferencedResponse.swift index bc11e1603..add208773 100644 --- a/Sources/OpenAPIKit30/Response/DereferencedResponse.swift +++ b/Sources/OpenAPIKit30/Response/DereferencedResponse.swift @@ -81,9 +81,11 @@ extension OpenAPI.Response: ExternallyDereferenceable { public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { let oldContent = content let oldLinks = links + let oldHeaders = headers async let (newContent, c1) = oldContent.externallyDereferenced(with: loader) async let (newLinks, c2) = oldLinks.externallyDereferenced(with: loader) +// async let (newHeaders, c3) = oldHeaders.externallyDereferenced(with: loader) var response = self response.content = try await newContent @@ -92,7 +94,7 @@ extension OpenAPI.Response: ExternallyDereferenceable { var components = try await c1 try await components.merge(c2) - if let oldHeaders = headers { + if let oldHeaders { let (newHeaders, c3) = try await oldHeaders.externallyDereferenced(with: loader) response.headers = newHeaders try components.merge(c3) From 47884267048332c6a24edc9312a3657e167e1664 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Wed, 24 Apr 2024 05:57:03 -0500 Subject: [PATCH 056/110] update remaining two utility implementations of external dereferencing in the OAS 3.0 port --- .../Array+ExternallyDereferenceable.swift | 17 ++++++++++------- ...edDictionary+ExternallyDereferenceable.swift | 3 +++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Sources/OpenAPIKit30/Utility/Array+ExternallyDereferenceable.swift b/Sources/OpenAPIKit30/Utility/Array+ExternallyDereferenceable.swift index 262fe4b6a..a67002587 100644 --- a/Sources/OpenAPIKit30/Utility/Array+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit30/Utility/Array+ExternallyDereferenceable.swift @@ -7,21 +7,24 @@ import OpenAPIKitCore extension Array where Element: ExternallyDereferenceable { public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { - try await withThrowingTaskGroup(of: (Element, OpenAPI.Components).self) { group in - for elem in self { + try await withThrowingTaskGroup(of: (Int, (Element, OpenAPI.Components)).self) { group in + for (idx, elem) in zip(self.indices, self) { group.addTask { - return try await elem.externallyDereferenced(with: loader) + return try await (idx, elem.externallyDereferenced(with: loader)) } } - var newElems = Self() + var newElems = Array<(Int, Element)>() var newComponents = OpenAPI.Components() - for try await (elem, components) in group { - newElems.append(elem) + for try await (idx, (elem, components)) in group { + newElems.append((idx, elem)) try newComponents.merge(components) } - return (newElems, newComponents) + // things may come in out of order because of concurrency + // so we reorder after completing all entries. + newElems.sort { left, right in left.0 < right.0 } + return (newElems.map { $0.1 }, newComponents) } } } diff --git a/Sources/OpenAPIKit30/Utility/OrderedDictionary+ExternallyDereferenceable.swift b/Sources/OpenAPIKit30/Utility/OrderedDictionary+ExternallyDereferenceable.swift index 7636b5e13..38ab9bbc3 100644 --- a/Sources/OpenAPIKit30/Utility/OrderedDictionary+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit30/Utility/OrderedDictionary+ExternallyDereferenceable.swift @@ -25,6 +25,9 @@ extension OrderedDictionary where Value: ExternallyDereferenceable { newDict[key] = newRef try newComponents.merge(components) } + // things may come in out of order because of concurrency + // so we reorder after completing all entries. + try newDict.applyOrder(self) return (newDict, newComponents) } } From a6bc17778d6d689315664b9bd31da3ad645b0a0b Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Wed, 24 Apr 2024 06:08:25 -0500 Subject: [PATCH 057/110] fix OAS 3.0 test to not use a path item reference --- .../Document/ExternalDereferencingDocumentTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift b/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift index 527fa2a5c..450174496 100644 --- a/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift +++ b/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift @@ -160,7 +160,7 @@ final class ExternalDereferencingDocumentTests: XCTestCase { "callbacks_one_json": """ { "https://callback.site.com/callback": { - "$ref": "file://./paths/callback.json" + "summary": "just a callback" } } """, From 042a54a8a344cc34db78cdec98fec3a7c2baca37 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Wed, 24 Apr 2024 09:50:41 -0500 Subject: [PATCH 058/110] Fix support for setting vendor extensions on JSONSchema values. --- Sources/OpenAPIKit/Schema Object/JSONSchema.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/OpenAPIKit/Schema Object/JSONSchema.swift b/Sources/OpenAPIKit/Schema Object/JSONSchema.swift index cba7fe6d6..27564a342 100644 --- a/Sources/OpenAPIKit/Schema Object/JSONSchema.swift +++ b/Sources/OpenAPIKit/Schema Object/JSONSchema.swift @@ -13,7 +13,7 @@ import OpenAPIKitCore public struct JSONSchema: JSONSchemaContext, HasWarnings { public let warnings: [OpenAPI.Warning] - public let value: Schema + public var value: Schema internal init(warnings: [OpenAPI.Warning], schema: Schema) { self.warnings = warnings @@ -441,8 +441,8 @@ extension JSONSchema: VendorExtendable { get { coreContext.vendorExtensions } - set { - coreContext.vendorExtensions + set(extensions) { + self.value = value.with(vendorExtensions: extensions) } } From a49d111ec250c4ee479fe4b4c61debc60aaf2df2 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Wed, 24 Apr 2024 10:06:56 -0500 Subject: [PATCH 059/110] Support setting vendor extensions including on JSON Schemas and carry through external dereference process --- .../CodableVendorExtendable.swift | 2 +- Sources/OpenAPIKit30/Example.swift | 2 +- .../DereferencedJSONSchema.swift | 21 ++++++++++++------- .../Schema Object/JSONSchema.swift | 2 +- .../ExternalDereferencingDocumentTests.swift | 4 +--- .../VendorExtendableTests.swift | 2 +- 6 files changed, 19 insertions(+), 14 deletions(-) diff --git a/Sources/OpenAPIKit30/CodableVendorExtendable.swift b/Sources/OpenAPIKit30/CodableVendorExtendable.swift index 9cfa2e0e0..1c75c293e 100644 --- a/Sources/OpenAPIKit30/CodableVendorExtendable.swift +++ b/Sources/OpenAPIKit30/CodableVendorExtendable.swift @@ -18,7 +18,7 @@ public protocol VendorExtendable { /// These should be of the form: /// `[ "x-extensionKey": ]` /// where the values are anything codable. - var vendorExtensions: VendorExtensions { get } + var vendorExtensions: VendorExtensions { get set } } public enum VendorExtensionsConfiguration { diff --git a/Sources/OpenAPIKit30/Example.swift b/Sources/OpenAPIKit30/Example.swift index cdb0b33b4..2eec08ce2 100644 --- a/Sources/OpenAPIKit30/Example.swift +++ b/Sources/OpenAPIKit30/Example.swift @@ -25,7 +25,7 @@ extension OpenAPI { /// These should be of the form: /// `[ "x-extensionKey": ]` /// where the values are anything codable. - public let vendorExtensions: [String: AnyCodable] + public var vendorExtensions: [String: AnyCodable] public init( summary: String? = nil, diff --git a/Sources/OpenAPIKit30/Schema Object/DereferencedJSONSchema.swift b/Sources/OpenAPIKit30/Schema Object/DereferencedJSONSchema.swift index dcce05e05..57776fe42 100644 --- a/Sources/OpenAPIKit30/Schema Object/DereferencedJSONSchema.swift +++ b/Sources/OpenAPIKit30/Schema Object/DereferencedJSONSchema.swift @@ -451,7 +451,8 @@ extension JSONSchema: ExternallyDereferenceable { maxProperties: object.maxProperties, minProperties: object._minProperties ) - ) + ), + vendorExtensions: vendorExtensions ) case .array(let core, let array): let (newItems, components) = try await array.items.externallyDereferenced(with: loader) @@ -465,37 +466,43 @@ extension JSONSchema: ExternallyDereferenceable { minItems: array._minItems, uniqueItems: array._uniqueItems ) - ) + ), + vendorExtensions: vendorExtensions ) case .all(let schema, let core): let (newSubschemas, components) = try await schema.externallyDereferenced(with: loader) newComponents = components newSchema = .init( - schema: .all(of: newSubschemas, core: core) + schema: .all(of: newSubschemas, core: core), + vendorExtensions: vendorExtensions ) case .one(let schema, let core): let (newSubschemas, components) = try await schema.externallyDereferenced(with: loader) newComponents = components newSchema = .init( - schema: .one(of: newSubschemas, core: core) + schema: .one(of: newSubschemas, core: core), + vendorExtensions: vendorExtensions ) case .any(let schema, let core): let (newSubschemas, components) = try await schema.externallyDereferenced(with: loader) newComponents = components newSchema = .init( - schema: .any(of: newSubschemas, core: core) + schema: .any(of: newSubschemas, core: core), + vendorExtensions: vendorExtensions ) case .not(let schema, let core): let (newSubschema, components) = try await schema.externallyDereferenced(with: loader) newComponents = components newSchema = .init( - schema: .not(newSubschema, core: core) + schema: .not(newSubschema, core: core), + vendorExtensions: vendorExtensions ) case .reference(let reference, let core): let (newReference, components) = try await reference.externallyDereferenced(with: loader) newComponents = components newSchema = .init( - schema: .reference(newReference, core) + schema: .reference(newReference, core), + vendorExtensions: vendorExtensions ) case .fragment(_): newComponents = .noComponents diff --git a/Sources/OpenAPIKit30/Schema Object/JSONSchema.swift b/Sources/OpenAPIKit30/Schema Object/JSONSchema.swift index 93dc1b10a..ff3fa8166 100644 --- a/Sources/OpenAPIKit30/Schema Object/JSONSchema.swift +++ b/Sources/OpenAPIKit30/Schema Object/JSONSchema.swift @@ -14,7 +14,7 @@ public struct JSONSchema: JSONSchemaContext, HasWarnings, VendorExtendable { public let warnings: [OpenAPI.Warning] public let value: Schema - public let vendorExtensions: [String: AnyCodable] + public var vendorExtensions: [String: AnyCodable] internal init(warnings: [OpenAPI.Warning], schema: Schema, vendorExtensions: [String: AnyCodable]) { self.warnings = warnings diff --git a/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift b/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift index 450174496..862f47474 100644 --- a/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift +++ b/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift @@ -25,9 +25,7 @@ final class ExternalDereferencingDocumentTests: XCTestCase { // to keep track of where a reference was loaded from. This test makes sure // the following strategy of using vendor extensions works. if var extendable = decoded as? VendorExtendable { - //TODO: revisit vendor extensions mutability -#warning("revisit vendor extensions mutability") -// extendable.vendorExtensions["x-source-url"] = AnyCodable(url) + extendable.vendorExtensions["x-source-url"] = AnyCodable(url) finished = extendable as! T } else { finished = decoded diff --git a/Tests/OpenAPIKit30Tests/VendorExtendableTests.swift b/Tests/OpenAPIKit30Tests/VendorExtendableTests.swift index 44ea33cdf..944655df7 100644 --- a/Tests/OpenAPIKit30Tests/VendorExtendableTests.swift +++ b/Tests/OpenAPIKit30Tests/VendorExtendableTests.swift @@ -145,7 +145,7 @@ private struct TestStruct: Codable, CodableVendorExtendable { } } - public let vendorExtensions: Self.VendorExtensions + public var vendorExtensions: Self.VendorExtensions init(vendorExtensions: Self.VendorExtensions) { self.vendorExtensions = vendorExtensions From e599b4531c08986fe1f80e446f8e83e5d7cc1182 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Wed, 24 Apr 2024 10:52:18 -0500 Subject: [PATCH 060/110] pipe messages through all external dereferencing --- .../Components Object/Components.swift | 30 ++++++++------- .../Content/DereferencedContent.swift | 13 ++++--- .../Content/DereferencedContentEncoding.swift | 8 ++-- Sources/OpenAPIKit/Document/Document.swift | 12 +++--- .../Either+ExternallyDereferenceable.swift | 10 ++--- Sources/OpenAPIKit/Example.swift | 4 +- Sources/OpenAPIKit/ExternalLoader.swift | 10 ++++- .../Header/DereferencedHeader.swift | 11 ++++-- Sources/OpenAPIKit/JSONReference.swift | 14 +++---- Sources/OpenAPIKit/Link.swift | 6 +-- .../Operation/DereferencedOperation.swift | 29 +++++++-------- .../Parameter/DereferencedParameter.swift | 11 ++++-- .../Parameter/DereferencedSchemaContext.swift | 10 +++-- .../Path Item/DereferencedPathItem.swift | 37 ++++++++++++------- .../Request/DereferencedRequest.swift | 6 +-- .../Response/DereferencedResponse.swift | 15 +++++--- .../DereferencedJSONSchema.swift | 37 ++++++++++++++----- .../OpenAPIKit/Security/SecurityScheme.swift | 4 +- Sources/OpenAPIKit/Server.swift | 4 +- .../Array+ExternallyDereferenceable.swift | 10 +++-- ...Dictionary+ExternallyDereferenceable.swift | 14 ++++--- .../Optional+ExternallyDereferenceable.swift | 4 +- ...Dictionary+ExternallyDereferenceable.swift | 14 ++++--- .../ExternalDereferencingDocumentTests.swift | 6 ++- 24 files changed, 190 insertions(+), 129 deletions(-) diff --git a/Sources/OpenAPIKit/Components Object/Components.swift b/Sources/OpenAPIKit/Components Object/Components.swift index e90d318b1..509bbe3d7 100644 --- a/Sources/OpenAPIKit/Components Object/Components.swift +++ b/Sources/OpenAPIKit/Components Object/Components.swift @@ -320,10 +320,10 @@ extension OpenAPI.Components { } extension OpenAPI.Components { - internal mutating func externallyDereference(with loader: Loader.Type, depth: ExternalDereferenceDepth = .iterations(1)) async throws { + internal mutating func externallyDereference(with loader: Loader.Type, depth: ExternalDereferenceDepth = .iterations(1), context: [Loader.Message] = []) async throws -> [Loader.Message] { if case let .iterations(number) = depth, number <= 0 { - return + return context } let oldSchemas = schemas @@ -336,15 +336,15 @@ extension OpenAPI.Components { let oldCallbacks = callbacks let oldPathItems = pathItems - async let (newSchemas, c1) = oldSchemas.externallyDereferenced(with: loader) - async let (newResponses, c2) = oldResponses.externallyDereferenced(with: loader) - async let (newParameters, c3) = oldParameters.externallyDereferenced(with: loader) - async let (newExamples, c4) = oldExamples.externallyDereferenced(with: loader) - async let (newRequestBodies, c5) = oldRequestBodies.externallyDereferenced(with: loader) - async let (newHeaders, c6) = oldHeaders.externallyDereferenced(with: loader) - async let (newSecuritySchemes, c7) = oldSecuritySchemes.externallyDereferenced(with: loader) - async let (newCallbacks, c8) = oldCallbacks.externallyDereferenced(with: loader) - async let (newPathItems, c9) = oldPathItems.externallyDereferenced(with: loader) + async let (newSchemas, c1, m1) = oldSchemas.externallyDereferenced(with: loader) + async let (newResponses, c2, m2) = oldResponses.externallyDereferenced(with: loader) + async let (newParameters, c3, m3) = oldParameters.externallyDereferenced(with: loader) + async let (newExamples, c4, m4) = oldExamples.externallyDereferenced(with: loader) + async let (newRequestBodies, c5, m5) = oldRequestBodies.externallyDereferenced(with: loader) + async let (newHeaders, c6, m6) = oldHeaders.externallyDereferenced(with: loader) + async let (newSecuritySchemes, c7, m7) = oldSecuritySchemes.externallyDereferenced(with: loader) + async let (newCallbacks, c8, m8) = oldCallbacks.externallyDereferenced(with: loader) + async let (newPathItems, c9, m9) = oldPathItems.externallyDereferenced(with: loader) schemas = try await newSchemas responses = try await newResponses @@ -377,7 +377,9 @@ extension OpenAPI.Components { && c8Resolved.isEmpty && c9Resolved.isEmpty - if noNewComponents { return } + let newMessages = try await context + m1 + m2 + m3 + m4 + m5 + m6 + m7 + m8 + m9 + + if noNewComponents { return newMessages } try merge(c1Resolved) try merge(c2Resolved) @@ -391,9 +393,9 @@ extension OpenAPI.Components { switch depth { case .iterations(let number): - try await externallyDereference(with: loader, depth: .iterations(number - 1)) + return try await externallyDereference(with: loader, depth: .iterations(number - 1), context: newMessages) case .full: - try await externallyDereference(with: loader, depth: .full) + return try await externallyDereference(with: loader, depth: .full, context: newMessages) } } } diff --git a/Sources/OpenAPIKit/Content/DereferencedContent.swift b/Sources/OpenAPIKit/Content/DereferencedContent.swift index afdcd582e..5350b55ee 100644 --- a/Sources/OpenAPIKit/Content/DereferencedContent.swift +++ b/Sources/OpenAPIKit/Content/DereferencedContent.swift @@ -77,28 +77,31 @@ extension OpenAPI.Content: LocallyDereferenceable { } extension OpenAPI.Content: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { let oldSchema = schema - async let (newSchema, c1) = oldSchema.externallyDereferenced(with: loader) + async let (newSchema, c1, m1) = oldSchema.externallyDereferenced(with: loader) var newContent = self var newComponents = try await c1 + var newMessages = try await m1 newContent.schema = try await newSchema if let oldExamples = examples { - let (newExamples, c2) = try await oldExamples.externallyDereferenced(with: loader) + let (newExamples, c2, m2) = try await oldExamples.externallyDereferenced(with: loader) newContent.examples = newExamples try newComponents.merge(c2) + newMessages += m2 } if let oldEncoding = encoding { - let (newEncoding, c3) = try await oldEncoding.externallyDereferenced(with: loader) + let (newEncoding, c3, m3) = try await oldEncoding.externallyDereferenced(with: loader) newContent.encoding = newEncoding try newComponents.merge(c3) + newMessages += m3 } - return (newContent, newComponents) + return (newContent, newComponents, newMessages) } } diff --git a/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift b/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift index 605ae7426..aaa9a1fd5 100644 --- a/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift +++ b/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift @@ -58,15 +58,17 @@ extension OpenAPI.Content.Encoding: LocallyDereferenceable { } extension OpenAPI.Content.Encoding: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { let newHeaders: OpenAPI.Header.Map? let newComponents: OpenAPI.Components + let newMessages: [Loader.Message] if let oldHeaders = headers { - (newHeaders, newComponents) = try await oldHeaders.externallyDereferenced(with: loader) + (newHeaders, newComponents, newMessages) = try await oldHeaders.externallyDereferenced(with: loader) } else { newHeaders = nil newComponents = .init() + newMessages = [] } let newEncoding = OpenAPI.Content.Encoding( @@ -77,6 +79,6 @@ extension OpenAPI.Content.Encoding: ExternallyDereferenceable { allowReserved: allowReserved ) - return (newEncoding, newComponents) + return (newEncoding, newComponents, newMessages) } } diff --git a/Sources/OpenAPIKit/Document/Document.swift b/Sources/OpenAPIKit/Document/Document.swift index 9864d8280..a2b13a36e 100644 --- a/Sources/OpenAPIKit/Document/Document.swift +++ b/Sources/OpenAPIKit/Document/Document.swift @@ -362,24 +362,26 @@ extension OpenAPI.Document { return try DereferencedDocument(self) } - public mutating func externallyDereference(with loader: Loader.Type, depth: ExternalDereferenceDepth = .iterations(1)) async throws { + public mutating func externallyDereference(with loader: Loader.Type, depth: ExternalDereferenceDepth = .iterations(1), context: [Loader.Message] = []) async throws -> [Loader.Message] { if case let .iterations(number) = depth, number <= 0 { - return + return context } let oldPaths = paths let oldWebhooks = webhooks - async let (newPaths, c1) = oldPaths.externallyDereferenced(with: loader) - async let (newWebhooks, c2) = oldWebhooks.externallyDereferenced(with: loader) + async let (newPaths, c1, m1) = oldPaths.externallyDereferenced(with: loader) + async let (newWebhooks, c2, m2) = oldWebhooks.externallyDereferenced(with: loader) paths = try await newPaths webhooks = try await newWebhooks try await components.merge(c1) try await components.merge(c2) - try await components.externallyDereference(with: loader, depth: depth) + let m3 = try await components.externallyDereference(with: loader, depth: depth, context: context) + + return try await context + m1 + m2 + m3 } } diff --git a/Sources/OpenAPIKit/Either/Either+ExternallyDereferenceable.swift b/Sources/OpenAPIKit/Either/Either+ExternallyDereferenceable.swift index 8c202bb5b..62c919355 100644 --- a/Sources/OpenAPIKit/Either/Either+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit/Either/Either+ExternallyDereferenceable.swift @@ -10,14 +10,14 @@ import OpenAPIKitCore // MARK: - ExternallyDereferenceable extension Either: ExternallyDereferenceable where A: ExternallyDereferenceable, B: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { switch self { case .a(let a): - let (newA, components) = try await a.externallyDereferenced(with: loader) - return (.a(newA), components) + let (newA, components, messages) = try await a.externallyDereferenced(with: loader) + return (.a(newA), components, messages) case .b(let b): - let (newB, components) = try await b.externallyDereferenced(with: loader) - return (.b(newB), components) + let (newB, components, messages) = try await b.externallyDereferenced(with: loader) + return (.b(newB), components, messages) } } } diff --git a/Sources/OpenAPIKit/Example.swift b/Sources/OpenAPIKit/Example.swift index 74c9f7226..e9c904f7c 100644 --- a/Sources/OpenAPIKit/Example.swift +++ b/Sources/OpenAPIKit/Example.swift @@ -209,8 +209,8 @@ extension OpenAPI.Example: LocallyDereferenceable { } extension OpenAPI.Example: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { - return (self, .init()) + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { + return (self, .init(), []) } } diff --git a/Sources/OpenAPIKit/ExternalLoader.swift b/Sources/OpenAPIKit/ExternalLoader.swift index 257264995..28cfa8d39 100644 --- a/Sources/OpenAPIKit/ExternalLoader.swift +++ b/Sources/OpenAPIKit/ExternalLoader.swift @@ -12,11 +12,17 @@ import Foundation /// without knowing the details of what decoder is being used or how new internal /// references should be named. public protocol ExternalLoader { + /// This can be anything that an implementor of this protocol wants to pass back from + /// the `load()` function and have available after all external loading has been done. + /// + /// A trivial type if no Messages are needed would be Void. + associatedtype Message + /// Load the given URL and decode it as Type `T`. All Types `T` are `Decodable`, so /// the only real responsibility of a `load` function is to locate and load the given /// `URL` and pass its `Data` or `String` (depending on the decoder) to an appropriate /// `Decoder` for the given file type. - static func load(_: URL) async throws -> T where T: Decodable + static func load(_: URL) async throws -> (T, [Message]) where T: Decodable /// Determine the next Component Key (where to store something in the /// Components Object) for a new object of the given type that was loaded @@ -30,5 +36,5 @@ public protocol ExternalLoader { } public protocol ExternallyDereferenceable { - func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) + func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) } diff --git a/Sources/OpenAPIKit/Header/DereferencedHeader.swift b/Sources/OpenAPIKit/Header/DereferencedHeader.swift index ec9881c71..40509234a 100644 --- a/Sources/OpenAPIKit/Header/DereferencedHeader.swift +++ b/Sources/OpenAPIKit/Header/DereferencedHeader.swift @@ -84,7 +84,7 @@ extension OpenAPI.Header: LocallyDereferenceable { } extension OpenAPI.Header: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { // if not for a Swift bug, this whole next bit would just be the // next line: @@ -92,16 +92,19 @@ extension OpenAPI.Header: ExternallyDereferenceable { let newSchemaOrContent: Either let newComponents: OpenAPI.Components + let newMessages: [Loader.Message] switch schemaOrContent { case .a(let schemaContext): - let (context, components) = try await schemaContext.externallyDereferenced(with: loader) + let (context, components, messages) = try await schemaContext.externallyDereferenced(with: loader) newSchemaOrContent = .a(context) newComponents = components + newMessages = messages case .b(let contentMap): - let (map, components) = try await contentMap.externallyDereferenced(with: loader) + let (map, components, messages) = try await contentMap.externallyDereferenced(with: loader) newSchemaOrContent = .b(map) newComponents = components + newMessages = messages } let newHeader = OpenAPI.Header( @@ -112,6 +115,6 @@ extension OpenAPI.Header: ExternallyDereferenceable { vendorExtensions: vendorExtensions ) - return (newHeader, newComponents) + return (newHeader, newComponents, newMessages) } } diff --git a/Sources/OpenAPIKit/JSONReference.swift b/Sources/OpenAPIKit/JSONReference.swift index 7d59430f5..1509f029f 100644 --- a/Sources/OpenAPIKit/JSONReference.swift +++ b/Sources/OpenAPIKit/JSONReference.swift @@ -538,16 +538,16 @@ extension JSONReference: LocallyDereferenceable where ReferenceType: LocallyDere } extension JSONReference: ExternallyDereferenceable where ReferenceType: ExternallyDereferenceable & Decodable & Equatable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { switch self { case .internal(let ref): - return (.internal(ref), .init()) + return (.internal(ref), .init(), []) case .external(let url): let componentKey = try loader.componentKey(type: ReferenceType.self, at: url) - let component: ReferenceType = try await loader.load(url) + let (component, messages): (ReferenceType, [Loader.Message]) = try await loader.load(url) var components = OpenAPI.Components() components[keyPath: ReferenceType.openAPIComponentsKeyPath][componentKey] = component - return (try components.reference(named: componentKey.rawValue, ofType: ReferenceType.self).jsonReference, components) + return (try components.reference(named: componentKey.rawValue, ofType: ReferenceType.self).jsonReference, components, messages) } } } @@ -580,9 +580,9 @@ extension OpenAPI.Reference: LocallyDereferenceable where ReferenceType: Locally } extension OpenAPI.Reference: ExternallyDereferenceable where ReferenceType: ExternallyDereferenceable & Decodable & Equatable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { - let (newRef, components) = try await jsonReference.externallyDereferenced(with: loader) - return (.init(newRef), components) + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { + let (newRef, components, messages) = try await jsonReference.externallyDereferenced(with: loader) + return (.init(newRef), components, messages) } } diff --git a/Sources/OpenAPIKit/Link.swift b/Sources/OpenAPIKit/Link.swift index dde15299e..39a1da918 100644 --- a/Sources/OpenAPIKit/Link.swift +++ b/Sources/OpenAPIKit/Link.swift @@ -290,13 +290,13 @@ extension OpenAPI.Link: LocallyDereferenceable { } extension OpenAPI.Link: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { - let (newServer, newComponents) = try await server.externallyDereferenced(with: loader) + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { + let (newServer, newComponents, newMessages) = try await server.externallyDereferenced(with: loader) var newLink = self newLink.server = newServer - return (newLink, newComponents) + return (newLink, newComponents, newMessages) } } diff --git a/Sources/OpenAPIKit/Operation/DereferencedOperation.swift b/Sources/OpenAPIKit/Operation/DereferencedOperation.swift index f4139fcd5..f2019842f 100644 --- a/Sources/OpenAPIKit/Operation/DereferencedOperation.swift +++ b/Sources/OpenAPIKit/Operation/DereferencedOperation.swift @@ -126,41 +126,40 @@ extension OpenAPI.Operation: LocallyDereferenceable { } extension OpenAPI.Operation: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { let oldParameters = parameters let oldRequestBody = requestBody let oldResponses = responses - async let (newParameters, c1) = oldParameters.externallyDereferenced(with: loader) - async let (newRequestBody, c2) = oldRequestBody.externallyDereferenced(with: loader) - async let (newResponses, c3) = oldResponses.externallyDereferenced(with: loader) - async let (newCallbacks, c4) = callbacks.externallyDereferenced(with: loader) -// let (newServers, c6) = try await servers.externallyDereferenced(with: loader) + async let (newParameters, c1, m1) = oldParameters.externallyDereferenced(with: loader) + async let (newRequestBody, c2, m2) = oldRequestBody.externallyDereferenced(with: loader) + async let (newResponses, c3, m3) = oldResponses.externallyDereferenced(with: loader) + async let (newCallbacks, c4, m4) = callbacks.externallyDereferenced(with: loader) +// let (newServers, c5, m5) = try await servers.externallyDereferenced(with: loader) var newOperation = self var newComponents = try await c1 + var newMessages = try await m1 newOperation.parameters = try await newParameters newOperation.requestBody = try await newRequestBody try await newComponents.merge(c2) + try await newMessages += m2 newOperation.responses = try await newResponses try await newComponents.merge(c3) + try await newMessages += m3 newOperation.callbacks = try await newCallbacks try await newComponents.merge(c4) + try await newMessages += m4 - if let oldServers = servers { - let (newServers, c6) = try await oldServers.externallyDereferenced(with: loader) - newOperation.servers = newServers - try newComponents.merge(c6) - } - // should not be necessary but current Swift compiler can't figure out conformance of ExternallyDereferenceable: if let oldServers = servers { - let (newServers, c6) = try await oldServers.externallyDereferenced(with: loader) + let (newServers, c5, m5) = try await oldServers.externallyDereferenced(with: loader) newOperation.servers = newServers - try newComponents.merge(c6) + try newComponents.merge(c5) + newMessages += m5 } - return (newOperation, newComponents) + return (newOperation, newComponents, newMessages) } } diff --git a/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift b/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift index e0385e1c1..4b5002ca3 100644 --- a/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift +++ b/Sources/OpenAPIKit/Parameter/DereferencedParameter.swift @@ -84,7 +84,7 @@ extension OpenAPI.Parameter: LocallyDereferenceable { } extension OpenAPI.Parameter: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { // if not for a Swift bug, this whole function would just be the // next line: @@ -92,21 +92,24 @@ extension OpenAPI.Parameter: ExternallyDereferenceable { let newSchemaOrContent: Either let newComponents: OpenAPI.Components + let newMessages: [Loader.Message] switch schemaOrContent { case .a(let schemaContext): - let (context, components) = try await schemaContext.externallyDereferenced(with: loader) + let (context, components, messages) = try await schemaContext.externallyDereferenced(with: loader) newSchemaOrContent = .a(context) newComponents = components + newMessages = messages case .b(let contentMap): - let (map, components) = try await contentMap.externallyDereferenced(with: loader) + let (map, components, messages) = try await contentMap.externallyDereferenced(with: loader) newSchemaOrContent = .b(map) newComponents = components + newMessages = messages } var newParameter = self newParameter.schemaOrContent = newSchemaOrContent - return (newParameter, newComponents) + return (newParameter, newComponents, newMessages) } } diff --git a/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift b/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift index 68f9bb772..a101c1653 100644 --- a/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift +++ b/Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift @@ -71,22 +71,24 @@ extension OpenAPI.Parameter.SchemaContext: LocallyDereferenceable { } extension OpenAPI.Parameter.SchemaContext: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { let oldSchema = schema - async let (newSchema, c1) = oldSchema.externallyDereferenced(with: loader) + async let (newSchema, c1, m1) = oldSchema.externallyDereferenced(with: loader) var newSchemaContext = self var newComponents = try await c1 + var newMessages = try await m1 newSchemaContext.schema = try await newSchema if let oldExamples = examples { - let (newExamples, c2) = try await oldExamples.externallyDereferenced(with: loader) + let (newExamples, c2, m2) = try await oldExamples.externallyDereferenced(with: loader) newSchemaContext.examples = newExamples try newComponents.merge(c2) + newMessages += m2 } - return (newSchemaContext, newComponents) + return (newSchemaContext, newComponents, newMessages) } } diff --git a/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift b/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift index 48d9b038a..d9f25538f 100644 --- a/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift +++ b/Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift @@ -140,7 +140,7 @@ extension OpenAPI.PathItem: LocallyDereferenceable { } extension OpenAPI.PathItem: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { let oldParameters = parameters let oldServers = servers let oldGet = get @@ -152,19 +152,20 @@ extension OpenAPI.PathItem: ExternallyDereferenceable { let oldPatch = patch let oldTrace = trace - async let (newParameters, c1) = oldParameters.externallyDereferenced(with: loader) -// async let (newServers, c2) = oldServers.externallyDereferenced(with: loader) - async let (newGet, c3) = oldGet.externallyDereferenced(with: loader) - async let (newPut, c4) = oldPut.externallyDereferenced(with: loader) - async let (newPost, c5) = oldPost.externallyDereferenced(with: loader) - async let (newDelete, c6) = oldDelete.externallyDereferenced(with: loader) - async let (newOptions, c7) = oldOptions.externallyDereferenced(with: loader) - async let (newHead, c8) = oldHead.externallyDereferenced(with: loader) - async let (newPatch, c9) = oldPatch.externallyDereferenced(with: loader) - async let (newTrace, c10) = oldTrace.externallyDereferenced(with: loader) + async let (newParameters, c1, m1) = oldParameters.externallyDereferenced(with: loader) +// async let (newServers, c2, m2) = oldServers.externallyDereferenced(with: loader) + async let (newGet, c3, m3) = oldGet.externallyDereferenced(with: loader) + async let (newPut, c4, m4) = oldPut.externallyDereferenced(with: loader) + async let (newPost, c5, m5) = oldPost.externallyDereferenced(with: loader) + async let (newDelete, c6, m6) = oldDelete.externallyDereferenced(with: loader) + async let (newOptions, c7, m7) = oldOptions.externallyDereferenced(with: loader) + async let (newHead, c8, m8) = oldHead.externallyDereferenced(with: loader) + async let (newPatch, c9, m9) = oldPatch.externallyDereferenced(with: loader) + async let (newTrace, c10, m10) = oldTrace.externallyDereferenced(with: loader) var pathItem = self var newComponents = try await c1 + var newMessages = try await m1 // ideally we would async let all of the props above and then set them here, // but for now since there seems to be some sort of compiler bug we will do @@ -188,12 +189,22 @@ extension OpenAPI.PathItem: ExternallyDereferenceable { try await newComponents.merge(c9) try await newComponents.merge(c10) + try await newMessages += m3 + try await newMessages += m4 + try await newMessages += m5 + try await newMessages += m6 + try await newMessages += m7 + try await newMessages += m8 + try await newMessages += m9 + try await newMessages += m10 + if let oldServers { - async let (newServers, c2) = oldServers.externallyDereferenced(with: loader) + async let (newServers, c2, m2) = oldServers.externallyDereferenced(with: loader) pathItem.servers = try await newServers try await newComponents.merge(c2) + try await newMessages += m2 } - return (pathItem, newComponents) + return (pathItem, newComponents, newMessages) } } diff --git a/Sources/OpenAPIKit/Request/DereferencedRequest.swift b/Sources/OpenAPIKit/Request/DereferencedRequest.swift index 39d0c2409..36d802767 100644 --- a/Sources/OpenAPIKit/Request/DereferencedRequest.swift +++ b/Sources/OpenAPIKit/Request/DereferencedRequest.swift @@ -63,12 +63,12 @@ extension OpenAPI.Request: LocallyDereferenceable { } extension OpenAPI.Request: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { var newRequest = self - let (newContent, components) = try await content.externallyDereferenced(with: loader) + let (newContent, components, messages) = try await content.externallyDereferenced(with: loader) newRequest.content = newContent - return (newRequest, components) + return (newRequest, components, messages) } } diff --git a/Sources/OpenAPIKit/Response/DereferencedResponse.swift b/Sources/OpenAPIKit/Response/DereferencedResponse.swift index add208773..702da8765 100644 --- a/Sources/OpenAPIKit/Response/DereferencedResponse.swift +++ b/Sources/OpenAPIKit/Response/DereferencedResponse.swift @@ -78,28 +78,31 @@ extension OpenAPI.Response: LocallyDereferenceable { } extension OpenAPI.Response: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { let oldContent = content let oldLinks = links let oldHeaders = headers - async let (newContent, c1) = oldContent.externallyDereferenced(with: loader) - async let (newLinks, c2) = oldLinks.externallyDereferenced(with: loader) -// async let (newHeaders, c3) = oldHeaders.externallyDereferenced(with: loader) + async let (newContent, c1, m1) = oldContent.externallyDereferenced(with: loader) + async let (newLinks, c2, m2) = oldLinks.externallyDereferenced(with: loader) +// async let (newHeaders, c3, m3) = oldHeaders.externallyDereferenced(with: loader) var response = self + var messages = try await m1 response.content = try await newContent response.links = try await newLinks var components = try await c1 try await components.merge(c2) + try await messages += m2 if let oldHeaders { - let (newHeaders, c3) = try await oldHeaders.externallyDereferenced(with: loader) + let (newHeaders, c3, m3) = try await oldHeaders.externallyDereferenced(with: loader) response.headers = newHeaders try components.merge(c3) + messages += m3 } - return (response, components) + return (response, components, messages) } } diff --git a/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift b/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift index dfe1af9e3..f3b061f5c 100644 --- a/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift +++ b/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift @@ -536,41 +536,51 @@ extension JSONSchema: LocallyDereferenceable { } extension JSONSchema: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { let newSchema: JSONSchema let newComponents: OpenAPI.Components + let newMessages: [Loader.Message] switch value { case .null(_): newComponents = .noComponents newSchema = self + newMessages = [] case .boolean(_): newComponents = .noComponents newSchema = self + newMessages = [] case .number(_, _): newComponents = .noComponents newSchema = self + newMessages = [] case .integer(_, _): newComponents = .noComponents newSchema = self + newMessages = [] case .string(_, _): newComponents = .noComponents newSchema = self + newMessages = [] case .object(let core, let object): var components = OpenAPI.Components() + var messages = [Loader.Message]() - let (newProperties, c1) = try await object.properties.externallyDereferenced(with: loader) + let (newProperties, c1, m1) = try await object.properties.externallyDereferenced(with: loader) try components.merge(c1) + messages += m1 let newAdditionalProperties: Either? if case .b(let schema) = object.additionalProperties { - let (additionalProperties, c2) = try await schema.externallyDereferenced(with: loader) + let (additionalProperties, c2, m2) = try await schema.externallyDereferenced(with: loader) try components.merge(c2) + messages += m2 newAdditionalProperties = .b(additionalProperties) } else { newAdditionalProperties = object.additionalProperties } newComponents = components + newMessages = messages newSchema = .init( schema: .object( core, @@ -583,8 +593,9 @@ extension JSONSchema: ExternallyDereferenceable { ) ) case .array(let core, let array): - let (newItems, components) = try await array.items.externallyDereferenced(with: loader) + let (newItems, components, messages) = try await array.items.externallyDereferenced(with: loader) newComponents = components + newMessages = messages newSchema = .init( schema: .array( core, @@ -597,40 +608,46 @@ extension JSONSchema: ExternallyDereferenceable { ) ) case .all(let schema, let core): - let (newSubschemas, components) = try await schema.externallyDereferenced(with: loader) + let (newSubschemas, components, messages) = try await schema.externallyDereferenced(with: loader) newComponents = components + newMessages = messages newSchema = .init( schema: .all(of: newSubschemas, core: core) ) case .one(let schema, let core): - let (newSubschemas, components) = try await schema.externallyDereferenced(with: loader) + let (newSubschemas, components, messages) = try await schema.externallyDereferenced(with: loader) newComponents = components + newMessages = messages newSchema = .init( schema: .one(of: newSubschemas, core: core) ) case .any(let schema, let core): - let (newSubschemas, components) = try await schema.externallyDereferenced(with: loader) + let (newSubschemas, components, messages) = try await schema.externallyDereferenced(with: loader) newComponents = components + newMessages = messages newSchema = .init( schema: .any(of: newSubschemas, core: core) ) case .not(let schema, let core): - let (newSubschema, components) = try await schema.externallyDereferenced(with: loader) + let (newSubschema, components, messages) = try await schema.externallyDereferenced(with: loader) newComponents = components + newMessages = messages newSchema = .init( schema: .not(newSubschema, core: core) ) case .reference(let reference, let core): - let (newReference, components) = try await reference.externallyDereferenced(with: loader) + let (newReference, components, messages) = try await reference.externallyDereferenced(with: loader) newComponents = components + newMessages = messages newSchema = .init( schema: .reference(newReference, core) ) case .fragment(_): newComponents = .noComponents newSchema = self + newMessages = [] } - return (newSchema, newComponents) + return (newSchema, newComponents, newMessages) } } diff --git a/Sources/OpenAPIKit/Security/SecurityScheme.swift b/Sources/OpenAPIKit/Security/SecurityScheme.swift index c4af0ff78..8e487bba7 100644 --- a/Sources/OpenAPIKit/Security/SecurityScheme.swift +++ b/Sources/OpenAPIKit/Security/SecurityScheme.swift @@ -275,7 +275,7 @@ extension OpenAPI.SecurityScheme: LocallyDereferenceable { } extension OpenAPI.SecurityScheme: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { - return (self, .init()) + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { + return (self, .init(), []) } } diff --git a/Sources/OpenAPIKit/Server.swift b/Sources/OpenAPIKit/Server.swift index 80f4bec07..d21a1b5e7 100644 --- a/Sources/OpenAPIKit/Server.swift +++ b/Sources/OpenAPIKit/Server.swift @@ -260,8 +260,8 @@ extension OpenAPI.Server.Variable { } extension OpenAPI.Server: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { - return (self, .init()) + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { + return (self, .init(), []) } } diff --git a/Sources/OpenAPIKit/Utility/Array+ExternallyDereferenceable.swift b/Sources/OpenAPIKit/Utility/Array+ExternallyDereferenceable.swift index a67002587..3a959b7ce 100644 --- a/Sources/OpenAPIKit/Utility/Array+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit/Utility/Array+ExternallyDereferenceable.swift @@ -6,8 +6,8 @@ import OpenAPIKitCore extension Array where Element: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { - try await withThrowingTaskGroup(of: (Int, (Element, OpenAPI.Components)).self) { group in + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { + try await withThrowingTaskGroup(of: (Int, (Element, OpenAPI.Components, [Loader.Message])).self) { group in for (idx, elem) in zip(self.indices, self) { group.addTask { return try await (idx, elem.externallyDereferenced(with: loader)) @@ -16,15 +16,17 @@ extension Array where Element: ExternallyDereferenceable { var newElems = Array<(Int, Element)>() var newComponents = OpenAPI.Components() + var newMessages = [Loader.Message]() - for try await (idx, (elem, components)) in group { + for try await (idx, (elem, components, messages)) in group { newElems.append((idx, elem)) try newComponents.merge(components) + newMessages += messages } // things may come in out of order because of concurrency // so we reorder after completing all entries. newElems.sort { left, right in left.0 < right.0 } - return (newElems.map { $0.1 }, newComponents) + return (newElems.map { $0.1 }, newComponents, newMessages) } } } diff --git a/Sources/OpenAPIKit/Utility/Dictionary+ExternallyDereferenceable.swift b/Sources/OpenAPIKit/Utility/Dictionary+ExternallyDereferenceable.swift index d15b123b2..ae0696e24 100644 --- a/Sources/OpenAPIKit/Utility/Dictionary+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit/Utility/Dictionary+ExternallyDereferenceable.swift @@ -7,23 +7,25 @@ import OpenAPIKitCore extension Dictionary where Value: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { - try await withThrowingTaskGroup(of: (Key, Value, OpenAPI.Components).self) { group in + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { + try await withThrowingTaskGroup(of: (Key, Value, OpenAPI.Components, [Loader.Message]).self) { group in for (key, value) in self { group.addTask { - let (newRef, components) = try await value.externallyDereferenced(with: loader) - return (key, newRef, components) + let (newRef, components, messages) = try await value.externallyDereferenced(with: loader) + return (key, newRef, components, messages) } } var newDict = Self() var newComponents = OpenAPI.Components() + var newMessage = [Loader.Message]() - for try await (key, newRef, components) in group { + for try await (key, newRef, components, messages) in group { newDict[key] = newRef try newComponents.merge(components) + newMessage += messages } - return (newDict, newComponents) + return (newDict, newComponents, newMessage) } } } diff --git a/Sources/OpenAPIKit/Utility/Optional+ExternallyDereferenceable.swift b/Sources/OpenAPIKit/Utility/Optional+ExternallyDereferenceable.swift index 79b055b52..87c7a0649 100644 --- a/Sources/OpenAPIKit/Utility/Optional+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit/Utility/Optional+ExternallyDereferenceable.swift @@ -6,8 +6,8 @@ import OpenAPIKitCore extension Optional where Wrapped: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { - guard let wrapped = self else { return (nil, .init()) } + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { + guard let wrapped = self else { return (nil, .init(), []) } return try await wrapped.externallyDereferenced(with: loader) } } diff --git a/Sources/OpenAPIKit/Utility/OrderedDictionary+ExternallyDereferenceable.swift b/Sources/OpenAPIKit/Utility/OrderedDictionary+ExternallyDereferenceable.swift index 38ab9bbc3..1c882a7ce 100644 --- a/Sources/OpenAPIKit/Utility/OrderedDictionary+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit/Utility/OrderedDictionary+ExternallyDereferenceable.swift @@ -9,26 +9,28 @@ import OpenAPIKitCore extension OrderedDictionary where Value: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { - try await withThrowingTaskGroup(of: (Key, Value, OpenAPI.Components).self) { group in + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { + try await withThrowingTaskGroup(of: (Key, Value, OpenAPI.Components, [Loader.Message]).self) { group in for (key, value) in self { group.addTask { - let (newRef, components) = try await value.externallyDereferenced(with: loader) - return (key, newRef, components) + let (newRef, components, messages) = try await value.externallyDereferenced(with: loader) + return (key, newRef, components, messages) } } var newDict = Self() var newComponents = OpenAPI.Components() + var newMessages = [Loader.Message]() - for try await (key, newRef, components) in group { + for try await (key, newRef, components, messages) in group { newDict[key] = newRef try newComponents.merge(components) + newMessages += messages } // things may come in out of order because of concurrency // so we reorder after completing all entries. try newDict.applyOrder(self) - return (newDict, newComponents) + return (newDict, newComponents, newMessages) } } } diff --git a/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift b/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift index 35bf2429d..15c1a346a 100644 --- a/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift @@ -14,7 +14,9 @@ final class ExternalDereferencingDocumentTests: XCTestCase { /// An example of implementing a loader context for loading external references /// into an OpenAPI document. struct ExampleLoader: ExternalLoader { - static func load(_ url: URL) async throws -> T where T : Decodable { + typealias Message = Void + + static func load(_ url: URL) async throws -> (T, [Message]) where T : Decodable { // load data from file, perhaps. we will just mock that up for the test: let data = try await mockData(componentKey(type: T.self, at: url)) @@ -30,7 +32,7 @@ final class ExternalDereferencingDocumentTests: XCTestCase { } else { finished = decoded } - return finished + return (finished, []) } static func componentKey(type: T.Type, at url: URL) throws -> OpenAPIKit.OpenAPI.ComponentKey { From a752a43965d254d11fa314c613009d3d405837ec Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Wed, 24 Apr 2024 11:02:18 -0500 Subject: [PATCH 061/110] make result of Document external dereferencing discardable. fix json reference external dereferencing tests --- Sources/OpenAPIKit/Document/Document.swift | 1 + Tests/OpenAPIKitTests/JSONReferenceTests.swift | 13 ++++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Sources/OpenAPIKit/Document/Document.swift b/Sources/OpenAPIKit/Document/Document.swift index a2b13a36e..9c5c4519d 100644 --- a/Sources/OpenAPIKit/Document/Document.swift +++ b/Sources/OpenAPIKit/Document/Document.swift @@ -362,6 +362,7 @@ extension OpenAPI.Document { return try DereferencedDocument(self) } + @discardableResult public mutating func externallyDereference(with loader: Loader.Type, depth: ExternalDereferenceDepth = .iterations(1), context: [Loader.Message] = []) async throws -> [Loader.Message] { if case let .iterations(number) = depth, number <= 0 { diff --git a/Tests/OpenAPIKitTests/JSONReferenceTests.swift b/Tests/OpenAPIKitTests/JSONReferenceTests.swift index c5e74f18f..bb0d6d6d3 100644 --- a/Tests/OpenAPIKitTests/JSONReferenceTests.swift +++ b/Tests/OpenAPIKitTests/JSONReferenceTests.swift @@ -382,28 +382,31 @@ extension JSONReferenceTests { func test_externalDerefNoFragment() async throws { let reference: JSONReference = .external(.init(string: "./schema.json")!) - let (newReference, components) = try await reference.externallyDereferenced(with: SchemaLoader.self) + let (newReference, components, messages) = try await reference.externallyDereferenced(with: SchemaLoader.self) XCTAssertEqual(newReference, .component(named: "__schema_json")) XCTAssertEqual(components, .init(schemas: ["__schema_json": .string])) + XCTAssertEqual(messages, ["./schema.json"]) } func test_externalDerefFragment() async throws { let reference: JSONReference = .external(.init(string: "./schema.json#/test")!) - let (newReference, components) = try await reference.externallyDereferenced(with: SchemaLoader.self) + let (newReference, components, messages) = try await reference.externallyDereferenced(with: SchemaLoader.self) XCTAssertEqual(newReference, .component(named: "__schema_json__test")) XCTAssertEqual(components, .init(schemas: ["__schema_json__test": .string])) + XCTAssertEqual(messages, ["./schema.json#/test"]) } func test_externalDerefExternalComponents() async throws { let reference: JSONReference = .external(.init(string: "./schema.json#/components/schemas/test")!) - let (newReference, components) = try await reference.externallyDereferenced(with: SchemaLoader.self) + let (newReference, components, messages) = try await reference.externallyDereferenced(with: SchemaLoader.self) XCTAssertEqual(newReference, .component(named: "__schema_json__components_schemas_test")) XCTAssertEqual(components, .init(schemas: ["__schema_json__components_schemas_test": .string])) + XCTAssertEqual(messages, ["./schema.json#/components/schemas/test"]) } } @@ -414,8 +417,8 @@ extension JSONReferenceTests { } struct SchemaLoader: ExternalLoader { - static func load(_ url: URL) -> T where T: Decodable { - return JSONSchema.string as! T + static func load(_ url: URL) -> (T, [String]) { + return (JSONSchema.string as! T, [url.absoluteString]) } static func componentKey(type: T.Type, at url: URL) throws -> OpenAPI.ComponentKey { From 76898f602864b19ac7eafb4edb6c79fee37aa89e Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Wed, 24 Apr 2024 21:13:25 -0500 Subject: [PATCH 062/110] port loader messages over to OAS 3.0 --- Sources/OpenAPIKit/Document/Document.swift | 17 +++++++++ .../Components Object/Components.swift | 30 ++++++++------- .../Content/DereferencedContent.swift | 21 ++++++----- .../Content/DereferencedContentEncoding.swift | 8 ++-- Sources/OpenAPIKit30/Document/Document.swift | 33 +++++++++++++++-- .../Either+ExternallyDereferenceable.swift | 10 ++--- Sources/OpenAPIKit30/Example.swift | 4 +- Sources/OpenAPIKit30/ExternalLoader.swift | 10 ++++- .../Header/DereferencedHeader.swift | 11 ++++-- Sources/OpenAPIKit30/JSONReference.swift | 8 ++-- Sources/OpenAPIKit30/Link.swift | 6 +-- .../Operation/DereferencedOperation.swift | 29 +++++++-------- .../Parameter/DereferencedParameter.swift | 13 ++++--- .../Parameter/DereferencedSchemaContext.swift | 10 +++-- .../Path Item/DereferencedPathItem.swift | 37 ++++++++++++------- .../Request/DereferencedRequest.swift | 6 +-- .../Response/DereferencedResponse.swift | 16 +++++--- .../DereferencedJSONSchema.swift | 36 +++++++++++++----- .../Security/SecurityScheme.swift | 4 +- Sources/OpenAPIKit30/Server.swift | 4 +- .../Array+ExternallyDereferenceable.swift | 10 +++-- ...Dictionary+ExternallyDereferenceable.swift | 14 ++++--- .../Optional+ExternallyDereferenceable.swift | 4 +- ...Dictionary+ExternallyDereferenceable.swift | 14 ++++--- 24 files changed, 227 insertions(+), 128 deletions(-) diff --git a/Sources/OpenAPIKit/Document/Document.swift b/Sources/OpenAPIKit/Document/Document.swift index 9c5c4519d..33546f086 100644 --- a/Sources/OpenAPIKit/Document/Document.swift +++ b/Sources/OpenAPIKit/Document/Document.swift @@ -362,6 +362,23 @@ extension OpenAPI.Document { return try DereferencedDocument(self) } + /// Load all remote references into the document. A remote reference is one + /// that points to another file rather than a location within the + /// same file. + /// + /// This function will load remote references into the Components object + /// and replace the remote reference with a local reference to that component. + /// No local references are modified or resolved by this function. You can + /// call `locallyDereferenced()` on the externally dereferenced document if + /// you want to also remove local references by inlining all of them. + /// + /// Externally dereferencing a document requires that you provide both a + /// function that produces a `OpenAPI.ComponentKey` for any given remote + /// file URI and also a function that loads and decodes the data found in + /// that remote file. The latter is less work than it may sound like because + /// the function is told what Decodable thing it wants, so you really just + /// need to decide what decoder to use and provide the file data to that + /// decoder. See `ExternalLoader` documentation for details. @discardableResult public mutating func externallyDereference(with loader: Loader.Type, depth: ExternalDereferenceDepth = .iterations(1), context: [Loader.Message] = []) async throws -> [Loader.Message] { if case let .iterations(number) = depth, diff --git a/Sources/OpenAPIKit30/Components Object/Components.swift b/Sources/OpenAPIKit30/Components Object/Components.swift index 12b79fced..c37338a08 100644 --- a/Sources/OpenAPIKit30/Components Object/Components.swift +++ b/Sources/OpenAPIKit30/Components Object/Components.swift @@ -312,10 +312,10 @@ extension OpenAPI.Components { } extension OpenAPI.Components { - internal mutating func externallyDereference(with loader: Loader.Type, depth: ExternalDereferenceDepth = .iterations(1)) async throws { + internal mutating func externallyDereference(with loader: Loader.Type, depth: ExternalDereferenceDepth = .iterations(1), context: [Loader.Message] = []) async throws -> [Loader.Message] { if case let .iterations(number) = depth, number <= 0 { - return + return context } let oldSchemas = schemas @@ -328,15 +328,15 @@ extension OpenAPI.Components { let oldCallbacks = callbacks let oldPathItems = pathItems - async let (newSchemas, c1) = oldSchemas.externallyDereferenced(with: loader) - async let (newResponses, c2) = oldResponses.externallyDereferenced(with: loader) - async let (newParameters, c3) = oldParameters.externallyDereferenced(with: loader) - async let (newExamples, c4) = oldExamples.externallyDereferenced(with: loader) - async let (newRequestBodies, c5) = oldRequestBodies.externallyDereferenced(with: loader) - async let (newHeaders, c6) = oldHeaders.externallyDereferenced(with: loader) - async let (newSecuritySchemes, c7) = oldSecuritySchemes.externallyDereferenced(with: loader) - async let (newCallbacks, c8) = oldCallbacks.externallyDereferenced(with: loader) - async let (newPathItems, c9) = oldPathItems.externallyDereferenced(with: loader) + async let (newSchemas, c1, m1) = oldSchemas.externallyDereferenced(with: loader) + async let (newResponses, c2, m2) = oldResponses.externallyDereferenced(with: loader) + async let (newParameters, c3, m3) = oldParameters.externallyDereferenced(with: loader) + async let (newExamples, c4, m4) = oldExamples.externallyDereferenced(with: loader) + async let (newRequestBodies, c5, m5) = oldRequestBodies.externallyDereferenced(with: loader) + async let (newHeaders, c6, m6) = oldHeaders.externallyDereferenced(with: loader) + async let (newSecuritySchemes, c7, m7) = oldSecuritySchemes.externallyDereferenced(with: loader) + async let (newCallbacks, c8, m8) = oldCallbacks.externallyDereferenced(with: loader) + async let (newPathItems, c9, m9) = oldPathItems.externallyDereferenced(with: loader) schemas = try await newSchemas responses = try await newResponses @@ -369,7 +369,9 @@ extension OpenAPI.Components { && c8Resolved.isEmpty && c9Resolved.isEmpty - if noNewComponents { return } + let newMessages = try await context + m1 + m2 + m3 + m4 + m5 + m6 + m7 + m8 + m9 + + if noNewComponents { return newMessages } try merge(c1Resolved) try merge(c2Resolved) @@ -383,9 +385,9 @@ extension OpenAPI.Components { switch depth { case .iterations(let number): - try await externallyDereference(with: loader, depth: .iterations(number - 1)) + return try await externallyDereference(with: loader, depth: .iterations(number - 1), context: newMessages) case .full: - try await externallyDereference(with: loader, depth: .full) + return try await externallyDereference(with: loader, depth: .full, context: newMessages) } } } diff --git a/Sources/OpenAPIKit30/Content/DereferencedContent.swift b/Sources/OpenAPIKit30/Content/DereferencedContent.swift index c7f256c1d..ebfa10f07 100644 --- a/Sources/OpenAPIKit30/Content/DereferencedContent.swift +++ b/Sources/OpenAPIKit30/Content/DereferencedContent.swift @@ -77,28 +77,31 @@ extension OpenAPI.Content: LocallyDereferenceable { } extension OpenAPI.Content: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { - let oldSchema = schema + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { + let oldSchema = schema - async let (newSchema, c1) = oldSchema.externallyDereferenced(with: loader) + async let (newSchema, c1, m1) = oldSchema.externallyDereferenced(with: loader) - var newContent = self - var newComponents = try await c1 + var newContent = self + var newComponents = try await c1 + var newMessages = try await m1 - newContent.schema = try await newSchema + newContent.schema = try await newSchema if let oldExamples = examples { - let (newExamples, c2) = try await oldExamples.externallyDereferenced(with: loader) + let (newExamples, c2, m2) = try await oldExamples.externallyDereferenced(with: loader) newContent.examples = newExamples try newComponents.merge(c2) + newMessages += m2 } if let oldEncoding = encoding { - let (newEncoding, c3) = try await oldEncoding.externallyDereferenced(with: loader) + let (newEncoding, c3, m3) = try await oldEncoding.externallyDereferenced(with: loader) newContent.encoding = newEncoding try newComponents.merge(c3) + newMessages += m3 } - return (newContent, newComponents) + return (newContent, newComponents, newMessages) } } diff --git a/Sources/OpenAPIKit30/Content/DereferencedContentEncoding.swift b/Sources/OpenAPIKit30/Content/DereferencedContentEncoding.swift index 605ae7426..aaa9a1fd5 100644 --- a/Sources/OpenAPIKit30/Content/DereferencedContentEncoding.swift +++ b/Sources/OpenAPIKit30/Content/DereferencedContentEncoding.swift @@ -58,15 +58,17 @@ extension OpenAPI.Content.Encoding: LocallyDereferenceable { } extension OpenAPI.Content.Encoding: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { let newHeaders: OpenAPI.Header.Map? let newComponents: OpenAPI.Components + let newMessages: [Loader.Message] if let oldHeaders = headers { - (newHeaders, newComponents) = try await oldHeaders.externallyDereferenced(with: loader) + (newHeaders, newComponents, newMessages) = try await oldHeaders.externallyDereferenced(with: loader) } else { newHeaders = nil newComponents = .init() + newMessages = [] } let newEncoding = OpenAPI.Content.Encoding( @@ -77,6 +79,6 @@ extension OpenAPI.Content.Encoding: ExternallyDereferenceable { allowReserved: allowReserved ) - return (newEncoding, newComponents) + return (newEncoding, newComponents, newMessages) } } diff --git a/Sources/OpenAPIKit30/Document/Document.swift b/Sources/OpenAPIKit30/Document/Document.swift index 29f7b0f2f..930a0ea6c 100644 --- a/Sources/OpenAPIKit30/Document/Document.swift +++ b/Sources/OpenAPIKit30/Document/Document.swift @@ -323,6 +323,11 @@ extension OpenAPI.Document { /// Create a locally-dereferenced OpenAPI /// Document. /// + /// This function assumes all references are + /// local to the same file. If you want to resolve + /// remote references as well, call `externallyDereference()` + /// first and then locally dereference the result. + /// /// A dereferenced document contains no /// `JSONReferences`. All components have been /// inlined. @@ -346,20 +351,40 @@ extension OpenAPI.Document { return try DereferencedDocument(self) } - public mutating func externallyDereference(with loader: Loader.Type, depth: ExternalDereferenceDepth = .iterations(1)) async throws { + /// Load all remote references into the document. A remote reference is one + /// that points to another file rather than a location within the + /// same file. + /// + /// This function will load remote references into the Components object + /// and replace the remote reference with a local reference to that component. + /// No local references are modified or resolved by this function. You can + /// call `locallyDereferenced()` on the externally dereferenced document if + /// you want to also remove local references by inlining all of them. + /// + /// Externally dereferencing a document requires that you provide both a + /// function that produces a `OpenAPI.ComponentKey` for any given remote + /// file URI and also a function that loads and decodes the data found in + /// that remote file. The latter is less work than it may sound like because + /// the function is told what Decodable thing it wants, so you really just + /// need to decide what decoder to use and provide the file data to that + /// decoder. See `ExternalLoader` documentation for details. + @discardableResult + public mutating func externallyDereference(with loader: Loader.Type, depth: ExternalDereferenceDepth = .iterations(1), context: [Loader.Message] = []) async throws -> [Loader.Message] { if case let .iterations(number) = depth, number <= 0 { - return + return context } let oldPaths = paths - async let (newPaths, c1) = oldPaths.externallyDereferenced(with: loader) + async let (newPaths, c1, m1) = oldPaths.externallyDereferenced(with: loader) paths = try await newPaths try await components.merge(c1) - try await components.externallyDereference(with: loader, depth: depth) + let m2 = try await components.externallyDereference(with: loader, depth: depth) + + return try await context + m1 + m2 } } diff --git a/Sources/OpenAPIKit30/Either/Either+ExternallyDereferenceable.swift b/Sources/OpenAPIKit30/Either/Either+ExternallyDereferenceable.swift index 8c202bb5b..62c919355 100644 --- a/Sources/OpenAPIKit30/Either/Either+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit30/Either/Either+ExternallyDereferenceable.swift @@ -10,14 +10,14 @@ import OpenAPIKitCore // MARK: - ExternallyDereferenceable extension Either: ExternallyDereferenceable where A: ExternallyDereferenceable, B: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { switch self { case .a(let a): - let (newA, components) = try await a.externallyDereferenced(with: loader) - return (.a(newA), components) + let (newA, components, messages) = try await a.externallyDereferenced(with: loader) + return (.a(newA), components, messages) case .b(let b): - let (newB, components) = try await b.externallyDereferenced(with: loader) - return (.b(newB), components) + let (newB, components, messages) = try await b.externallyDereferenced(with: loader) + return (.b(newB), components, messages) } } } diff --git a/Sources/OpenAPIKit30/Example.swift b/Sources/OpenAPIKit30/Example.swift index 2eec08ce2..d28424238 100644 --- a/Sources/OpenAPIKit30/Example.swift +++ b/Sources/OpenAPIKit30/Example.swift @@ -185,8 +185,8 @@ extension OpenAPI.Example: LocallyDereferenceable { } extension OpenAPI.Example: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { - return (self, .init()) + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { + return (self, .init(), []) } } diff --git a/Sources/OpenAPIKit30/ExternalLoader.swift b/Sources/OpenAPIKit30/ExternalLoader.swift index 257264995..28cfa8d39 100644 --- a/Sources/OpenAPIKit30/ExternalLoader.swift +++ b/Sources/OpenAPIKit30/ExternalLoader.swift @@ -12,11 +12,17 @@ import Foundation /// without knowing the details of what decoder is being used or how new internal /// references should be named. public protocol ExternalLoader { + /// This can be anything that an implementor of this protocol wants to pass back from + /// the `load()` function and have available after all external loading has been done. + /// + /// A trivial type if no Messages are needed would be Void. + associatedtype Message + /// Load the given URL and decode it as Type `T`. All Types `T` are `Decodable`, so /// the only real responsibility of a `load` function is to locate and load the given /// `URL` and pass its `Data` or `String` (depending on the decoder) to an appropriate /// `Decoder` for the given file type. - static func load(_: URL) async throws -> T where T: Decodable + static func load(_: URL) async throws -> (T, [Message]) where T: Decodable /// Determine the next Component Key (where to store something in the /// Components Object) for a new object of the given type that was loaded @@ -30,5 +36,5 @@ public protocol ExternalLoader { } public protocol ExternallyDereferenceable { - func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) + func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) } diff --git a/Sources/OpenAPIKit30/Header/DereferencedHeader.swift b/Sources/OpenAPIKit30/Header/DereferencedHeader.swift index ec9881c71..40509234a 100644 --- a/Sources/OpenAPIKit30/Header/DereferencedHeader.swift +++ b/Sources/OpenAPIKit30/Header/DereferencedHeader.swift @@ -84,7 +84,7 @@ extension OpenAPI.Header: LocallyDereferenceable { } extension OpenAPI.Header: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { // if not for a Swift bug, this whole next bit would just be the // next line: @@ -92,16 +92,19 @@ extension OpenAPI.Header: ExternallyDereferenceable { let newSchemaOrContent: Either let newComponents: OpenAPI.Components + let newMessages: [Loader.Message] switch schemaOrContent { case .a(let schemaContext): - let (context, components) = try await schemaContext.externallyDereferenced(with: loader) + let (context, components, messages) = try await schemaContext.externallyDereferenced(with: loader) newSchemaOrContent = .a(context) newComponents = components + newMessages = messages case .b(let contentMap): - let (map, components) = try await contentMap.externallyDereferenced(with: loader) + let (map, components, messages) = try await contentMap.externallyDereferenced(with: loader) newSchemaOrContent = .b(map) newComponents = components + newMessages = messages } let newHeader = OpenAPI.Header( @@ -112,6 +115,6 @@ extension OpenAPI.Header: ExternallyDereferenceable { vendorExtensions: vendorExtensions ) - return (newHeader, newComponents) + return (newHeader, newComponents, newMessages) } } diff --git a/Sources/OpenAPIKit30/JSONReference.swift b/Sources/OpenAPIKit30/JSONReference.swift index e6b425224..6e6350751 100644 --- a/Sources/OpenAPIKit30/JSONReference.swift +++ b/Sources/OpenAPIKit30/JSONReference.swift @@ -375,16 +375,16 @@ extension JSONReference: LocallyDereferenceable where ReferenceType: LocallyDere // MARK: - ExternallyDereferenceable extension JSONReference: ExternallyDereferenceable where ReferenceType: ExternallyDereferenceable & Decodable & Equatable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { switch self { case .internal(let ref): - return (.internal(ref), .init()) + return (.internal(ref), .init(), []) case .external(let url): let componentKey = try loader.componentKey(type: ReferenceType.self, at: url) - let component: ReferenceType = try await loader.load(url) + let (component, messages): (ReferenceType, [Loader.Message]) = try await loader.load(url) var components = OpenAPI.Components() components[keyPath: ReferenceType.openAPIComponentsKeyPath][componentKey] = component - return (try components.reference(named: componentKey.rawValue, ofType: ReferenceType.self), components) + return (try components.reference(named: componentKey.rawValue, ofType: ReferenceType.self), components, messages) } } } diff --git a/Sources/OpenAPIKit30/Link.swift b/Sources/OpenAPIKit30/Link.swift index c39d6c219..c416e97b7 100644 --- a/Sources/OpenAPIKit30/Link.swift +++ b/Sources/OpenAPIKit30/Link.swift @@ -279,13 +279,13 @@ extension OpenAPI.Link: LocallyDereferenceable { } extension OpenAPI.Link: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { - let (newServer, newComponents) = try await server.externallyDereferenced(with: loader) + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { + let (newServer, newComponents, newMessages) = try await server.externallyDereferenced(with: loader) var newLink = self newLink.server = newServer - return (newLink, newComponents) + return (newLink, newComponents, newMessages) } } diff --git a/Sources/OpenAPIKit30/Operation/DereferencedOperation.swift b/Sources/OpenAPIKit30/Operation/DereferencedOperation.swift index 80cfb5590..ebcfccc78 100644 --- a/Sources/OpenAPIKit30/Operation/DereferencedOperation.swift +++ b/Sources/OpenAPIKit30/Operation/DereferencedOperation.swift @@ -126,41 +126,40 @@ extension OpenAPI.Operation: LocallyDereferenceable { } extension OpenAPI.Operation: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { let oldParameters = parameters let oldRequestBody = requestBody let oldResponses = responses - async let (newParameters, c1) = oldParameters.externallyDereferenced(with: loader) - async let (newRequestBody, c2) = oldRequestBody.externallyDereferenced(with: loader) - async let (newResponses, c3) = oldResponses.externallyDereferenced(with: loader) - async let (newCallbacks, c4) = callbacks.externallyDereferenced(with: loader) -// let (newServers, c6) = try await servers.externallyDereferenced(with: loader) + async let (newParameters, c1, m1) = oldParameters.externallyDereferenced(with: loader) + async let (newRequestBody, c2, m2) = oldRequestBody.externallyDereferenced(with: loader) + async let (newResponses, c3, m3) = oldResponses.externallyDereferenced(with: loader) + async let (newCallbacks, c4, m4) = callbacks.externallyDereferenced(with: loader) +// let (newServers, c5, m5) = try await servers.externallyDereferenced(with: loader) var newOperation = self var newComponents = try await c1 + var newMessages = try await m1 newOperation.parameters = try await newParameters newOperation.requestBody = try await newRequestBody try await newComponents.merge(c2) + try await newMessages += m2 newOperation.responses = try await newResponses try await newComponents.merge(c3) + try await newMessages += m3 newOperation.callbacks = try await newCallbacks try await newComponents.merge(c4) - - if let oldServers = servers { - let (newServers, c6) = try await oldServers.externallyDereferenced(with: loader) - newOperation.servers = newServers - try newComponents.merge(c6) - } + try await newMessages += m4 // should not be necessary but current Swift compiler can't figure out conformance of ExternallyDereferenceable: if let oldServers = servers { - let (newServers, c6) = try await oldServers.externallyDereferenced(with: loader) + let (newServers, c5, m5) = try await oldServers.externallyDereferenced(with: loader) newOperation.servers = newServers - try newComponents.merge(c6) + try newComponents.merge(c5) + newMessages += m5 } - return (newOperation, newComponents) + return (newOperation, newComponents, newMessages) } } diff --git a/Sources/OpenAPIKit30/Parameter/DereferencedParameter.swift b/Sources/OpenAPIKit30/Parameter/DereferencedParameter.swift index e0385e1c1..acb30c8d2 100644 --- a/Sources/OpenAPIKit30/Parameter/DereferencedParameter.swift +++ b/Sources/OpenAPIKit30/Parameter/DereferencedParameter.swift @@ -84,29 +84,32 @@ extension OpenAPI.Parameter: LocallyDereferenceable { } extension OpenAPI.Parameter: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { // if not for a Swift bug, this whole function would just be the // next line: -// let (newSchemaOrContent, components) = try await schemaOrContent.externallyDereferenced(with: loader) +// let (newSchemaOrContent, components, messages) = try await schemaOrContent.externallyDereferenced(with: loader) let newSchemaOrContent: Either let newComponents: OpenAPI.Components + let newMessages: [Loader.Message] switch schemaOrContent { case .a(let schemaContext): - let (context, components) = try await schemaContext.externallyDereferenced(with: loader) + let (context, components, messages) = try await schemaContext.externallyDereferenced(with: loader) newSchemaOrContent = .a(context) newComponents = components + newMessages = messages case .b(let contentMap): - let (map, components) = try await contentMap.externallyDereferenced(with: loader) + let (map, components, messages) = try await contentMap.externallyDereferenced(with: loader) newSchemaOrContent = .b(map) newComponents = components + newMessages = messages } var newParameter = self newParameter.schemaOrContent = newSchemaOrContent - return (newParameter, newComponents) + return (newParameter, newComponents, newMessages) } } diff --git a/Sources/OpenAPIKit30/Parameter/DereferencedSchemaContext.swift b/Sources/OpenAPIKit30/Parameter/DereferencedSchemaContext.swift index 3e7d357f4..9a035f1e7 100644 --- a/Sources/OpenAPIKit30/Parameter/DereferencedSchemaContext.swift +++ b/Sources/OpenAPIKit30/Parameter/DereferencedSchemaContext.swift @@ -70,22 +70,24 @@ extension OpenAPI.Parameter.SchemaContext: LocallyDereferenceable { } extension OpenAPI.Parameter.SchemaContext: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { let oldSchema = schema - async let (newSchema, c1) = oldSchema.externallyDereferenced(with: loader) + async let (newSchema, c1, m1) = oldSchema.externallyDereferenced(with: loader) var newSchemaContext = self var newComponents = try await c1 + var newMessages = try await m1 newSchemaContext.schema = try await newSchema if let oldExamples = examples { - let (newExamples, c2) = try await oldExamples.externallyDereferenced(with: loader) + let (newExamples, c2, m2) = try await oldExamples.externallyDereferenced(with: loader) newSchemaContext.examples = newExamples try newComponents.merge(c2) + newMessages += m2 } - return (newSchemaContext, newComponents) + return (newSchemaContext, newComponents, newMessages) } } diff --git a/Sources/OpenAPIKit30/Path Item/DereferencedPathItem.swift b/Sources/OpenAPIKit30/Path Item/DereferencedPathItem.swift index 9b7e5ac49..542b8aa54 100644 --- a/Sources/OpenAPIKit30/Path Item/DereferencedPathItem.swift +++ b/Sources/OpenAPIKit30/Path Item/DereferencedPathItem.swift @@ -139,7 +139,7 @@ extension OpenAPI.PathItem: LocallyDereferenceable { } extension OpenAPI.PathItem: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { let oldParameters = parameters let oldServers = servers let oldGet = get @@ -151,19 +151,20 @@ extension OpenAPI.PathItem: ExternallyDereferenceable { let oldPatch = patch let oldTrace = trace - async let (newParameters, c1) = oldParameters.externallyDereferenced(with: loader) -// async let (newServers, c2) = oldServers.externallyDereferenced(with: loader) - async let (newGet, c3) = oldGet.externallyDereferenced(with: loader) - async let (newPut, c4) = oldPut.externallyDereferenced(with: loader) - async let (newPost, c5) = oldPost.externallyDereferenced(with: loader) - async let (newDelete, c6) = oldDelete.externallyDereferenced(with: loader) - async let (newOptions, c7) = oldOptions.externallyDereferenced(with: loader) - async let (newHead, c8) = oldHead.externallyDereferenced(with: loader) - async let (newPatch, c9) = oldPatch.externallyDereferenced(with: loader) - async let (newTrace, c10) = oldTrace.externallyDereferenced(with: loader) + async let (newParameters, c1, m1) = oldParameters.externallyDereferenced(with: loader) +// async let (newServers, c2, m2) = oldServers.externallyDereferenced(with: loader) + async let (newGet, c3, m3) = oldGet.externallyDereferenced(with: loader) + async let (newPut, c4, m4) = oldPut.externallyDereferenced(with: loader) + async let (newPost, c5, m5) = oldPost.externallyDereferenced(with: loader) + async let (newDelete, c6, m6) = oldDelete.externallyDereferenced(with: loader) + async let (newOptions, c7, m7) = oldOptions.externallyDereferenced(with: loader) + async let (newHead, c8, m8) = oldHead.externallyDereferenced(with: loader) + async let (newPatch, c9, m9) = oldPatch.externallyDereferenced(with: loader) + async let (newTrace, c10, m10) = oldTrace.externallyDereferenced(with: loader) var pathItem = self var newComponents = try await c1 + var newMessages = try await m1 // ideally we would async let all of the props above and then set them here, // but for now since there seems to be some sort of compiler bug we will do @@ -187,12 +188,22 @@ extension OpenAPI.PathItem: ExternallyDereferenceable { try await newComponents.merge(c9) try await newComponents.merge(c10) + try await newMessages += m3 + try await newMessages += m4 + try await newMessages += m5 + try await newMessages += m6 + try await newMessages += m7 + try await newMessages += m8 + try await newMessages += m9 + try await newMessages += m10 + if let oldServers { - async let (newServers, c2) = oldServers.externallyDereferenced(with: loader) + async let (newServers, c2, m2) = oldServers.externallyDereferenced(with: loader) pathItem.servers = try await newServers try await newComponents.merge(c2) + try await newMessages += m2 } - return (pathItem, newComponents) + return (pathItem, newComponents, newMessages) } } diff --git a/Sources/OpenAPIKit30/Request/DereferencedRequest.swift b/Sources/OpenAPIKit30/Request/DereferencedRequest.swift index e59647cde..6c7fd3189 100644 --- a/Sources/OpenAPIKit30/Request/DereferencedRequest.swift +++ b/Sources/OpenAPIKit30/Request/DereferencedRequest.swift @@ -63,12 +63,12 @@ extension OpenAPI.Request: LocallyDereferenceable { } extension OpenAPI.Request: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { var newRequest = self - let (newContent, components) = try await content.externallyDereferenced(with: loader) + let (newContent, components, messages) = try await content.externallyDereferenced(with: loader) newRequest.content = newContent - return (newRequest, components) + return (newRequest, components, messages) } } diff --git a/Sources/OpenAPIKit30/Response/DereferencedResponse.swift b/Sources/OpenAPIKit30/Response/DereferencedResponse.swift index add208773..ced58ae20 100644 --- a/Sources/OpenAPIKit30/Response/DereferencedResponse.swift +++ b/Sources/OpenAPIKit30/Response/DereferencedResponse.swift @@ -78,14 +78,14 @@ extension OpenAPI.Response: LocallyDereferenceable { } extension OpenAPI.Response: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { let oldContent = content let oldLinks = links let oldHeaders = headers - async let (newContent, c1) = oldContent.externallyDereferenced(with: loader) - async let (newLinks, c2) = oldLinks.externallyDereferenced(with: loader) -// async let (newHeaders, c3) = oldHeaders.externallyDereferenced(with: loader) + async let (newContent, c1, m1) = oldContent.externallyDereferenced(with: loader) + async let (newLinks, c2, m2) = oldLinks.externallyDereferenced(with: loader) +// async let (newHeaders, c3, m3) = oldHeaders.externallyDereferenced(with: loader) var response = self response.content = try await newContent @@ -94,12 +94,16 @@ extension OpenAPI.Response: ExternallyDereferenceable { var components = try await c1 try await components.merge(c2) + var messages = try await m1 + try await messages += m2 + if let oldHeaders { - let (newHeaders, c3) = try await oldHeaders.externallyDereferenced(with: loader) + let (newHeaders, c3, m3) = try await oldHeaders.externallyDereferenced(with: loader) response.headers = newHeaders try components.merge(c3) + messages += m3 } - return (response, components) + return (response, components, messages) } } diff --git a/Sources/OpenAPIKit30/Schema Object/DereferencedJSONSchema.swift b/Sources/OpenAPIKit30/Schema Object/DereferencedJSONSchema.swift index 57776fe42..f749f549a 100644 --- a/Sources/OpenAPIKit30/Schema Object/DereferencedJSONSchema.swift +++ b/Sources/OpenAPIKit30/Schema Object/DereferencedJSONSchema.swift @@ -410,38 +410,47 @@ extension JSONSchema: LocallyDereferenceable { } extension JSONSchema: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { let newSchema: JSONSchema let newComponents: OpenAPI.Components + let newMessages: [Loader.Message] switch value { case .boolean(_): newComponents = .noComponents newSchema = self + newMessages = [] case .number(_, _): newComponents = .noComponents newSchema = self + newMessages = [] case .integer(_, _): newComponents = .noComponents newSchema = self + newMessages = [] case .string(_, _): newComponents = .noComponents newSchema = self + newMessages = [] case .object(let core, let object): var components = OpenAPI.Components() + var messages = [Loader.Message]() - let (newProperties, c1) = try await object.properties.externallyDereferenced(with: loader) + let (newProperties, c1, m1) = try await object.properties.externallyDereferenced(with: loader) try components.merge(c1) + messages += m1 let newAdditionalProperties: Either? if case .b(let schema) = object.additionalProperties { - let (additionalProperties, c2) = try await schema.externallyDereferenced(with: loader) + let (additionalProperties, c2, m2) = try await schema.externallyDereferenced(with: loader) try components.merge(c2) + messages += m2 newAdditionalProperties = .b(additionalProperties) } else { newAdditionalProperties = object.additionalProperties } newComponents = components + newMessages = messages newSchema = .init( schema: .object( core, @@ -455,8 +464,9 @@ extension JSONSchema: ExternallyDereferenceable { vendorExtensions: vendorExtensions ) case .array(let core, let array): - let (newItems, components) = try await array.items.externallyDereferenced(with: loader) + let (newItems, components, messages) = try await array.items.externallyDereferenced(with: loader) newComponents = components + newMessages = messages newSchema = .init( schema: .array( core, @@ -470,36 +480,41 @@ extension JSONSchema: ExternallyDereferenceable { vendorExtensions: vendorExtensions ) case .all(let schema, let core): - let (newSubschemas, components) = try await schema.externallyDereferenced(with: loader) + let (newSubschemas, components, messages) = try await schema.externallyDereferenced(with: loader) newComponents = components + newMessages = messages newSchema = .init( schema: .all(of: newSubschemas, core: core), vendorExtensions: vendorExtensions ) case .one(let schema, let core): - let (newSubschemas, components) = try await schema.externallyDereferenced(with: loader) + let (newSubschemas, components, messages) = try await schema.externallyDereferenced(with: loader) newComponents = components + newMessages = messages newSchema = .init( schema: .one(of: newSubschemas, core: core), vendorExtensions: vendorExtensions ) case .any(let schema, let core): - let (newSubschemas, components) = try await schema.externallyDereferenced(with: loader) + let (newSubschemas, components, messages) = try await schema.externallyDereferenced(with: loader) newComponents = components + newMessages = messages newSchema = .init( schema: .any(of: newSubschemas, core: core), vendorExtensions: vendorExtensions ) case .not(let schema, let core): - let (newSubschema, components) = try await schema.externallyDereferenced(with: loader) + let (newSubschema, components, messages) = try await schema.externallyDereferenced(with: loader) newComponents = components + newMessages = messages newSchema = .init( schema: .not(newSubschema, core: core), vendorExtensions: vendorExtensions ) case .reference(let reference, let core): - let (newReference, components) = try await reference.externallyDereferenced(with: loader) + let (newReference, components, messages) = try await reference.externallyDereferenced(with: loader) newComponents = components + newMessages = messages newSchema = .init( schema: .reference(newReference, core), vendorExtensions: vendorExtensions @@ -507,8 +522,9 @@ extension JSONSchema: ExternallyDereferenceable { case .fragment(_): newComponents = .noComponents newSchema = self + newMessages = [] } - return (newSchema, newComponents) + return (newSchema, newComponents, newMessages) } } diff --git a/Sources/OpenAPIKit30/Security/SecurityScheme.swift b/Sources/OpenAPIKit30/Security/SecurityScheme.swift index 364196b67..22171d097 100644 --- a/Sources/OpenAPIKit30/Security/SecurityScheme.swift +++ b/Sources/OpenAPIKit30/Security/SecurityScheme.swift @@ -252,8 +252,8 @@ extension OpenAPI.SecurityScheme: LocallyDereferenceable { } extension OpenAPI.SecurityScheme: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { - return (self, .init()) + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { + return (self, .init(), []) } } diff --git a/Sources/OpenAPIKit30/Server.swift b/Sources/OpenAPIKit30/Server.swift index 5990e297b..d44a280bf 100644 --- a/Sources/OpenAPIKit30/Server.swift +++ b/Sources/OpenAPIKit30/Server.swift @@ -246,8 +246,8 @@ extension OpenAPI.Server.Variable { } extension OpenAPI.Server: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { - return (self, .init()) + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { + return (self, .init(), []) } } diff --git a/Sources/OpenAPIKit30/Utility/Array+ExternallyDereferenceable.swift b/Sources/OpenAPIKit30/Utility/Array+ExternallyDereferenceable.swift index a67002587..3a959b7ce 100644 --- a/Sources/OpenAPIKit30/Utility/Array+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit30/Utility/Array+ExternallyDereferenceable.swift @@ -6,8 +6,8 @@ import OpenAPIKitCore extension Array where Element: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { - try await withThrowingTaskGroup(of: (Int, (Element, OpenAPI.Components)).self) { group in + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { + try await withThrowingTaskGroup(of: (Int, (Element, OpenAPI.Components, [Loader.Message])).self) { group in for (idx, elem) in zip(self.indices, self) { group.addTask { return try await (idx, elem.externallyDereferenced(with: loader)) @@ -16,15 +16,17 @@ extension Array where Element: ExternallyDereferenceable { var newElems = Array<(Int, Element)>() var newComponents = OpenAPI.Components() + var newMessages = [Loader.Message]() - for try await (idx, (elem, components)) in group { + for try await (idx, (elem, components, messages)) in group { newElems.append((idx, elem)) try newComponents.merge(components) + newMessages += messages } // things may come in out of order because of concurrency // so we reorder after completing all entries. newElems.sort { left, right in left.0 < right.0 } - return (newElems.map { $0.1 }, newComponents) + return (newElems.map { $0.1 }, newComponents, newMessages) } } } diff --git a/Sources/OpenAPIKit30/Utility/Dictionary+ExternallyDereferenceable.swift b/Sources/OpenAPIKit30/Utility/Dictionary+ExternallyDereferenceable.swift index d15b123b2..1369bb788 100644 --- a/Sources/OpenAPIKit30/Utility/Dictionary+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit30/Utility/Dictionary+ExternallyDereferenceable.swift @@ -7,23 +7,25 @@ import OpenAPIKitCore extension Dictionary where Value: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { - try await withThrowingTaskGroup(of: (Key, Value, OpenAPI.Components).self) { group in + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { + try await withThrowingTaskGroup(of: (Key, Value, OpenAPI.Components, [Loader.Message]).self) { group in for (key, value) in self { group.addTask { - let (newRef, components) = try await value.externallyDereferenced(with: loader) - return (key, newRef, components) + let (newRef, components, messages) = try await value.externallyDereferenced(with: loader) + return (key, newRef, components, messages) } } var newDict = Self() var newComponents = OpenAPI.Components() + var newMessages = [Loader.Message]() - for try await (key, newRef, components) in group { + for try await (key, newRef, components, messages) in group { newDict[key] = newRef try newComponents.merge(components) + newMessages += messages } - return (newDict, newComponents) + return (newDict, newComponents, newMessages) } } } diff --git a/Sources/OpenAPIKit30/Utility/Optional+ExternallyDereferenceable.swift b/Sources/OpenAPIKit30/Utility/Optional+ExternallyDereferenceable.swift index 79b055b52..87c7a0649 100644 --- a/Sources/OpenAPIKit30/Utility/Optional+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit30/Utility/Optional+ExternallyDereferenceable.swift @@ -6,8 +6,8 @@ import OpenAPIKitCore extension Optional where Wrapped: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { - guard let wrapped = self else { return (nil, .init()) } + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { + guard let wrapped = self else { return (nil, .init(), []) } return try await wrapped.externallyDereferenced(with: loader) } } diff --git a/Sources/OpenAPIKit30/Utility/OrderedDictionary+ExternallyDereferenceable.swift b/Sources/OpenAPIKit30/Utility/OrderedDictionary+ExternallyDereferenceable.swift index 38ab9bbc3..1c882a7ce 100644 --- a/Sources/OpenAPIKit30/Utility/OrderedDictionary+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit30/Utility/OrderedDictionary+ExternallyDereferenceable.swift @@ -9,26 +9,28 @@ import OpenAPIKitCore extension OrderedDictionary where Value: ExternallyDereferenceable { - public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) { - try await withThrowingTaskGroup(of: (Key, Value, OpenAPI.Components).self) { group in + public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { + try await withThrowingTaskGroup(of: (Key, Value, OpenAPI.Components, [Loader.Message]).self) { group in for (key, value) in self { group.addTask { - let (newRef, components) = try await value.externallyDereferenced(with: loader) - return (key, newRef, components) + let (newRef, components, messages) = try await value.externallyDereferenced(with: loader) + return (key, newRef, components, messages) } } var newDict = Self() var newComponents = OpenAPI.Components() + var newMessages = [Loader.Message]() - for try await (key, newRef, components) in group { + for try await (key, newRef, components, messages) in group { newDict[key] = newRef try newComponents.merge(components) + newMessages += messages } // things may come in out of order because of concurrency // so we reorder after completing all entries. try newDict.applyOrder(self) - return (newDict, newComponents) + return (newDict, newComponents, newMessages) } } } From 595c80f03ebedcf9d0419b6cf34fb8ad95e0c8a0 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 25 Apr 2024 09:09:40 -0500 Subject: [PATCH 063/110] update tests --- .../ExternalDereferencingDocumentTests.swift | 6 ++++-- Tests/OpenAPIKit30Tests/JSONReferenceTests.swift | 15 ++++++++++----- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift b/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift index 862f47474..0a4cf20f9 100644 --- a/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift +++ b/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift @@ -14,7 +14,9 @@ final class ExternalDereferencingDocumentTests: XCTestCase { /// An example of implementing a loader context for loading external references /// into an OpenAPI document. struct ExampleLoader: ExternalLoader { - static func load(_ url: URL) async throws -> T where T : Decodable { + typealias Message = Void + + static func load(_ url: URL) async throws -> (T, [Message]) where T : Decodable { // load data from file, perhaps. we will just mock that up for the test: let data = try await mockData(componentKey(type: T.self, at: url)) @@ -30,7 +32,7 @@ final class ExternalDereferencingDocumentTests: XCTestCase { } else { finished = decoded } - return finished + return (finished, []) } static func componentKey(type: T.Type, at url: URL) throws -> OpenAPIKit30.OpenAPI.ComponentKey { diff --git a/Tests/OpenAPIKit30Tests/JSONReferenceTests.swift b/Tests/OpenAPIKit30Tests/JSONReferenceTests.swift index 5aca879c0..25332acd5 100644 --- a/Tests/OpenAPIKit30Tests/JSONReferenceTests.swift +++ b/Tests/OpenAPIKit30Tests/JSONReferenceTests.swift @@ -341,8 +341,10 @@ extension JSONReferenceTests { } struct SchemaLoader: ExternalLoader { - static func load(_ url: URL) -> T where T: Decodable { - return JSONSchema.string as! T + typealias Message = String + + static func load(_ url: URL) -> (T, [Message]) where T: Decodable { + return (JSONSchema.string as! T, [url.absoluteString]) } static func componentKey(type: T.Type, at url: URL) throws -> OpenAPI.ComponentKey { @@ -359,27 +361,30 @@ extension JSONReferenceTests { func test_externalDerefNoFragment() async throws { let reference: JSONReference = .external(.init(string: "./schema.json")!) - let (newReference, components) = try await reference.externallyDereferenced(with: SchemaLoader.self) + let (newReference, components, messages) = try await reference.externallyDereferenced(with: SchemaLoader.self) XCTAssertEqual(newReference, .component(named: "__schema_json")) XCTAssertEqual(components, .init(schemas: ["__schema_json": .string])) + XCTAssertEqual(messages, ["./schema.json"]) } func test_externalDerefFragment() async throws { let reference: JSONReference = .external(.init(string: "./schema.json#/test")!) - let (newReference, components) = try await reference.externallyDereferenced(with: SchemaLoader.self) + let (newReference, components, messages) = try await reference.externallyDereferenced(with: SchemaLoader.self) XCTAssertEqual(newReference, .component(named: "__schema_json__test")) XCTAssertEqual(components, .init(schemas: ["__schema_json__test": .string])) + XCTAssertEqual(messages, ["./schema.json#/test"]) } func test_externalDerefExternalComponents() async throws { let reference: JSONReference = .external(.init(string: "./schema.json#/components/schemas/test")!) - let (newReference, components) = try await reference.externallyDereferenced(with: SchemaLoader.self) + let (newReference, components, messages) = try await reference.externallyDereferenced(with: SchemaLoader.self) XCTAssertEqual(newReference, .component(named: "__schema_json__components_schemas_test")) XCTAssertEqual(components, .init(schemas: ["__schema_json__components_schemas_test": .string])) + XCTAssertEqual(messages, ["./schema.json#/components/schemas/test"]) } } From c6d90f591e58dc6641302ad22fb2b5b8588a5f88 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 25 Apr 2024 09:15:59 -0500 Subject: [PATCH 064/110] update README --- README.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5f0eccb65..db2363de1 100644 --- a/README.md +++ b/README.md @@ -330,23 +330,25 @@ OpenAPIKit leaves it to you to decide how to load external files and where to st ```swift struct ExampleLoader: ExternalLoader { - static func load(_ url: URL) async throws -> T where T : Decodable { + typealias Message = Void + + static func load(_ url: URL) async throws -> (T, [Message]) where T : Decodable { // load data from file, perhaps. we will just mock that up for the test: let data = try await mockData(componentKey(type: T.self, at: url)) - // We use the YAML decoder mostly for order-stability in this case but it is - // also nice that it will handle both YAML and JSON data. + // We use the YAML decoder purely for order-stability. let decoded = try YAMLDecoder().decode(T.self, from: data) let finished: T // while unnecessary, a loader may likely want to attatch some extra info - // to keep track of where a reference was loaded from. + // to keep track of where a reference was loaded from. This test makes sure + // the following strategy of using vendor extensions works. if var extendable = decoded as? VendorExtendable { extendable.vendorExtensions["x-source-url"] = AnyCodable(url) finished = extendable as! T } else { finished = decoded } - return finished + return (finished, []) } static func componentKey(type: T.Type, at url: URL) throws -> OpenAPIKit.OpenAPI.ComponentKey { @@ -362,6 +364,8 @@ struct ExampleLoader: ExternalLoader { Once you have an `ExternalLoader`, you can call an `OpenAPI.Document`'s `externallyDereference()` method to externally dereference it. You get to choose whether to only load references to a certain depth or to fully resolve references until you run out of them; any given referenced document may itself contain references and these references may point back to things loaded into the Document previously so dereferencing is done recursively up to a given depth (or until fully dereferenced if you use the `.full` depth). +If you have some information that you want to pass back to yourself from the `load()` function, you can specify any type you want as the `Message` type and return any number of messages from each `load()` function execution. These messages could be warnings, additional information about the files that each newly loaded Component came from, etc. If you want to tie some information about file loading to new Components in your messages, you can use the `componentKey()` function to get the key the new Component will be found under once external dereferencing is complete. + #### Internal References In addition to looking something up in the `Components` object, you can entirely derefererence many OpenAPIKit types. A dereferenced type has had all of its references looked up (and all of its properties' references, all the way down). From 1d8f5567990c805a2e1bc0b0b745f546f09d23c7 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 25 Apr 2024 09:41:27 -0500 Subject: [PATCH 065/110] test external dereferencing messages at the full document level. --- .../Operation/DereferencedOperation.swift | 8 +++-- .../ExternalDereferencingDocumentTests.swift | 29 ++++++++++++++++--- .../ExternalDereferencingDocumentTests.swift | 27 +++++++++++++++-- 3 files changed, 54 insertions(+), 10 deletions(-) diff --git a/Sources/OpenAPIKit30/Operation/DereferencedOperation.swift b/Sources/OpenAPIKit30/Operation/DereferencedOperation.swift index ebcfccc78..2bb1bbd16 100644 --- a/Sources/OpenAPIKit30/Operation/DereferencedOperation.swift +++ b/Sources/OpenAPIKit30/Operation/DereferencedOperation.swift @@ -130,12 +130,14 @@ extension OpenAPI.Operation: ExternallyDereferenceable { let oldParameters = parameters let oldRequestBody = requestBody let oldResponses = responses + let oldCallbacks = callbacks + let oldServers = servers async let (newParameters, c1, m1) = oldParameters.externallyDereferenced(with: loader) async let (newRequestBody, c2, m2) = oldRequestBody.externallyDereferenced(with: loader) async let (newResponses, c3, m3) = oldResponses.externallyDereferenced(with: loader) - async let (newCallbacks, c4, m4) = callbacks.externallyDereferenced(with: loader) -// let (newServers, c5, m5) = try await servers.externallyDereferenced(with: loader) + async let (newCallbacks, c4, m4) = oldCallbacks.externallyDereferenced(with: loader) +// let (newServers, c5, m5) = try await oldServers.externallyDereferenced(with: loader) var newOperation = self var newComponents = try await c1 @@ -153,7 +155,7 @@ extension OpenAPI.Operation: ExternallyDereferenceable { try await newMessages += m4 // should not be necessary but current Swift compiler can't figure out conformance of ExternallyDereferenceable: - if let oldServers = servers { + if let oldServers { let (newServers, c5, m5) = try await oldServers.externallyDereferenced(with: loader) newOperation.servers = newServers try newComponents.merge(c5) diff --git a/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift b/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift index 0a4cf20f9..977a6b6f0 100644 --- a/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift +++ b/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift @@ -14,7 +14,7 @@ final class ExternalDereferencingDocumentTests: XCTestCase { /// An example of implementing a loader context for loading external references /// into an OpenAPI document. struct ExampleLoader: ExternalLoader { - typealias Message = Void + typealias Message = String static func load(_ url: URL) async throws -> (T, [Message]) where T : Decodable { // load data from file, perhaps. we will just mock that up for the test: @@ -32,7 +32,7 @@ final class ExternalDereferencingDocumentTests: XCTestCase { } else { finished = decoded } - return (finished, []) + return (finished, [url.absoluteString]) } static func componentKey(type: T.Type, at url: URL) throws -> OpenAPIKit30.OpenAPI.ComponentKey { @@ -217,7 +217,8 @@ final class ExternalDereferencingDocumentTests: XCTestCase { .reference(.external(URL(string: "file://./params/name.json")!)) ] ), - "/webhook": .reference(.external(URL(string: "file://./paths/webhook.json")!)) + "/webhook": .reference(.external(URL(string: "file://./paths/webhook.json")!)), + "/callback": .reference(.external(URL(string: "file://./paths/callback.json")!)) ], components: .init( schemas: [ @@ -242,10 +243,30 @@ final class ExternalDereferencingDocumentTests: XCTestCase { docCopy2.components.sort() var docCopy3 = document - try await docCopy3.externallyDereference(with: ExampleLoader.self, depth: .full) + let messages = try await docCopy3.externallyDereference(with: ExampleLoader.self, depth: .full) docCopy3.components.sort() XCTAssertEqual(docCopy1, docCopy2) XCTAssertEqual(docCopy2, docCopy3) + + XCTAssertEqual( + messages.sorted(), + ["file://./callbacks/one.json", + "file://./examples/good.json", + "file://./headers/webhook.json", + "file://./headers/webhook.json", + "file://./links/first.json", + "file://./params/name.json", + "file://./params/name.json", + "file://./paths/callback.json", + "file://./paths/webhook.json", + "file://./requests/webhook.json", + "file://./responses/webhook.json", + "file://./schemas/basic_object.json", + "file://./schemas/string_param.json", + "file://./schemas/string_param.json", + "file://./schemas/string_param.json", + "file://./schemas/string_param.json#"] + ) } } diff --git a/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift b/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift index 15c1a346a..89fa48186 100644 --- a/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift @@ -14,7 +14,7 @@ final class ExternalDereferencingDocumentTests: XCTestCase { /// An example of implementing a loader context for loading external references /// into an OpenAPI document. struct ExampleLoader: ExternalLoader { - typealias Message = Void + typealias Message = String static func load(_ url: URL) async throws -> (T, [Message]) where T : Decodable { // load data from file, perhaps. we will just mock that up for the test: @@ -32,7 +32,7 @@ final class ExternalDereferencingDocumentTests: XCTestCase { } else { finished = decoded } - return (finished, []) + return (finished, [url.absoluteString]) } static func componentKey(type: T.Type, at url: URL) throws -> OpenAPIKit.OpenAPI.ComponentKey { @@ -245,10 +245,31 @@ final class ExternalDereferencingDocumentTests: XCTestCase { docCopy2.components.sort() var docCopy3 = document - try await docCopy3.externallyDereference(with: ExampleLoader.self, depth: .full) + let messages = try await docCopy3.externallyDereference(with: ExampleLoader.self, depth: .full) docCopy3.components.sort() XCTAssertEqual(docCopy1, docCopy2) XCTAssertEqual(docCopy2, docCopy3) + + XCTAssertEqual( + messages.sorted(), + ["file://./callbacks/one.json", + "file://./examples/good.json", + "file://./headers/webhook.json", + "file://./headers/webhook.json", + "file://./links/first.json", + "file://./params/name.json", + "file://./params/name.json", + "file://./paths/callback.json", + "file://./paths/webhook.json", + "file://./paths/webhook.json", + "file://./requests/webhook.json", + "file://./responses/webhook.json", + "file://./schemas/basic_object.json", + "file://./schemas/string_param.json", + "file://./schemas/string_param.json", + "file://./schemas/string_param.json", + "file://./schemas/string_param.json#"] + ) } } From cbc02afb505105cff755ed8d877a7b243900275d Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 25 Apr 2024 10:15:52 -0500 Subject: [PATCH 066/110] expand test coverage --- .../ExternalDereferencingDocumentTests.swift | 16 +++++++++++++++- .../ExternalDereferencingDocumentTests.swift | 16 +++++++++++++++- Tests/OpenAPIKitTests/JSONReferenceTests.swift | 8 ++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift b/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift index 977a6b6f0..8b6f5f806 100644 --- a/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift +++ b/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift @@ -113,6 +113,9 @@ final class ExternalDereferencingDocumentTests: XCTestCase { "$ref": "file://./headers/webhook.json" } } + }, + "enc2": { + "style": "form" } } } @@ -140,7 +143,7 @@ final class ExternalDereferencingDocumentTests: XCTestCase { }, "headers": { "X-Hello": { - "$ref": "file://./headers/webhook.json" + "$ref": "file://./headers/webhook2.json" } } } @@ -152,6 +155,17 @@ final class ExternalDereferencingDocumentTests: XCTestCase { } } """, + "headers_webhook2_json": """ + { + "content": { + "application/json": { + "schema": { + "$ref": "file://./schemas/string_param.json" + } + } + } + } + """, "examples_good_json": """ { "value": "{\\"body\\": \\"request me\\"}" diff --git a/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift b/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift index 89fa48186..c3f64891d 100644 --- a/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift @@ -113,6 +113,9 @@ final class ExternalDereferencingDocumentTests: XCTestCase { "$ref": "file://./headers/webhook.json" } } + }, + "enc2": { + "style": "form" } } } @@ -140,7 +143,7 @@ final class ExternalDereferencingDocumentTests: XCTestCase { }, "headers": { "X-Hello": { - "$ref": "file://./headers/webhook.json" + "$ref": "file://./headers/webhook2.json" } } } @@ -152,6 +155,17 @@ final class ExternalDereferencingDocumentTests: XCTestCase { } } """, + "headers_webhook2_json": """ + { + "content": { + "application/json": { + "schema": { + "$ref": "file://./schemas/string_param.json" + } + } + } + } + """, "examples_good_json": """ { "value": "{\\"body\\": \\"request me\\"}" diff --git a/Tests/OpenAPIKitTests/JSONReferenceTests.swift b/Tests/OpenAPIKitTests/JSONReferenceTests.swift index bb0d6d6d3..296009552 100644 --- a/Tests/OpenAPIKitTests/JSONReferenceTests.swift +++ b/Tests/OpenAPIKitTests/JSONReferenceTests.swift @@ -17,6 +17,8 @@ final class JSONReferenceTests: XCTestCase { XCTAssertEqual(t1, t2) XCTAssertTrue(t1.isInternal) XCTAssertFalse(t1.isExternal) + XCTAssertEqual(t1.internalValue, .init(rawValue: "/hello")) + XCTAssertNil(t1.externalValue) let t3 = JSONReference.component(named: "hello") let t4 = JSONReference.internal(.component(name: "hello")) @@ -27,6 +29,8 @@ final class JSONReferenceTests: XCTestCase { let externalTest = JSONReference.external(URL(string: "hello.json")!) XCTAssertFalse(externalTest.isInternal) XCTAssertTrue(externalTest.isExternal) + XCTAssertNil(externalTest.internalValue) + XCTAssertEqual(externalTest.externalValue, URL(string: "hello.json")) let t5 = JSONReference.InternalReference("#/hello/world") let t6 = JSONReference.InternalReference(rawValue: "#/hello/world") @@ -169,6 +173,10 @@ final class JSONReferenceTests: XCTestCase { XCTAssertEqual(t7.openAPIReference(withDescription: "hi").description, "hi") XCTAssertEqual(t8.openAPIReference(withDescription: "hi").description, "hi") XCTAssertEqual(t9.openAPIReference(withDescription: "hi").description, "hi") + + // test dynamic member lookup: + XCTAssertEqual(t1.openAPIReference().internalValue, .component(name: "hello")) + } } From 3744e9cadad2407bcf31e925b7613b73a153cd97 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 25 Apr 2024 10:22:28 -0500 Subject: [PATCH 067/110] fix whitespace, recurse one step further. --- .../ExternalDereferencingDocumentTests.swift | 33 ++++++++--------- .../ExternalDereferencingDocumentTests.swift | 35 ++++++++++--------- 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift b/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift index 8b6f5f806..9abb1a0d5 100644 --- a/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift +++ b/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift @@ -250,10 +250,11 @@ final class ExternalDereferencingDocumentTests: XCTestCase { try await docCopy1.externallyDereference(with: ExampleLoader.self) try await docCopy1.externallyDereference(with: ExampleLoader.self) try await docCopy1.externallyDereference(with: ExampleLoader.self) + try await docCopy1.externallyDereference(with: ExampleLoader.self) docCopy1.components.sort() var docCopy2 = document - try await docCopy2.externallyDereference(with: ExampleLoader.self, depth: 3) + try await docCopy2.externallyDereference(with: ExampleLoader.self, depth: 4) docCopy2.components.sort() var docCopy3 = document @@ -266,21 +267,21 @@ final class ExternalDereferencingDocumentTests: XCTestCase { XCTAssertEqual( messages.sorted(), ["file://./callbacks/one.json", - "file://./examples/good.json", - "file://./headers/webhook.json", - "file://./headers/webhook.json", - "file://./links/first.json", - "file://./params/name.json", - "file://./params/name.json", - "file://./paths/callback.json", - "file://./paths/webhook.json", - "file://./requests/webhook.json", - "file://./responses/webhook.json", - "file://./schemas/basic_object.json", - "file://./schemas/string_param.json", - "file://./schemas/string_param.json", - "file://./schemas/string_param.json", - "file://./schemas/string_param.json#"] + "file://./examples/good.json", + "file://./headers/webhook.json", + "file://./headers/webhook.json", + "file://./links/first.json", + "file://./params/name.json", + "file://./params/name.json", + "file://./paths/callback.json", + "file://./paths/webhook.json", + "file://./requests/webhook.json", + "file://./responses/webhook.json", + "file://./schemas/basic_object.json", + "file://./schemas/string_param.json", + "file://./schemas/string_param.json", + "file://./schemas/string_param.json", + "file://./schemas/string_param.json#"] ) } } diff --git a/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift b/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift index c3f64891d..4fced614d 100644 --- a/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift @@ -252,10 +252,11 @@ final class ExternalDereferencingDocumentTests: XCTestCase { try await docCopy1.externallyDereference(with: ExampleLoader.self) try await docCopy1.externallyDereference(with: ExampleLoader.self) try await docCopy1.externallyDereference(with: ExampleLoader.self) + try await docCopy1.externallyDereference(with: ExampleLoader.self) docCopy1.components.sort() var docCopy2 = document - try await docCopy2.externallyDereference(with: ExampleLoader.self, depth: 3) + try await docCopy2.externallyDereference(with: ExampleLoader.self, depth: 4) docCopy2.components.sort() var docCopy3 = document @@ -268,22 +269,22 @@ final class ExternalDereferencingDocumentTests: XCTestCase { XCTAssertEqual( messages.sorted(), ["file://./callbacks/one.json", - "file://./examples/good.json", - "file://./headers/webhook.json", - "file://./headers/webhook.json", - "file://./links/first.json", - "file://./params/name.json", - "file://./params/name.json", - "file://./paths/callback.json", - "file://./paths/webhook.json", - "file://./paths/webhook.json", - "file://./requests/webhook.json", - "file://./responses/webhook.json", - "file://./schemas/basic_object.json", - "file://./schemas/string_param.json", - "file://./schemas/string_param.json", - "file://./schemas/string_param.json", - "file://./schemas/string_param.json#"] + "file://./examples/good.json", + "file://./headers/webhook.json", + "file://./headers/webhook.json", + "file://./links/first.json", + "file://./params/name.json", + "file://./params/name.json", + "file://./paths/callback.json", + "file://./paths/webhook.json", + "file://./paths/webhook.json", + "file://./requests/webhook.json", + "file://./responses/webhook.json", + "file://./schemas/basic_object.json", + "file://./schemas/string_param.json", + "file://./schemas/string_param.json", + "file://./schemas/string_param.json", + "file://./schemas/string_param.json#"] ) } } From aeed07256db1ab48569ee1dba878fd64bd4dc0ea Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 25 Apr 2024 10:27:30 -0500 Subject: [PATCH 068/110] fix tests i broke --- .../Document/ExternalDereferencingDocumentTests.swift | 3 ++- .../Document/ExternalDereferencingDocumentTests.swift | 3 ++- Tests/OpenAPIKitTests/JSONReferenceTests.swift | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift b/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift index 9abb1a0d5..d9863a92c 100644 --- a/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift +++ b/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift @@ -269,7 +269,7 @@ final class ExternalDereferencingDocumentTests: XCTestCase { ["file://./callbacks/one.json", "file://./examples/good.json", "file://./headers/webhook.json", - "file://./headers/webhook.json", + "file://./headers/webhook2.json", "file://./links/first.json", "file://./params/name.json", "file://./params/name.json", @@ -281,6 +281,7 @@ final class ExternalDereferencingDocumentTests: XCTestCase { "file://./schemas/string_param.json", "file://./schemas/string_param.json", "file://./schemas/string_param.json", + "file://./schemas/string_param.json", "file://./schemas/string_param.json#"] ) } diff --git a/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift b/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift index 4fced614d..3aa95b8f0 100644 --- a/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift @@ -271,7 +271,7 @@ final class ExternalDereferencingDocumentTests: XCTestCase { ["file://./callbacks/one.json", "file://./examples/good.json", "file://./headers/webhook.json", - "file://./headers/webhook.json", + "file://./headers/webhook2.json", "file://./links/first.json", "file://./params/name.json", "file://./params/name.json", @@ -284,6 +284,7 @@ final class ExternalDereferencingDocumentTests: XCTestCase { "file://./schemas/string_param.json", "file://./schemas/string_param.json", "file://./schemas/string_param.json", + "file://./schemas/string_param.json", "file://./schemas/string_param.json#"] ) } diff --git a/Tests/OpenAPIKitTests/JSONReferenceTests.swift b/Tests/OpenAPIKitTests/JSONReferenceTests.swift index 296009552..4587d47cf 100644 --- a/Tests/OpenAPIKitTests/JSONReferenceTests.swift +++ b/Tests/OpenAPIKitTests/JSONReferenceTests.swift @@ -17,7 +17,7 @@ final class JSONReferenceTests: XCTestCase { XCTAssertEqual(t1, t2) XCTAssertTrue(t1.isInternal) XCTAssertFalse(t1.isExternal) - XCTAssertEqual(t1.internalValue, .init(rawValue: "/hello")) + XCTAssertEqual(t1.internalValue, .init(rawValue: "#/hello")) XCTAssertNil(t1.externalValue) let t3 = JSONReference.component(named: "hello") From 6ab90e589bf6ff09b9a794b02301b9d2c94e48c6 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 25 Apr 2024 10:40:00 -0500 Subject: [PATCH 069/110] Add missing components-> links external dereferencing. --- .../OpenAPIKit/Components Object/Components.swift | 14 ++++++++++---- .../Components Object/Components.swift | 14 ++++++++++---- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/Sources/OpenAPIKit/Components Object/Components.swift b/Sources/OpenAPIKit/Components Object/Components.swift index 509bbe3d7..da6322396 100644 --- a/Sources/OpenAPIKit/Components Object/Components.swift +++ b/Sources/OpenAPIKit/Components Object/Components.swift @@ -333,6 +333,7 @@ extension OpenAPI.Components { let oldRequestBodies = requestBodies let oldHeaders = headers let oldSecuritySchemes = securitySchemes + let oldLinks = links let oldCallbacks = callbacks let oldPathItems = pathItems @@ -343,8 +344,9 @@ extension OpenAPI.Components { async let (newRequestBodies, c5, m5) = oldRequestBodies.externallyDereferenced(with: loader) async let (newHeaders, c6, m6) = oldHeaders.externallyDereferenced(with: loader) async let (newSecuritySchemes, c7, m7) = oldSecuritySchemes.externallyDereferenced(with: loader) - async let (newCallbacks, c8, m8) = oldCallbacks.externallyDereferenced(with: loader) - async let (newPathItems, c9, m9) = oldPathItems.externallyDereferenced(with: loader) + async let (newLinks, c8, m8) = oldLinks.externallyDereferenced(with: loader) + async let (newCallbacks, c9, m9) = oldCallbacks.externallyDereferenced(with: loader) + async let (newPathItems, c10, m10) = oldPathItems.externallyDereferenced(with: loader) schemas = try await newSchemas responses = try await newResponses @@ -353,6 +355,7 @@ extension OpenAPI.Components { requestBodies = try await newRequestBodies headers = try await newHeaders securitySchemes = try await newSecuritySchemes + links = try await newLinks callbacks = try await newCallbacks pathItems = try await newPathItems @@ -365,6 +368,7 @@ extension OpenAPI.Components { let c7Resolved = try await c7 let c8Resolved = try await c8 let c9Resolved = try await c9 + let c10Resolved = try await c10 let noNewComponents = c1Resolved.isEmpty @@ -376,8 +380,9 @@ extension OpenAPI.Components { && c7Resolved.isEmpty && c8Resolved.isEmpty && c9Resolved.isEmpty + && c10Resolved.isEmpty - let newMessages = try await context + m1 + m2 + m3 + m4 + m5 + m6 + m7 + m8 + m9 + let newMessages = try await context + m1 + m2 + m3 + m4 + m5 + m6 + m7 + m8 + m9 + m10 if noNewComponents { return newMessages } @@ -390,7 +395,8 @@ extension OpenAPI.Components { try merge(c7Resolved) try merge(c8Resolved) try merge(c9Resolved) - + try merge(c10Resolved) + switch depth { case .iterations(let number): return try await externallyDereference(with: loader, depth: .iterations(number - 1), context: newMessages) diff --git a/Sources/OpenAPIKit30/Components Object/Components.swift b/Sources/OpenAPIKit30/Components Object/Components.swift index c37338a08..fb3d70039 100644 --- a/Sources/OpenAPIKit30/Components Object/Components.swift +++ b/Sources/OpenAPIKit30/Components Object/Components.swift @@ -325,6 +325,7 @@ extension OpenAPI.Components { let oldRequestBodies = requestBodies let oldHeaders = headers let oldSecuritySchemes = securitySchemes + let oldLinks = links let oldCallbacks = callbacks let oldPathItems = pathItems @@ -335,8 +336,9 @@ extension OpenAPI.Components { async let (newRequestBodies, c5, m5) = oldRequestBodies.externallyDereferenced(with: loader) async let (newHeaders, c6, m6) = oldHeaders.externallyDereferenced(with: loader) async let (newSecuritySchemes, c7, m7) = oldSecuritySchemes.externallyDereferenced(with: loader) - async let (newCallbacks, c8, m8) = oldCallbacks.externallyDereferenced(with: loader) - async let (newPathItems, c9, m9) = oldPathItems.externallyDereferenced(with: loader) + async let (newLinks, c8, m8) = oldLinks.externallyDereferenced(with: loader) + async let (newCallbacks, c9, m9) = oldCallbacks.externallyDereferenced(with: loader) + async let (newPathItems, c10, m10) = oldPathItems.externallyDereferenced(with: loader) schemas = try await newSchemas responses = try await newResponses @@ -345,6 +347,7 @@ extension OpenAPI.Components { requestBodies = try await newRequestBodies headers = try await newHeaders securitySchemes = try await newSecuritySchemes + links = try await newLinks callbacks = try await newCallbacks pathItems = try await newPathItems @@ -357,6 +360,7 @@ extension OpenAPI.Components { let c7Resolved = try await c7 let c8Resolved = try await c8 let c9Resolved = try await c9 + let c10Resolved = try await c10 let noNewComponents = c1Resolved.isEmpty @@ -368,8 +372,9 @@ extension OpenAPI.Components { && c7Resolved.isEmpty && c8Resolved.isEmpty && c9Resolved.isEmpty + && c10Resolved.isEmpty - let newMessages = try await context + m1 + m2 + m3 + m4 + m5 + m6 + m7 + m8 + m9 + let newMessages = try await context + m1 + m2 + m3 + m4 + m5 + m6 + m7 + m8 + m9 + m10 if noNewComponents { return newMessages } @@ -382,7 +387,8 @@ extension OpenAPI.Components { try merge(c7Resolved) try merge(c8Resolved) try merge(c9Resolved) - + try merge(c10Resolved) + switch depth { case .iterations(let number): return try await externallyDereference(with: loader, depth: .iterations(number - 1), context: newMessages) From da089f88d3963c8be28d6c98a99f03d19618aec4 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 25 Apr 2024 21:59:03 -0500 Subject: [PATCH 070/110] see if this is the difference between compilation succeeding or not --- .../OpenAPIKit/Components Object/Components.swift | 14 +++++++------- .../Components Object/Components.swift | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Sources/OpenAPIKit/Components Object/Components.swift b/Sources/OpenAPIKit/Components Object/Components.swift index da6322396..280c7c93c 100644 --- a/Sources/OpenAPIKit/Components Object/Components.swift +++ b/Sources/OpenAPIKit/Components Object/Components.swift @@ -333,7 +333,7 @@ extension OpenAPI.Components { let oldRequestBodies = requestBodies let oldHeaders = headers let oldSecuritySchemes = securitySchemes - let oldLinks = links +// let oldLinks = links let oldCallbacks = callbacks let oldPathItems = pathItems @@ -344,7 +344,7 @@ extension OpenAPI.Components { async let (newRequestBodies, c5, m5) = oldRequestBodies.externallyDereferenced(with: loader) async let (newHeaders, c6, m6) = oldHeaders.externallyDereferenced(with: loader) async let (newSecuritySchemes, c7, m7) = oldSecuritySchemes.externallyDereferenced(with: loader) - async let (newLinks, c8, m8) = oldLinks.externallyDereferenced(with: loader) +// async let (newLinks, c8, m8) = oldLinks.externallyDereferenced(with: loader) async let (newCallbacks, c9, m9) = oldCallbacks.externallyDereferenced(with: loader) async let (newPathItems, c10, m10) = oldPathItems.externallyDereferenced(with: loader) @@ -355,7 +355,7 @@ extension OpenAPI.Components { requestBodies = try await newRequestBodies headers = try await newHeaders securitySchemes = try await newSecuritySchemes - links = try await newLinks +// links = try await newLinks callbacks = try await newCallbacks pathItems = try await newPathItems @@ -366,7 +366,7 @@ extension OpenAPI.Components { let c5Resolved = try await c5 let c6Resolved = try await c6 let c7Resolved = try await c7 - let c8Resolved = try await c8 +// let c8Resolved = try await c8 let c9Resolved = try await c9 let c10Resolved = try await c10 @@ -378,11 +378,11 @@ extension OpenAPI.Components { && c5Resolved.isEmpty && c6Resolved.isEmpty && c7Resolved.isEmpty - && c8Resolved.isEmpty +// && c8Resolved.isEmpty && c9Resolved.isEmpty && c10Resolved.isEmpty - let newMessages = try await context + m1 + m2 + m3 + m4 + m5 + m6 + m7 + m8 + m9 + m10 + let newMessages = try await context + m1 + m2 + m3 + m4 + m5 + m6 + m7 + m9 + m10 // + m8 if noNewComponents { return newMessages } @@ -393,7 +393,7 @@ extension OpenAPI.Components { try merge(c5Resolved) try merge(c6Resolved) try merge(c7Resolved) - try merge(c8Resolved) +// try merge(c8Resolved) try merge(c9Resolved) try merge(c10Resolved) diff --git a/Sources/OpenAPIKit30/Components Object/Components.swift b/Sources/OpenAPIKit30/Components Object/Components.swift index fb3d70039..25b40bccb 100644 --- a/Sources/OpenAPIKit30/Components Object/Components.swift +++ b/Sources/OpenAPIKit30/Components Object/Components.swift @@ -325,7 +325,7 @@ extension OpenAPI.Components { let oldRequestBodies = requestBodies let oldHeaders = headers let oldSecuritySchemes = securitySchemes - let oldLinks = links +// let oldLinks = links let oldCallbacks = callbacks let oldPathItems = pathItems @@ -336,7 +336,7 @@ extension OpenAPI.Components { async let (newRequestBodies, c5, m5) = oldRequestBodies.externallyDereferenced(with: loader) async let (newHeaders, c6, m6) = oldHeaders.externallyDereferenced(with: loader) async let (newSecuritySchemes, c7, m7) = oldSecuritySchemes.externallyDereferenced(with: loader) - async let (newLinks, c8, m8) = oldLinks.externallyDereferenced(with: loader) +// async let (newLinks, c8, m8) = oldLinks.externallyDereferenced(with: loader) async let (newCallbacks, c9, m9) = oldCallbacks.externallyDereferenced(with: loader) async let (newPathItems, c10, m10) = oldPathItems.externallyDereferenced(with: loader) @@ -347,7 +347,7 @@ extension OpenAPI.Components { requestBodies = try await newRequestBodies headers = try await newHeaders securitySchemes = try await newSecuritySchemes - links = try await newLinks +// links = try await newLinks callbacks = try await newCallbacks pathItems = try await newPathItems @@ -358,7 +358,7 @@ extension OpenAPI.Components { let c5Resolved = try await c5 let c6Resolved = try await c6 let c7Resolved = try await c7 - let c8Resolved = try await c8 +// let c8Resolved = try await c8 let c9Resolved = try await c9 let c10Resolved = try await c10 @@ -370,11 +370,11 @@ extension OpenAPI.Components { && c5Resolved.isEmpty && c6Resolved.isEmpty && c7Resolved.isEmpty - && c8Resolved.isEmpty +// && c8Resolved.isEmpty && c9Resolved.isEmpty && c10Resolved.isEmpty - let newMessages = try await context + m1 + m2 + m3 + m4 + m5 + m6 + m7 + m8 + m9 + m10 + let newMessages = try await context + m1 + m2 + m3 + m4 + m5 + m6 + m7 + m9 + m10 //+ m8 if noNewComponents { return newMessages } @@ -385,7 +385,7 @@ extension OpenAPI.Components { try merge(c5Resolved) try merge(c6Resolved) try merge(c7Resolved) - try merge(c8Resolved) +// try merge(c8Resolved) try merge(c9Resolved) try merge(c10Resolved) From 7055ecb8a30274bba81cf240f57a0629366f7020 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 25 Apr 2024 22:06:53 -0500 Subject: [PATCH 071/110] see if I can uncomment most of it --- Sources/OpenAPIKit/Components Object/Components.swift | 8 ++++---- Sources/OpenAPIKit30/Components Object/Components.swift | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Sources/OpenAPIKit/Components Object/Components.swift b/Sources/OpenAPIKit/Components Object/Components.swift index 280c7c93c..0acec021a 100644 --- a/Sources/OpenAPIKit/Components Object/Components.swift +++ b/Sources/OpenAPIKit/Components Object/Components.swift @@ -333,7 +333,7 @@ extension OpenAPI.Components { let oldRequestBodies = requestBodies let oldHeaders = headers let oldSecuritySchemes = securitySchemes -// let oldLinks = links + let oldLinks = links let oldCallbacks = callbacks let oldPathItems = pathItems @@ -344,7 +344,7 @@ extension OpenAPI.Components { async let (newRequestBodies, c5, m5) = oldRequestBodies.externallyDereferenced(with: loader) async let (newHeaders, c6, m6) = oldHeaders.externallyDereferenced(with: loader) async let (newSecuritySchemes, c7, m7) = oldSecuritySchemes.externallyDereferenced(with: loader) -// async let (newLinks, c8, m8) = oldLinks.externallyDereferenced(with: loader) + async let (newLinks, c8, m8) = oldLinks.externallyDereferenced(with: loader) async let (newCallbacks, c9, m9) = oldCallbacks.externallyDereferenced(with: loader) async let (newPathItems, c10, m10) = oldPathItems.externallyDereferenced(with: loader) @@ -355,7 +355,7 @@ extension OpenAPI.Components { requestBodies = try await newRequestBodies headers = try await newHeaders securitySchemes = try await newSecuritySchemes -// links = try await newLinks + links = try await newLinks callbacks = try await newCallbacks pathItems = try await newPathItems @@ -366,7 +366,7 @@ extension OpenAPI.Components { let c5Resolved = try await c5 let c6Resolved = try await c6 let c7Resolved = try await c7 -// let c8Resolved = try await c8 + let c8Resolved = try await c8 let c9Resolved = try await c9 let c10Resolved = try await c10 diff --git a/Sources/OpenAPIKit30/Components Object/Components.swift b/Sources/OpenAPIKit30/Components Object/Components.swift index 25b40bccb..dc9fc494d 100644 --- a/Sources/OpenAPIKit30/Components Object/Components.swift +++ b/Sources/OpenAPIKit30/Components Object/Components.swift @@ -325,7 +325,7 @@ extension OpenAPI.Components { let oldRequestBodies = requestBodies let oldHeaders = headers let oldSecuritySchemes = securitySchemes -// let oldLinks = links + let oldLinks = links let oldCallbacks = callbacks let oldPathItems = pathItems @@ -336,7 +336,7 @@ extension OpenAPI.Components { async let (newRequestBodies, c5, m5) = oldRequestBodies.externallyDereferenced(with: loader) async let (newHeaders, c6, m6) = oldHeaders.externallyDereferenced(with: loader) async let (newSecuritySchemes, c7, m7) = oldSecuritySchemes.externallyDereferenced(with: loader) -// async let (newLinks, c8, m8) = oldLinks.externallyDereferenced(with: loader) + async let (newLinks, c8, m8) = oldLinks.externallyDereferenced(with: loader) async let (newCallbacks, c9, m9) = oldCallbacks.externallyDereferenced(with: loader) async let (newPathItems, c10, m10) = oldPathItems.externallyDereferenced(with: loader) @@ -347,7 +347,7 @@ extension OpenAPI.Components { requestBodies = try await newRequestBodies headers = try await newHeaders securitySchemes = try await newSecuritySchemes -// links = try await newLinks + links = try await newLinks callbacks = try await newCallbacks pathItems = try await newPathItems @@ -358,7 +358,7 @@ extension OpenAPI.Components { let c5Resolved = try await c5 let c6Resolved = try await c6 let c7Resolved = try await c7 -// let c8Resolved = try await c8 + let c8Resolved = try await c8 let c9Resolved = try await c9 let c10Resolved = try await c10 From a3d852c4326e5136c869d7a8ccb36069a125ca61 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 25 Apr 2024 22:11:23 -0500 Subject: [PATCH 072/110] try and find the limit --- .../Components Object/Components.swift | 31 +++++++++++++------ .../Components Object/Components.swift | 31 +++++++++++++------ 2 files changed, 44 insertions(+), 18 deletions(-) diff --git a/Sources/OpenAPIKit/Components Object/Components.swift b/Sources/OpenAPIKit/Components Object/Components.swift index 0acec021a..cc1996b68 100644 --- a/Sources/OpenAPIKit/Components Object/Components.swift +++ b/Sources/OpenAPIKit/Components Object/Components.swift @@ -326,6 +326,10 @@ extension OpenAPI.Components { return context } + // NOTE: The links and callbacks related code commented out below pushes Swift 5.8 and 5.9 + // over the edge and you get exit code 137 crashes in CI. + // Swift 5.10 handles it fine. + let oldSchemas = schemas let oldResponses = responses let oldParameters = parameters @@ -344,8 +348,8 @@ extension OpenAPI.Components { async let (newRequestBodies, c5, m5) = oldRequestBodies.externallyDereferenced(with: loader) async let (newHeaders, c6, m6) = oldHeaders.externallyDereferenced(with: loader) async let (newSecuritySchemes, c7, m7) = oldSecuritySchemes.externallyDereferenced(with: loader) - async let (newLinks, c8, m8) = oldLinks.externallyDereferenced(with: loader) - async let (newCallbacks, c9, m9) = oldCallbacks.externallyDereferenced(with: loader) +// async let (newLinks, c8, m8) = oldLinks.externallyDereferenced(with: loader) +// async let (newCallbacks, c9, m9) = oldCallbacks.externallyDereferenced(with: loader) async let (newPathItems, c10, m10) = oldPathItems.externallyDereferenced(with: loader) schemas = try await newSchemas @@ -355,8 +359,8 @@ extension OpenAPI.Components { requestBodies = try await newRequestBodies headers = try await newHeaders securitySchemes = try await newSecuritySchemes - links = try await newLinks - callbacks = try await newCallbacks +// links = try await newLinks +// callbacks = try await newCallbacks pathItems = try await newPathItems let c1Resolved = try await c1 @@ -366,10 +370,19 @@ extension OpenAPI.Components { let c5Resolved = try await c5 let c6Resolved = try await c6 let c7Resolved = try await c7 - let c8Resolved = try await c8 - let c9Resolved = try await c9 +// let c8Resolved = try await c8 +// let c9Resolved = try await c9 let c10Resolved = try await c10 + // For Swift 5.10+ we can delete the following links and callbacks code and uncomment the + // preferred code above. + let (newLinks, c8, m8) = try await oldLinks.externallyDereferenced(with: loader) + links = newLinks + let c8Resolved = c8 + let (newCallbacks, c9, m9) = try await oldCallbacks.externallyDereferenced(with: loader) + callbacks = newCallbacks + let c9Resolved = c9 + let noNewComponents = c1Resolved.isEmpty && c2Resolved.isEmpty @@ -378,11 +391,11 @@ extension OpenAPI.Components { && c5Resolved.isEmpty && c6Resolved.isEmpty && c7Resolved.isEmpty -// && c8Resolved.isEmpty + && c8Resolved.isEmpty && c9Resolved.isEmpty && c10Resolved.isEmpty - let newMessages = try await context + m1 + m2 + m3 + m4 + m5 + m6 + m7 + m9 + m10 // + m8 + let newMessages = try await context + m1 + m2 + m3 + m4 + m5 + m6 + m7 + m8 + m9 + m10 if noNewComponents { return newMessages } @@ -393,7 +406,7 @@ extension OpenAPI.Components { try merge(c5Resolved) try merge(c6Resolved) try merge(c7Resolved) -// try merge(c8Resolved) + try merge(c8Resolved) try merge(c9Resolved) try merge(c10Resolved) diff --git a/Sources/OpenAPIKit30/Components Object/Components.swift b/Sources/OpenAPIKit30/Components Object/Components.swift index dc9fc494d..352f4841b 100644 --- a/Sources/OpenAPIKit30/Components Object/Components.swift +++ b/Sources/OpenAPIKit30/Components Object/Components.swift @@ -318,6 +318,10 @@ extension OpenAPI.Components { return context } + // NOTE: The links and callbacks related code commented out below pushes Swift 5.8 and 5.9 + // over the edge and you get exit code 137 crashes in CI. + // Swift 5.10 handles it fine. + let oldSchemas = schemas let oldResponses = responses let oldParameters = parameters @@ -336,8 +340,8 @@ extension OpenAPI.Components { async let (newRequestBodies, c5, m5) = oldRequestBodies.externallyDereferenced(with: loader) async let (newHeaders, c6, m6) = oldHeaders.externallyDereferenced(with: loader) async let (newSecuritySchemes, c7, m7) = oldSecuritySchemes.externallyDereferenced(with: loader) - async let (newLinks, c8, m8) = oldLinks.externallyDereferenced(with: loader) - async let (newCallbacks, c9, m9) = oldCallbacks.externallyDereferenced(with: loader) +// async let (newLinks, c8, m8) = oldLinks.externallyDereferenced(with: loader) +// async let (newCallbacks, c9, m9) = oldCallbacks.externallyDereferenced(with: loader) async let (newPathItems, c10, m10) = oldPathItems.externallyDereferenced(with: loader) schemas = try await newSchemas @@ -347,8 +351,8 @@ extension OpenAPI.Components { requestBodies = try await newRequestBodies headers = try await newHeaders securitySchemes = try await newSecuritySchemes - links = try await newLinks - callbacks = try await newCallbacks +// links = try await newLinks +// callbacks = try await newCallbacks pathItems = try await newPathItems let c1Resolved = try await c1 @@ -358,10 +362,19 @@ extension OpenAPI.Components { let c5Resolved = try await c5 let c6Resolved = try await c6 let c7Resolved = try await c7 - let c8Resolved = try await c8 - let c9Resolved = try await c9 +// let c8Resolved = try await c8 +// let c9Resolved = try await c9 let c10Resolved = try await c10 + // For Swift 5.10+ we can delete the following links and callbacks code and uncomment the + // preferred code above. + let (newLinks, c8, m8) = try await oldLinks.externallyDereferenced(with: loader) + links = newLinks + let c8Resolved = c8 + let (newCallbacks, c9, m9) = try await oldCallbacks.externallyDereferenced(with: loader) + callbacks = newCallbacks + let c9Resolved = c9 + let noNewComponents = c1Resolved.isEmpty && c2Resolved.isEmpty @@ -370,11 +383,11 @@ extension OpenAPI.Components { && c5Resolved.isEmpty && c6Resolved.isEmpty && c7Resolved.isEmpty -// && c8Resolved.isEmpty + && c8Resolved.isEmpty && c9Resolved.isEmpty && c10Resolved.isEmpty - let newMessages = try await context + m1 + m2 + m3 + m4 + m5 + m6 + m7 + m9 + m10 //+ m8 + let newMessages = try await context + m1 + m2 + m3 + m4 + m5 + m6 + m7 + m8 + m9 + m10 if noNewComponents { return newMessages } @@ -385,7 +398,7 @@ extension OpenAPI.Components { try merge(c5Resolved) try merge(c6Resolved) try merge(c7Resolved) -// try merge(c8Resolved) + try merge(c8Resolved) try merge(c9Resolved) try merge(c10Resolved) From f8e3248e207c23bb3df32c3757835694b3c4753e Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 25 Apr 2024 22:41:32 -0500 Subject: [PATCH 073/110] use newer swift for codecov (faster compilation) --- .github/workflows/codecov.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 0d392008d..01689c62d 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -5,7 +5,7 @@ on: [pull_request] jobs: codecov: container: - image: swift:5.8 + image: swift:5.10 runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 From 3fde60e4636f6d7f8f20ab56d981aed3d09ec164 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 25 Apr 2024 23:10:52 -0500 Subject: [PATCH 074/110] add external dereferencing and v4 to the features/versions table --- README.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index db2363de1..ae17c51a1 100644 --- a/README.md +++ b/README.md @@ -6,17 +6,19 @@ A library containing Swift types that encode to- and decode from [OpenAPI 3.0.x](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md) and [OpenAPI 3.1.x](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md) Documents and their components. -The single most confusing thing you will grapple with out of the gate is explained by the following grid of what OpenAPIKit versions support which OpenAPI specification versions. +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. -| OpenAPIKit | OpenAPI v3.0 | OpenAPI v3.1 | -|-------------|---------------|--------------| -| v2.x | ✅ | ❌ | -| v3.x | ✅ | ✅ | +| OpenAPIKit | Swift | OpenAPI v3.0 | OpenAPI v3.1 | External Dereferencing | +|------------|-------|--------------|--------------|------------------------| +| v2.x | 5.1+ | ✅ | ❌ | ❌ | +| v3.x | 5.1+ | ✅ | ✅ | ❌ | +| v4.x | 5.8+ | ✅ | ✅ | ✅ | - [Usage](#usage) - [Migration](#migration) - [1.x to 2.x](#1.x-to-2.x) - [2.x to 3.x](#2.x-to-3.x) + - [3.x to 4.x](#3.x-to-4.x) - [Decoding OpenAPI Documents](#decoding-openapi-documents) - [Decoding Errors](#decoding-errors) - [Encoding OpenAPI Documents](#encoding-openapi-documents) @@ -73,6 +75,9 @@ import OpenAPIKit 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. +#### 3.x to 4.x +This section has not been written yet. Stay tuned! + ### Decoding OpenAPI Documents 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. From 8a3ad28b68c7fc25ad8c6347d2f26ebbcb71b131 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Fri, 19 Jul 2024 10:49:02 -0500 Subject: [PATCH 075/110] remove deprecated content encoding property --- .../OpenAPIKit/Content/ContentEncoding.swift | 23 +++---------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/Sources/OpenAPIKit/Content/ContentEncoding.swift b/Sources/OpenAPIKit/Content/ContentEncoding.swift index 20814859a..30c40f874 100644 --- a/Sources/OpenAPIKit/Content/ContentEncoding.swift +++ b/Sources/OpenAPIKit/Content/ContentEncoding.swift @@ -14,21 +14,6 @@ extension OpenAPI.Content { public struct Encoding: Equatable { public typealias Style = OpenAPI.Parameter.SchemaContext.Style - /// If an encoding object only contains 1 content type, it will be populated here. - /// Two or more content types will result in a null value here but the `contentTypes` - /// (plural) property will contain all content types specified. - /// - /// The singular `contentType` property is only provided for backwards compatibility and - /// using the plural `contentTypes` property should be preferred. - @available(*, deprecated, message: "use contentTypes instead") - public var contentType: OpenAPI.ContentType? { - guard let contentType = contentTypes.first, - contentTypes.count == 1 else { - return nil - } - return contentType - } - public let contentTypes: [OpenAPI.ContentType] public let headers: OpenAPI.Header.Map? public let style: Style @@ -38,13 +23,12 @@ extension OpenAPI.Content { /// The singular `contentType` argument is only provided for backwards compatibility and /// using the plural `contentTypes` argument should be preferred. public init( - contentType: OpenAPI.ContentType? = nil, contentTypes: [OpenAPI.ContentType] = [], headers: OpenAPI.Header.Map? = nil, style: Style = Self.defaultStyle, allowReserved: Bool = false ) { - self.contentTypes = contentTypes + [contentType].compactMap { $0 } + self.contentTypes = contentTypes self.headers = headers self.style = style self.explode = style.defaultExplode @@ -54,14 +38,13 @@ extension OpenAPI.Content { /// The singular `contentType` argument is only provided for backwards compatibility and /// using the plural `contentTypes` argument should be preferred. public init( - contentType: OpenAPI.ContentType? = nil, contentTypes: [OpenAPI.ContentType] = [], headers: OpenAPI.Header.Map? = nil, style: Style = Self.defaultStyle, explode: Bool, allowReserved: Bool = false ) { - self.contentTypes = contentTypes + [contentType].compactMap { $0 } + self.contentTypes = contentTypes self.headers = headers self.style = style self.explode = explode @@ -104,7 +87,7 @@ extension OpenAPI.Content.Encoding: Decodable { let container = try decoder.container(keyedBy: CodingKeys.self) let contentTypesString = try container.decodeIfPresent(String.self, forKey: .contentType) - if let contentTypesString = contentTypesString { + if let contentTypesString { contentTypes = contentTypesString .split(separator: ",") .compactMap { string in From 3d6665755246bf10aae524205a0cc5567eb6d624 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Fri, 19 Jul 2024 10:50:09 -0500 Subject: [PATCH 076/110] don't test against bionic 5.8 image --- .github/workflows/tests.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8cf275044..9aeedd053 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,6 @@ jobs: fail-fast: false matrix: image: - - swift:5.8-bionic - swift:5.8-focal - swift:5.8-jammy - swift:5.9-focal From d11fe0c30321225cfffdd0331c7f7480dea80716 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Fri, 19 Jul 2024 23:31:24 -0500 Subject: [PATCH 077/110] remove jsonschema fallback implementations. fix code that used old content type argument to Encoding constructor --- .../Content/DereferencedContentEncoding.swift | 2 +- .../Schema Object/JSONSchemaContext.swift | 16 ---------------- Sources/OpenAPIKitCompat/Compat30To31.swift | 2 +- 3 files changed, 2 insertions(+), 18 deletions(-) diff --git a/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift b/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift index aaa9a1fd5..b715e7ac7 100644 --- a/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift +++ b/Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift @@ -72,7 +72,7 @@ extension OpenAPI.Content.Encoding: ExternallyDereferenceable { } let newEncoding = OpenAPI.Content.Encoding( - contentType: contentType, + contentTypes: contentTypes, headers: newHeaders, style: style, explode: explode, diff --git a/Sources/OpenAPIKit/Schema Object/JSONSchemaContext.swift b/Sources/OpenAPIKit/Schema Object/JSONSchemaContext.swift index 37be04003..e425e4ddc 100644 --- a/Sources/OpenAPIKit/Schema Object/JSONSchemaContext.swift +++ b/Sources/OpenAPIKit/Schema Object/JSONSchemaContext.swift @@ -135,22 +135,6 @@ public protocol JSONSchemaContext { var vendorExtensions: [String: AnyCodable] { get } } -extension JSONSchemaContext { - - // TODO: Remove the default implementations of the following in v4 of OpenAPIKit. - // They are only here to make their addition non-breaking. - - // Default implementation to make addition of this new property which is only - // supposed to be set internally a non-breaking addition. - public var inferred: Bool { false } - - // Default implementation to make addition non-breaking - public var anchor: String? { nil } - - // Default implementation to make addition non-breaking - public var dynamicAnchor: String? { nil } -} - extension JSONSchema { /// The context that applies to all schemas. public struct CoreContext: JSONSchemaContext, HasWarnings { diff --git a/Sources/OpenAPIKitCompat/Compat30To31.swift b/Sources/OpenAPIKitCompat/Compat30To31.swift index 444aabfea..bb128b282 100644 --- a/Sources/OpenAPIKitCompat/Compat30To31.swift +++ b/Sources/OpenAPIKitCompat/Compat30To31.swift @@ -214,7 +214,7 @@ extension OpenAPIKit30.OpenAPI.Parameter.SchemaContext: To31 { extension OpenAPIKit30.OpenAPI.Content.Encoding: To31 { fileprivate func to31() -> OpenAPIKit.OpenAPI.Content.Encoding { OpenAPIKit.OpenAPI.Content.Encoding( - contentType: contentType, + contentTypes: [contentType].compactMap { $0 }, headers: headers?.mapValues(eitherRefTo31), style: style, explode: explode, From 7ec84469c237f05a835cec67b2ae8623ca78c087 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Fri, 19 Jul 2024 23:38:39 -0500 Subject: [PATCH 078/110] update some tests --- .../DocumentConversionTests.swift | 2 +- .../OpenAPIKitTests/Content/ContentTests.swift | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Tests/OpenAPIKitCompatTests/DocumentConversionTests.swift b/Tests/OpenAPIKitCompatTests/DocumentConversionTests.swift index 99617df42..fc0c42e31 100644 --- a/Tests/OpenAPIKitCompatTests/DocumentConversionTests.swift +++ b/Tests/OpenAPIKitCompatTests/DocumentConversionTests.swift @@ -1233,7 +1233,7 @@ fileprivate func assertEqualNewToOld(_ newExample: OpenAPIKit.OpenAPI.Example, _ } fileprivate func assertEqualNewToOld(_ newEncoding: OpenAPIKit.OpenAPI.Content.Encoding, _ oldEncoding: OpenAPIKit30.OpenAPI.Content.Encoding) throws { - XCTAssertEqual(newEncoding.contentType, oldEncoding.contentType) + XCTAssertEqual(newEncoding.contentTypes.first, oldEncoding.contentType) if let newEncodingHeaders = newEncoding.headers { let oldEncodingHeaders = try XCTUnwrap(oldEncoding.headers) for ((newKey, newHeader), (oldKey, oldHeader)) in zip(newEncodingHeaders, oldEncodingHeaders) { diff --git a/Tests/OpenAPIKitTests/Content/ContentTests.swift b/Tests/OpenAPIKitTests/Content/ContentTests.swift index 72afe5a15..300bf67ee 100644 --- a/Tests/OpenAPIKitTests/Content/ContentTests.swift +++ b/Tests/OpenAPIKitTests/Content/ContentTests.swift @@ -66,7 +66,7 @@ final class ContentTests: XCTestCase { example: nil, encoding: [ "hello": .init( - contentType: .json, + contentTypes: [.json], headers: [ "world": .init(OpenAPI.Header(schemaOrContent: .init(.header(.string)))) ], @@ -355,7 +355,7 @@ extension ContentTests { func test_encodingAndSchema_encode() { let content = OpenAPI.Content( schema: .init(.string), - encoding: ["json": .init(contentType: .json)] + encoding: ["json": .init(contentTypes: [.json])] ) let encodedContent = try! orderUnstableTestStringFromEncoding(of: content) @@ -397,7 +397,7 @@ extension ContentTests { content, OpenAPI.Content( schema: .init(.string), - encoding: ["json": .init(contentType: .json)] + encoding: ["json": .init(contentTypes: [.json])] ) ) } @@ -500,18 +500,18 @@ extension ContentTests { func test_encodingInit() { let _ = OpenAPI.Content.Encoding() - let _ = OpenAPI.Content.Encoding(contentType: .json) + let _ = OpenAPI.Content.Encoding(contentTypes: [.json]) let _ = OpenAPI.Content.Encoding(headers: ["special": .a(.external(URL(string: "hello.yml")!))]) let _ = OpenAPI.Content.Encoding(allowReserved: true) - let _ = OpenAPI.Content.Encoding(contentType: .form, + let _ = OpenAPI.Content.Encoding(contentTypes: [.form], headers: ["special": .a(.external(URL(string: "hello.yml")!))], allowReserved: true) - let _ = OpenAPI.Content.Encoding(contentType: .json, + let _ = OpenAPI.Content.Encoding(contentTypes: [.json], style: .form) - let _ = OpenAPI.Content.Encoding(contentType: .json, + let _ = OpenAPI.Content.Encoding(contentTypes: [.json], style: .form, explode: true) } @@ -544,7 +544,7 @@ extension ContentTests { } func test_encoding_contentType_encode() throws { - let encoding = OpenAPI.Content.Encoding(contentType: .csv) + let encoding = OpenAPI.Content.Encoding(contentTypes: [.csv]) let encodedEncoding = try! orderUnstableTestStringFromEncoding(of: encoding) @@ -567,7 +567,7 @@ extension ContentTests { """.data(using: .utf8)! let encoding = try! orderUnstableDecode(OpenAPI.Content.Encoding.self, from: encodingData) - XCTAssertEqual(encoding, OpenAPI.Content.Encoding(contentType: .csv)) + XCTAssertEqual(encoding, OpenAPI.Content.Encoding(contentTypes: [.csv])) } func test_encoding_multiple_contentTypes_encode() throws { From 198d90d057f62c54834a1ad123d8c8d34c1fc6a1 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sun, 21 Jul 2024 18:18:21 -0500 Subject: [PATCH 079/110] remove outdated code comments --- Sources/OpenAPIKit/Content/ContentEncoding.swift | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Sources/OpenAPIKit/Content/ContentEncoding.swift b/Sources/OpenAPIKit/Content/ContentEncoding.swift index 30c40f874..65d55b754 100644 --- a/Sources/OpenAPIKit/Content/ContentEncoding.swift +++ b/Sources/OpenAPIKit/Content/ContentEncoding.swift @@ -20,8 +20,6 @@ extension OpenAPI.Content { public let explode: Bool public let allowReserved: Bool - /// The singular `contentType` argument is only provided for backwards compatibility and - /// using the plural `contentTypes` argument should be preferred. public init( contentTypes: [OpenAPI.ContentType] = [], headers: OpenAPI.Header.Map? = nil, @@ -35,8 +33,6 @@ extension OpenAPI.Content { self.allowReserved = allowReserved } - /// The singular `contentType` argument is only provided for backwards compatibility and - /// using the plural `contentTypes` argument should be preferred. public init( contentTypes: [OpenAPI.ContentType] = [], headers: OpenAPI.Header.Map? = nil, From f80f2e30eae740dc9cd20f786405075404456187 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 3 Oct 2024 11:41:04 -0500 Subject: [PATCH 080/110] use newer if let syntax --- Sources/OpenAPIKit/JSONReference.swift | 12 +++--- .../Utility/Container+DecodeURLAsString.swift | 38 +++++++++--------- Sources/OpenAPIKit30/JSONReference.swift | 20 +++++----- .../Utility/Container+DecodeURLAsString.swift | 40 +++++++++---------- .../URLTemplate/URLTemplate.swift | 20 +++++----- .../Utility/Container+DecodeURLAsString.swift | 40 +++++++++---------- 6 files changed, 85 insertions(+), 85 deletions(-) diff --git a/Sources/OpenAPIKit/JSONReference.swift b/Sources/OpenAPIKit/JSONReference.swift index ba66b671a..3a5d24e07 100644 --- a/Sources/OpenAPIKit/JSONReference.swift +++ b/Sources/OpenAPIKit/JSONReference.swift @@ -469,19 +469,19 @@ extension JSONReference: Decodable { } self = .internal(internalReference) } else { - let externalReferenceCandidate: URL? + let externalReference: URL? #if canImport(FoundationEssentials) - externalReferenceCandidate = URL(string: referenceString, encodingInvalidCharacters: false) + externalReference = URL(string: referenceString, encodingInvalidCharacters: false) #elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS) if #available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) { - externalReferenceCandidate = URL(string: referenceString, encodingInvalidCharacters: false) + externalReference = URL(string: referenceString, encodingInvalidCharacters: false) } else { - externalReferenceCandidate = URL(string: referenceString) + externalReference = URL(string: referenceString) } #else - externalReferenceCandidate = URL(string: referenceString) + externalReference = URL(string: referenceString) #endif - guard let externalReference = externalReferenceCandidate else { + guard let externalReference else { throw InconsistencyError( subjectName: "JSON Reference", details: "Failed to parse a valid URI for a JSON Reference from '\(referenceString)'", diff --git a/Sources/OpenAPIKit/Utility/Container+DecodeURLAsString.swift b/Sources/OpenAPIKit/Utility/Container+DecodeURLAsString.swift index ae5fd9fb5..d750d99de 100644 --- a/Sources/OpenAPIKit/Utility/Container+DecodeURLAsString.swift +++ b/Sources/OpenAPIKit/Utility/Container+DecodeURLAsString.swift @@ -11,19 +11,19 @@ import Foundation extension KeyedDecodingContainerProtocol { internal func decodeURLAsString(forKey key: Self.Key) throws -> URL { let string = try decode(String.self, forKey: key) - let urlCandidate: URL? -#if canImport(FoundationEssentials) - urlCandidate = URL(string: string, encodingInvalidCharacters: false) -#elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + let url: URL? + #if canImport(FoundationEssentials) + url = URL(string: string, encodingInvalidCharacters: false) + #elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS) if #available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) { - urlCandidate = URL(string: string, encodingInvalidCharacters: false) + url = URL(string: string, encodingInvalidCharacters: false) } else { - urlCandidate = URL(string: string) + url = URL(string: string) } -#else - urlCandidate = URL(string: string) -#endif - guard let url = urlCandidate else { + #else + url = URL(string: string) + #endif + guard let url else { throw InconsistencyError( subjectName: key.stringValue, details: "If specified, must be a valid URL", @@ -38,19 +38,19 @@ extension KeyedDecodingContainerProtocol { return nil } - let urlCandidate: URL? -#if canImport(FoundationEssentials) - urlCandidate = URL(string: string, encodingInvalidCharacters: false) -#elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + let url: URL? + #if canImport(FoundationEssentials) + url = URL(string: string, encodingInvalidCharacters: false) + #elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS) if #available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) { - urlCandidate = URL(string: string, encodingInvalidCharacters: false) + url = URL(string: string, encodingInvalidCharacters: false) } else { - urlCandidate = URL(string: string) + url = URL(string: string) } -#else - urlCandidate = URL(string: string) + #else + url = URL(string: string) #endif - guard let url = urlCandidate else { + guard let url else { throw InconsistencyError( subjectName: key.stringValue, details: "If specified, must be a valid URL", diff --git a/Sources/OpenAPIKit30/JSONReference.swift b/Sources/OpenAPIKit30/JSONReference.swift index e1d85c684..e39883bc5 100644 --- a/Sources/OpenAPIKit30/JSONReference.swift +++ b/Sources/OpenAPIKit30/JSONReference.swift @@ -333,19 +333,19 @@ extension JSONReference: Decodable { } self = .internal(internalReference) } else { - let externalReferenceCandidate: URL? -#if canImport(FoundationEssentials) - externalReferenceCandidate = URL(string: referenceString, encodingInvalidCharacters: false) -#elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + let externalReference: URL? + #if canImport(FoundationEssentials) + externalReference = URL(string: referenceString, encodingInvalidCharacters: false) + #elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS) if #available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) { - externalReferenceCandidate = URL(string: referenceString, encodingInvalidCharacters: false) + externalReference = URL(string: referenceString, encodingInvalidCharacters: false) } else { - externalReferenceCandidate = URL(string: referenceString) + externalReference = URL(string: referenceString) } -#else - externalReferenceCandidate = URL(string: referenceString) -#endif - guard let externalReference = externalReferenceCandidate else { + #else + externalReference = URL(string: referenceString) + #endif + guard let externalReference else { throw InconsistencyError( subjectName: "JSON Reference", details: "Failed to parse a valid URI for a JSON Reference from '\(referenceString)'", diff --git a/Sources/OpenAPIKit30/Utility/Container+DecodeURLAsString.swift b/Sources/OpenAPIKit30/Utility/Container+DecodeURLAsString.swift index ae5fd9fb5..71e3f36a3 100644 --- a/Sources/OpenAPIKit30/Utility/Container+DecodeURLAsString.swift +++ b/Sources/OpenAPIKit30/Utility/Container+DecodeURLAsString.swift @@ -11,19 +11,19 @@ import Foundation extension KeyedDecodingContainerProtocol { internal func decodeURLAsString(forKey key: Self.Key) throws -> URL { let string = try decode(String.self, forKey: key) - let urlCandidate: URL? -#if canImport(FoundationEssentials) - urlCandidate = URL(string: string, encodingInvalidCharacters: false) -#elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + let url: URL? + #if canImport(FoundationEssentials) + url = URL(string: string, encodingInvalidCharacters: false) + #elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS) if #available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) { - urlCandidate = URL(string: string, encodingInvalidCharacters: false) + url = URL(string: string, encodingInvalidCharacters: false) } else { - urlCandidate = URL(string: string) + url = URL(string: string) } -#else - urlCandidate = URL(string: string) -#endif - guard let url = urlCandidate else { + #else + url = URL(string: string) + #endif + guard let url else { throw InconsistencyError( subjectName: key.stringValue, details: "If specified, must be a valid URL", @@ -38,19 +38,19 @@ extension KeyedDecodingContainerProtocol { return nil } - let urlCandidate: URL? -#if canImport(FoundationEssentials) - urlCandidate = URL(string: string, encodingInvalidCharacters: false) -#elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + let url: URL? + #if canImport(FoundationEssentials) + url = URL(string: string, encodingInvalidCharacters: false) + #elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS) if #available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) { - urlCandidate = URL(string: string, encodingInvalidCharacters: false) + url = URL(string: string, encodingInvalidCharacters: false) } else { - urlCandidate = URL(string: string) + url = URL(string: string) } -#else - urlCandidate = URL(string: string) -#endif - guard let url = urlCandidate else { + #else + url = URL(string: string) + #endif + guard let url else { throw InconsistencyError( subjectName: key.stringValue, details: "If specified, must be a valid URL", diff --git a/Sources/OpenAPIKitCore/URLTemplate/URLTemplate.swift b/Sources/OpenAPIKitCore/URLTemplate/URLTemplate.swift index 0bebbc73c..a962abbd6 100644 --- a/Sources/OpenAPIKitCore/URLTemplate/URLTemplate.swift +++ b/Sources/OpenAPIKitCore/URLTemplate/URLTemplate.swift @@ -74,19 +74,19 @@ public struct URLTemplate: Hashable, RawRepresentable { /// Templated URLs with variables in them will not be valid URLs /// and are therefore guaranteed to return `nil`. public var url: URL? { - let urlCandidate: URL? -#if canImport(FoundationEssentials) - urlCandidate = URL(string: rawValue, encodingInvalidCharacters: false) -#elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + let url: URL? + #if canImport(FoundationEssentials) + url = URL(string: rawValue, encodingInvalidCharacters: false) + #elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS) if #available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) { - urlCandidate = URL(string: rawValue, encodingInvalidCharacters: false) + url = URL(string: rawValue, encodingInvalidCharacters: false) } else { - urlCandidate = URL(string: rawValue) + url = URL(string: rawValue) } -#else - urlCandidate = URL(string: rawValue) -#endif - return urlCandidate + #else + url = URL(string: rawValue) + #endif + return url } /// Get the names of all variables in the URL Template. diff --git a/Sources/OpenAPIKitCore/Utility/Container+DecodeURLAsString.swift b/Sources/OpenAPIKitCore/Utility/Container+DecodeURLAsString.swift index a1402ddb8..db12e7ed1 100644 --- a/Sources/OpenAPIKitCore/Utility/Container+DecodeURLAsString.swift +++ b/Sources/OpenAPIKitCore/Utility/Container+DecodeURLAsString.swift @@ -10,19 +10,19 @@ import Foundation extension KeyedDecodingContainerProtocol { internal func decodeURLAsString(forKey key: Self.Key) throws -> URL { let string = try decode(String.self, forKey: key) - let urlCandidate: URL? -#if canImport(FoundationEssentials) - urlCandidate = URL(string: string, encodingInvalidCharacters: false) -#elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + let url: URL? + #if canImport(FoundationEssentials) + url = URL(string: string, encodingInvalidCharacters: false) + #elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS) if #available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) { - urlCandidate = URL(string: string, encodingInvalidCharacters: false) + url = URL(string: string, encodingInvalidCharacters: false) } else { - urlCandidate = URL(string: string) + url = URL(string: string) } -#else - urlCandidate = URL(string: string) -#endif - guard let url = urlCandidate else { + #else + url = URL(string: string) + #endif + guard let url else { throw InconsistencyError( subjectName: key.stringValue, details: "If specified, must be a valid URL", @@ -37,19 +37,19 @@ extension KeyedDecodingContainerProtocol { return nil } - let urlCandidate: URL? -#if canImport(FoundationEssentials) - urlCandidate = URL(string: string, encodingInvalidCharacters: false) -#elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + let url: URL? + #if canImport(FoundationEssentials) + url = URL(string: string, encodingInvalidCharacters: false) + #elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS) if #available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) { - urlCandidate = URL(string: string, encodingInvalidCharacters: false) + url = URL(string: string, encodingInvalidCharacters: false) } else { - urlCandidate = URL(string: string) + url = URL(string: string) } -#else - urlCandidate = URL(string: string) -#endif - guard let url = urlCandidate else { + #else + url = URL(string: string) + #endif + guard let url else { throw InconsistencyError( subjectName: key.stringValue, details: "If specified, must be a valid URL", From a526991312799ba177c9d1307da45a1be40b6233 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 3 Oct 2024 11:54:15 -0500 Subject: [PATCH 081/110] use Swift 5.10 to build docs --- .github/workflows/documentation.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index ab044c416..be25e892d 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -9,10 +9,11 @@ on: jobs: build: runs-on: ubuntu-latest + container: + image: swift:5.10 steps: - uses: actions/checkout@v4 - - name: Build Docs run: | mkdir -p ./gh-pages @@ -23,9 +24,11 @@ jobs: --transform-for-static-hosting \ --hosting-base-path OpenAPIKit \ --target OpenAPIKit - + - name: Install rsync + run: | + apt-get update && apt-get install -y rsync - name: Deploy to GitHub Pages - uses: JamesIves/github-pages-deploy-action@v4.5.0 + uses: JamesIves/github-pages-deploy-action@v4.6.8 with: folder: gh-pages branch: gh-pages From bfbc86f73c3e2a48eb70fcd7a009bf8b454ed85a Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 3 Oct 2024 23:43:32 -0500 Subject: [PATCH 082/110] get rid of check for unsupported versions of Swift --- Tests/OpenAPIKitCoreTests/URLTemplate/URLTemplateTests.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/Tests/OpenAPIKitCoreTests/URLTemplate/URLTemplateTests.swift b/Tests/OpenAPIKitCoreTests/URLTemplate/URLTemplateTests.swift index 1020b074a..79110a6af 100644 --- a/Tests/OpenAPIKitCoreTests/URLTemplate/URLTemplateTests.swift +++ b/Tests/OpenAPIKitCoreTests/URLTemplate/URLTemplateTests.swift @@ -377,7 +377,6 @@ fileprivate struct TemplatedURLWrapper: Codable { } // MARK: - Stack Overflow Regression Test -#if swift(>=5.5) import Dispatch extension URLTemplateTests { @@ -408,4 +407,3 @@ extension URLTemplateTests { } } } -#endif From 3a2e80e7b175608bfa7f06d968b60de0483076e8 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sun, 13 Oct 2024 18:37:43 -0500 Subject: [PATCH 083/110] remove deprecated misspelled builtin validations --- .../Validator/Validation+Builtins.swift | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/Sources/OpenAPIKit/Validator/Validation+Builtins.swift b/Sources/OpenAPIKit/Validator/Validation+Builtins.swift index 55af772fb..1fff043a4 100644 --- a/Sources/OpenAPIKit/Validator/Validation+Builtins.swift +++ b/Sources/OpenAPIKit/Validator/Validation+Builtins.swift @@ -444,16 +444,6 @@ extension Validation { } ) } - - /// Validate that `enum` must not be empty in the document's - /// Server Variable. - /// - /// - Important: This is included in validation by default. - /// - @available(*, deprecated, renamed: "serverVariableEnumIsValid") - public static var serverVarialbeEnumIsValid: Validation { - return serverVariableEnumIsValid - } /// Validate that `default` must exist in the enum values in the document's /// Server Variable, if such values (enum) are defined. @@ -469,16 +459,6 @@ extension Validation { } ) } - - /// Validate that `default` must exist in the enum values in the document's - /// Server Variable, if such values (enum) are defined. - /// - /// - Important: This is included in validation by default. - /// - @available(*, deprecated, renamed: "serverVariableDefaultExistsInEnum") - public static var serverVarialbeDefaultExistsInEnum : Validation { - return serverVariableDefaultExistsInEnum - } } /// Used by both the Path Item parameter check and the From 4141510b9e1f7c9eef079f1e4a72f70db4aa75ab Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Fri, 1 Nov 2024 11:32:55 -0500 Subject: [PATCH 084/110] use newest versions of OAS by default for new document construction --- Sources/OpenAPIKit/Document/Document.swift | 2 +- Sources/OpenAPIKit30/Document/Document.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/OpenAPIKit/Document/Document.swift b/Sources/OpenAPIKit/Document/Document.swift index d00f4d970..ee00946b4 100644 --- a/Sources/OpenAPIKit/Document/Document.swift +++ b/Sources/OpenAPIKit/Document/Document.swift @@ -142,7 +142,7 @@ extension OpenAPI { public var vendorExtensions: [String: AnyCodable] public init( - openAPIVersion: Version = .v3_1_0, + openAPIVersion: Version = .v3_1_1, info: Info, servers: [Server], paths: PathItem.Map, diff --git a/Sources/OpenAPIKit30/Document/Document.swift b/Sources/OpenAPIKit30/Document/Document.swift index 89159e1eb..54f6bc063 100644 --- a/Sources/OpenAPIKit30/Document/Document.swift +++ b/Sources/OpenAPIKit30/Document/Document.swift @@ -135,7 +135,7 @@ extension OpenAPI { public var vendorExtensions: [String: AnyCodable] public init( - openAPIVersion: Version = .v3_0_0, + openAPIVersion: Version = .v3_0_4, info: Info, servers: [Server], paths: PathItem.Map, From 12f6ed77ca9352a0f70b4aec5828337e8711a78f Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Fri, 1 Nov 2024 12:32:11 -0500 Subject: [PATCH 085/110] replace spec 3.0.3 links with 3.0.4 links --- README.md | 4 ++-- Sources/OpenAPIKit30/Callbacks.swift | 2 +- .../Components Object/Components.swift | 2 +- Sources/OpenAPIKit30/Content/Content.swift | 2 +- Sources/OpenAPIKit30/Content/ContentEncoding.swift | 2 +- .../Document/DereferencedDocument.swift | 2 +- Sources/OpenAPIKit30/Document/Document.swift | 6 +++--- Sources/OpenAPIKit30/Document/DocumentInfo.swift | 6 +++--- Sources/OpenAPIKit30/Example.swift | 2 +- Sources/OpenAPIKit30/ExternalDocumentation.swift | 2 +- Sources/OpenAPIKit30/Header/Header.swift | 2 +- Sources/OpenAPIKit30/Operation/Operation.swift | 2 +- Sources/OpenAPIKit30/Parameter/Parameter.swift | 4 ++-- .../OpenAPIKit30/Parameter/ParameterContext.swift | 2 +- .../Parameter/ParameterSchemaContext.swift | 6 +++--- Sources/OpenAPIKit30/Path Item/PathItem.swift | 2 +- Sources/OpenAPIKit30/Request/Request.swift | 2 +- Sources/OpenAPIKit30/Response/Response.swift | 2 +- Sources/OpenAPIKit30/RuntimeExpression.swift | 2 +- .../OpenAPIKit30/Schema Object/JSONSchema.swift | 2 +- .../Schema Object/JSONSchemaContext.swift | 2 +- .../Schema Object/TypesAndFormats.swift | 6 +++--- .../Security/DereferencedSecurityRequirement.swift | 2 +- Sources/OpenAPIKit30/Security/SecurityScheme.swift | 2 +- Sources/OpenAPIKit30/Server.swift | 4 ++-- Sources/OpenAPIKit30/Tag.swift | 2 +- .../Validator/Validation+Builtins.swift | 14 +++++++------- Sources/OpenAPIKit30/XML.swift | 2 +- Sources/OpenAPIKitCore/Shared/CallbackURL.swift | 2 +- Sources/OpenAPIKitCore/Shared/Discriminator.swift | 2 +- Sources/OpenAPIKitCore/Shared/HttpMethod.swift | 2 +- Sources/OpenAPIKitCore/Shared/OAuthFlows.swift | 2 +- Sources/OpenAPIKitCore/Shared/Path.swift | 4 ++-- .../OpenAPIKitCore/Shared/ResponseStatusCode.swift | 2 +- 34 files changed, 52 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index ae17c51a1..a5abf9b53 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ # OpenAPIKit -A library containing Swift types that encode to- and decode from [OpenAPI 3.0.x](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md) and [OpenAPI 3.1.x](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md) Documents and their components. +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://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md) Documents and their components. 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. @@ -165,7 +165,7 @@ If retaining order is important for your use-case, I recommend the [**Yams**](ht 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. ### OpenAPI Document structure -The types used by this library largely mirror the object definitions found in the OpenAPI specification [version 3.1.0](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md) (`OpenAPIKit` module) and [version 3.0.3](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md) (`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. +The types used by this library largely mirror the object definitions found in the OpenAPI specification [version 3.1.0](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md) (`OpenAPIKit` module) and [version 3.0.3](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. #### Document Root 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). diff --git a/Sources/OpenAPIKit30/Callbacks.swift b/Sources/OpenAPIKit30/Callbacks.swift index 3e0341915..ada29dc5f 100644 --- a/Sources/OpenAPIKit30/Callbacks.swift +++ b/Sources/OpenAPIKit30/Callbacks.swift @@ -12,7 +12,7 @@ extension OpenAPI { /// A map from runtime expressions to path items to be used as /// callbacks for the API. /// - /// See [OpenAPI Callback Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#callback-object). + /// See [OpenAPI Callback Object](https://spec.openapis.org/oas/v3.0.4.html#callback-object). /// public typealias Callbacks = OrderedDictionary diff --git a/Sources/OpenAPIKit30/Components Object/Components.swift b/Sources/OpenAPIKit30/Components Object/Components.swift index 352f4841b..cfd709ba2 100644 --- a/Sources/OpenAPIKit30/Components Object/Components.swift +++ b/Sources/OpenAPIKit30/Components Object/Components.swift @@ -11,7 +11,7 @@ import Foundation extension OpenAPI { /// OpenAPI Spec "Components Object". /// - /// See [OpenAPI Components Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#components-object). + /// See [OpenAPI Components Object](https://spec.openapis.org/oas/v3.0.4.html#components-object). /// /// This is a place to put reusable components to /// be referenced from other parts of the spec. diff --git a/Sources/OpenAPIKit30/Content/Content.swift b/Sources/OpenAPIKit30/Content/Content.swift index f2dc83f2e..30a6ad70d 100644 --- a/Sources/OpenAPIKit30/Content/Content.swift +++ b/Sources/OpenAPIKit30/Content/Content.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore extension OpenAPI { /// OpenAPI Spec "Media Type Object" /// - /// See [OpenAPI Media Type Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#media-type-object). + /// See [OpenAPI Media Type Object](https://spec.openapis.org/oas/v3.0.4.html#media-type-object). public struct Content: Equatable, CodableVendorExtendable { public var schema: Either, JSONSchema>? public var example: AnyCodable? diff --git a/Sources/OpenAPIKit30/Content/ContentEncoding.swift b/Sources/OpenAPIKit30/Content/ContentEncoding.swift index 6e61195c4..24317003c 100644 --- a/Sources/OpenAPIKit30/Content/ContentEncoding.swift +++ b/Sources/OpenAPIKit30/Content/ContentEncoding.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore extension OpenAPI.Content { /// OpenAPI Spec "Encoding Object" /// - /// See [OpenAPI Encoding Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#encoding-object). + /// See [OpenAPI Encoding Object](https://spec.openapis.org/oas/v3.0.4.html#encoding-object). public struct Encoding: Equatable { public typealias Style = OpenAPI.Parameter.SchemaContext.Style diff --git a/Sources/OpenAPIKit30/Document/DereferencedDocument.swift b/Sources/OpenAPIKit30/Document/DereferencedDocument.swift index 4a1008f4b..fed984635 100644 --- a/Sources/OpenAPIKit30/Document/DereferencedDocument.swift +++ b/Sources/OpenAPIKit30/Document/DereferencedDocument.swift @@ -100,7 +100,7 @@ extension DereferencedDocument { /// each path, traversed in the order the paths appear in /// the document. /// - /// See [Operation Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#operation-object) in the specifcation. + /// See [Operation Object](https://spec.openapis.org/oas/v3.0.4.html#operation-object) in the specifcation. /// public var allOperationIds: [String] { return paths.values diff --git a/Sources/OpenAPIKit30/Document/Document.swift b/Sources/OpenAPIKit30/Document/Document.swift index 54f6bc063..2223e83bc 100644 --- a/Sources/OpenAPIKit30/Document/Document.swift +++ b/Sources/OpenAPIKit30/Document/Document.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore extension OpenAPI { /// The root of an OpenAPI 3.0 document. /// - /// See [OpenAPI Specification](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md). + /// See [OpenAPI Specification](https://spec.openapis.org/oas/v3.0.4.html). /// /// An OpenAPI Document can say a _lot_ about the API it describes. /// A read-through of the specification is highly recommended because @@ -213,7 +213,7 @@ extension OpenAPI.Document { /// each path, traversed in the order the paths appear in /// the document. /// - /// See [Operation Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#operation-object) in the specifcation. + /// See [Operation Object](https://spec.openapis.org/oas/v3.0.4.html#operation-object) in the specifcation. /// public var allOperationIds: [String] { return paths.values @@ -398,7 +398,7 @@ extension OpenAPI { /// Multiple entries in this dictionary indicate all schemes named are /// required on the same request. /// - /// See [OpenAPI Security Requirement Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#security-requirement-object). + /// See [OpenAPI Security Requirement Object](https://spec.openapis.org/oas/v3.0.4.html#security-requirement-object). public typealias SecurityRequirement = [JSONReference: [String]] } diff --git a/Sources/OpenAPIKit30/Document/DocumentInfo.swift b/Sources/OpenAPIKit30/Document/DocumentInfo.swift index 7eda90a3c..035dda515 100644 --- a/Sources/OpenAPIKit30/Document/DocumentInfo.swift +++ b/Sources/OpenAPIKit30/Document/DocumentInfo.swift @@ -11,7 +11,7 @@ import Foundation extension OpenAPI.Document { /// OpenAPI Spec "Info Object" /// - /// See [OpenAPI Info Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#info-object). + /// See [OpenAPI Info Object](https://spec.openapis.org/oas/v3.0.4.html#info-object). public struct Info: Equatable, CodableVendorExtendable { public let title: String public let description: String? @@ -47,7 +47,7 @@ extension OpenAPI.Document { /// OpenAPI Spec "Contact Object" /// - /// See [OpenAPI Contact Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#contact-object). + /// See [OpenAPI Contact Object](https://spec.openapis.org/oas/v3.0.4.html#contact-object). public struct Contact: Equatable, CodableVendorExtendable { public let name: String? public let url: URL? @@ -75,7 +75,7 @@ extension OpenAPI.Document { /// OpenAPI Spec "License Object" /// - /// See [OpenAPI License Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#license-object). + /// See [OpenAPI License Object](https://spec.openapis.org/oas/v3.0.4.html#license-object). public struct License: Equatable, CodableVendorExtendable { public let name: String public let url: URL? diff --git a/Sources/OpenAPIKit30/Example.swift b/Sources/OpenAPIKit30/Example.swift index d28424238..8012c39cf 100644 --- a/Sources/OpenAPIKit30/Example.swift +++ b/Sources/OpenAPIKit30/Example.swift @@ -11,7 +11,7 @@ import Foundation extension OpenAPI { /// OpenAPI Spec "Example Object" /// - /// See [OpenAPI Example Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#example-object). + /// See [OpenAPI Example Object](https://spec.openapis.org/oas/v3.0.4.html#example-object). public struct Example: Equatable, CodableVendorExtendable { public let summary: String? public let description: String? diff --git a/Sources/OpenAPIKit30/ExternalDocumentation.swift b/Sources/OpenAPIKit30/ExternalDocumentation.swift index 84a90a273..b1d1c4b1a 100644 --- a/Sources/OpenAPIKit30/ExternalDocumentation.swift +++ b/Sources/OpenAPIKit30/ExternalDocumentation.swift @@ -11,7 +11,7 @@ import Foundation extension OpenAPI { /// OpenAPI Spec "External Documentation Object" /// - /// See [OpenAPI External Documentation Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#external-documentation-object). + /// See [OpenAPI External Documentation Object](https://spec.openapis.org/oas/v3.0.4.html#external-documentation-object). public struct ExternalDocumentation: Equatable, CodableVendorExtendable { public var description: String? public var url: URL diff --git a/Sources/OpenAPIKit30/Header/Header.swift b/Sources/OpenAPIKit30/Header/Header.swift index 718ed935b..024306f6a 100644 --- a/Sources/OpenAPIKit30/Header/Header.swift +++ b/Sources/OpenAPIKit30/Header/Header.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore extension OpenAPI { /// OpenAPI Spec "Header Object" /// - /// See [OpenAPI Header Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#header-object). + /// See [OpenAPI Header Object](https://spec.openapis.org/oas/v3.0.4.html#header-object). public struct Header: Equatable, CodableVendorExtendable { public typealias SchemaContext = Parameter.SchemaContext diff --git a/Sources/OpenAPIKit30/Operation/Operation.swift b/Sources/OpenAPIKit30/Operation/Operation.swift index 992f0b3f5..59280467e 100644 --- a/Sources/OpenAPIKit30/Operation/Operation.swift +++ b/Sources/OpenAPIKit30/Operation/Operation.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore extension OpenAPI { /// OpenAPI Spec "Operation Object" /// - /// See [OpenAPI Operation Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#operation-object). + /// See [OpenAPI Operation Object](https://spec.openapis.org/oas/v3.0.4.html#operation-object). public struct Operation: Equatable, CodableVendorExtendable { public var tags: [String]? public var summary: String? diff --git a/Sources/OpenAPIKit30/Parameter/Parameter.swift b/Sources/OpenAPIKit30/Parameter/Parameter.swift index 045cdb13e..f1663c3b4 100644 --- a/Sources/OpenAPIKit30/Parameter/Parameter.swift +++ b/Sources/OpenAPIKit30/Parameter/Parameter.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore extension OpenAPI { /// OpenAPI Spec "Parameter Object" /// - /// See [OpenAPI Parameter Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#parameter-object). + /// See [OpenAPI Parameter Object](https://spec.openapis.org/oas/v3.0.4.html#parameter-object). public struct Parameter: Equatable, CodableVendorExtendable { public var name: String @@ -157,7 +157,7 @@ extension OpenAPI.Parameter { /// containing exactly the things that differentiate /// one parameter from another, per the specification. /// - /// See [Parameter Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#parameter-object). + /// See [Parameter Object](https://spec.openapis.org/oas/v3.0.4.html#parameter-object). internal struct ParameterIdentity: Hashable { let name: String let location: Context.Location diff --git a/Sources/OpenAPIKit30/Parameter/ParameterContext.swift b/Sources/OpenAPIKit30/Parameter/ParameterContext.swift index abb134563..112a08273 100644 --- a/Sources/OpenAPIKit30/Parameter/ParameterContext.swift +++ b/Sources/OpenAPIKit30/Parameter/ParameterContext.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore extension OpenAPI.Parameter { /// OpenAPI Spec "Parameter Object" location-specific configuration. /// - /// See [OpenAPI Parameter Locations](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#parameter-locations). + /// See [OpenAPI Parameter Locations](https://spec.openapis.org/oas/v3.0.4.html#parameter-locations). /// /// Query, Header, and Cookie parameters are /// all optional by default unless you pass diff --git a/Sources/OpenAPIKit30/Parameter/ParameterSchemaContext.swift b/Sources/OpenAPIKit30/Parameter/ParameterSchemaContext.swift index cab0963e8..3b4b2d600 100644 --- a/Sources/OpenAPIKit30/Parameter/ParameterSchemaContext.swift +++ b/Sources/OpenAPIKit30/Parameter/ParameterSchemaContext.swift @@ -10,8 +10,8 @@ import OpenAPIKitCore extension OpenAPI.Parameter { /// OpenAPI Spec "Parameter Object" schema and style configuration. /// - /// See [OpenAPI Parameter Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#parameter-object) - /// and [OpenAPI Style Values](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#style-values). + /// See [OpenAPI Parameter Object](https://spec.openapis.org/oas/v3.0.4.html#parameter-object) + /// and [OpenAPI Style Values](https://spec.openapis.org/oas/v3.0.4.html#style-values). public struct SchemaContext: Equatable { public var style: Style public var explode: Bool @@ -133,7 +133,7 @@ extension OpenAPI.Parameter.SchemaContext.Style { /// per the OpenAPI Specification. /// /// See the `style` fixed field under - /// [OpenAPI Parameter Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#parameter-object). + /// [OpenAPI Parameter Object](https://spec.openapis.org/oas/v3.0.4.html#parameter-object). public static func `default`(for location: OpenAPI.Parameter.Context) -> Self { switch location { case .query: diff --git a/Sources/OpenAPIKit30/Path Item/PathItem.swift b/Sources/OpenAPIKit30/Path Item/PathItem.swift index 5829c6534..f37b1a1cb 100644 --- a/Sources/OpenAPIKit30/Path Item/PathItem.swift +++ b/Sources/OpenAPIKit30/Path Item/PathItem.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore extension OpenAPI { /// OpenAPI Spec "Path Item Object" /// - /// See [OpenAPI Path Item Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#path-item-object). + /// See [OpenAPI Path Item Object](https://spec.openapis.org/oas/v3.0.4.html#path-item-object). /// /// In addition to parameters that apply to all endpoints under the current path, /// this type offers access to each possible endpoint operation under properties diff --git a/Sources/OpenAPIKit30/Request/Request.swift b/Sources/OpenAPIKit30/Request/Request.swift index 0692215a4..847f386f3 100644 --- a/Sources/OpenAPIKit30/Request/Request.swift +++ b/Sources/OpenAPIKit30/Request/Request.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore extension OpenAPI { /// OpenAPI Spec "Request Body Object" /// - /// See [OpenAPI Request Body Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#request-body-object). + /// See [OpenAPI Request Body Object](https://spec.openapis.org/oas/v3.0.4.html#request-body-object). public struct Request: Equatable, CodableVendorExtendable { public var description: String? public var content: Content.Map diff --git a/Sources/OpenAPIKit30/Response/Response.swift b/Sources/OpenAPIKit30/Response/Response.swift index 56e057ddc..c13eed62e 100644 --- a/Sources/OpenAPIKit30/Response/Response.swift +++ b/Sources/OpenAPIKit30/Response/Response.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore extension OpenAPI { /// OpenAPI Spec "Response Object" /// - /// See [OpenAPI Response Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#response-object). + /// See [OpenAPI Response Object](https://spec.openapis.org/oas/v3.0.4.html#response-object). public struct Response: Equatable, CodableVendorExtendable { public var description: String public var headers: Header.Map? diff --git a/Sources/OpenAPIKit30/RuntimeExpression.swift b/Sources/OpenAPIKit30/RuntimeExpression.swift index 960e01ffc..0736f1b3e 100644 --- a/Sources/OpenAPIKit30/RuntimeExpression.swift +++ b/Sources/OpenAPIKit30/RuntimeExpression.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore extension OpenAPI { /// OpenAPI Spec "Runtime Expression" /// - /// See [OpenAPI Runtime Expression[(https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#runtime-expressions). + /// See [OpenAPI Runtime Expression[(https://spec.openapis.org/oas/v3.0.4.html#runtime-expressions). /// public enum RuntimeExpression: RawRepresentable, Equatable { case url diff --git a/Sources/OpenAPIKit30/Schema Object/JSONSchema.swift b/Sources/OpenAPIKit30/Schema Object/JSONSchema.swift index ff3fa8166..57d2c9319 100644 --- a/Sources/OpenAPIKit30/Schema Object/JSONSchema.swift +++ b/Sources/OpenAPIKit30/Schema Object/JSONSchema.swift @@ -9,7 +9,7 @@ import OpenAPIKitCore /// OpenAPI "Schema Object" /// -/// See [OpenAPI Schema Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#schema-object). +/// See [OpenAPI Schema Object](https://spec.openapis.org/oas/v3.0.4.html#schema-object). public struct JSONSchema: JSONSchemaContext, HasWarnings, VendorExtendable { public let warnings: [OpenAPI.Warning] public let value: Schema diff --git a/Sources/OpenAPIKit30/Schema Object/JSONSchemaContext.swift b/Sources/OpenAPIKit30/Schema Object/JSONSchemaContext.swift index 07a12b1f9..8d2ee88b5 100644 --- a/Sources/OpenAPIKit30/Schema Object/JSONSchemaContext.swift +++ b/Sources/OpenAPIKit30/Schema Object/JSONSchemaContext.swift @@ -60,7 +60,7 @@ public protocol JSONSchemaContext { /// be placed on a parent object (one level up from an `allOf`, `anyOf`, /// or `oneOf`) as a way to reduce redundancy. /// - /// See [OpenAPI Discriminator Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#discriminator-object). + /// See [OpenAPI Discriminator Object](https://spec.openapis.org/oas/v3.0.4.html#discriminator-object). var discriminator: OpenAPI.Discriminator? { get } /// Get the external docs, if specified. If unspecified, returns `nil`. diff --git a/Sources/OpenAPIKit30/Schema Object/TypesAndFormats.swift b/Sources/OpenAPIKit30/Schema Object/TypesAndFormats.swift index 1077135ac..464da411a 100644 --- a/Sources/OpenAPIKit30/Schema Object/TypesAndFormats.swift +++ b/Sources/OpenAPIKit30/Schema Object/TypesAndFormats.swift @@ -18,7 +18,7 @@ public protocol SwiftTyped { /// The raw types supported by JSON Schema. /// -/// These are the OpenAPI [data types](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#data-types) +/// These are the OpenAPI [data types](https://spec.openapis.org/oas/v3.0.4.html#data-types) /// and additionally the `object` and `array` /// "compound" data types. /// - boolean @@ -53,7 +53,7 @@ public enum JSONType: String, Codable { /// /// You can also find information on types and /// formats in the OpenAPI Specification's -/// section on [data types](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#data-types). +/// section on [data types](https://spec.openapis.org/oas/v3.0.4.html#data-types). public enum JSONTypeFormat: Equatable { case boolean(BooleanFormat) case object(ObjectFormat) @@ -107,7 +107,7 @@ public enum JSONTypeFormat: Equatable { /// adheres to the [RFC3339](https://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14) /// specification for a "date-time." /// -/// See "formats" under the OpenAPI [data type](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#data-types) +/// See "formats" under the OpenAPI [data type](https://spec.openapis.org/oas/v3.0.4.html#data-types) /// documentation. public protocol OpenAPIFormat: SwiftTyped, Codable, Equatable, RawRepresentable, Validatable where RawValue == String { static var unspecified: Self { get } diff --git a/Sources/OpenAPIKit30/Security/DereferencedSecurityRequirement.swift b/Sources/OpenAPIKit30/Security/DereferencedSecurityRequirement.swift index 7d46b2fe6..20fd0ead3 100644 --- a/Sources/OpenAPIKit30/Security/DereferencedSecurityRequirement.swift +++ b/Sources/OpenAPIKit30/Security/DereferencedSecurityRequirement.swift @@ -63,7 +63,7 @@ public struct DereferencedSecurityRequirement: Equatable { /// not require a specified scope. For other security scheme types, /// the array MUST be empty. /// - /// See [Security Requirement Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#security-requirement-object) for more. + /// See [Security Requirement Object](https://spec.openapis.org/oas/v3.0.4.html#security-requirement-object) for more. public let requiredScopes: [String] } } diff --git a/Sources/OpenAPIKit30/Security/SecurityScheme.swift b/Sources/OpenAPIKit30/Security/SecurityScheme.swift index 22171d097..d8fb46413 100644 --- a/Sources/OpenAPIKit30/Security/SecurityScheme.swift +++ b/Sources/OpenAPIKit30/Security/SecurityScheme.swift @@ -11,7 +11,7 @@ import Foundation extension OpenAPI { /// OpenAPI Spec "Security Scheme Object" /// - /// See [OpenAPI Security Scheme Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#security-scheme-object). + /// See [OpenAPI Security Scheme Object](https://spec.openapis.org/oas/v3.0.4.html#security-scheme-object). public struct SecurityScheme: Equatable, CodableVendorExtendable { public var type: SecurityType public var description: String? diff --git a/Sources/OpenAPIKit30/Server.swift b/Sources/OpenAPIKit30/Server.swift index d44a280bf..71404a904 100644 --- a/Sources/OpenAPIKit30/Server.swift +++ b/Sources/OpenAPIKit30/Server.swift @@ -11,7 +11,7 @@ import Foundation extension OpenAPI { /// OpenAPI Spec "Server Object" /// - /// See [OpenAPI Server Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#server-object). + /// See [OpenAPI Server Object](https://spec.openapis.org/oas/v3.0.4.html#server-object). /// public struct Server: Equatable, CodableVendorExtendable { /// OpenAPI Server URLs can have variable placeholders in them. @@ -63,7 +63,7 @@ extension OpenAPI { extension OpenAPI.Server { /// OpenAPI Spec "Server Variable Object" /// - /// See [OpenAPI Server Variable Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#server-variable-object). + /// See [OpenAPI Server Variable Object](https://spec.openapis.org/oas/v3.0.4.html#server-variable-object). /// public struct Variable: Equatable, CodableVendorExtendable { public var `enum`: [String] diff --git a/Sources/OpenAPIKit30/Tag.swift b/Sources/OpenAPIKit30/Tag.swift index 5669c977c..d88450810 100644 --- a/Sources/OpenAPIKit30/Tag.swift +++ b/Sources/OpenAPIKit30/Tag.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore extension OpenAPI { /// OpenAPI Spec "Tag Object" /// - /// See [OpenAPI Tag Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#tag-object). + /// See [OpenAPI Tag Object](https://spec.openapis.org/oas/v3.0.4.html#tag-object). public struct Tag: Equatable, CodableVendorExtendable { public let name: String public let description: String? diff --git a/Sources/OpenAPIKit30/Validator/Validation+Builtins.swift b/Sources/OpenAPIKit30/Validator/Validation+Builtins.swift index 414d26d24..67151ae52 100644 --- a/Sources/OpenAPIKit30/Validator/Validation+Builtins.swift +++ b/Sources/OpenAPIKit30/Validator/Validation+Builtins.swift @@ -14,7 +14,7 @@ extension Validation { /// `PathItem.Map`. /// /// The OpenAPI Specifcation does not require that the document - /// contain any paths for [security reasons](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#security-filtering) + /// contain any paths for [security reasons](https://spec.openapis.org/oas/v3.0.4.html#security-filtering) /// but documentation that is public in nature might only ever have /// an empty `PathItem.Map` in error. /// @@ -30,7 +30,7 @@ extension Validation { /// one operation. /// /// The OpenAPI Specifcation does not require that path items - /// contain any operations for [security reasons](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#security-filtering) + /// contain any operations for [security reasons](https://spec.openapis.org/oas/v3.0.4.html#security-filtering) /// but documentation that is public in nature might only ever have /// a `PathItem` with no operations in error. /// @@ -160,7 +160,7 @@ extension Validation { /// one response. /// /// The OpenAPI Specifcation requires that Responses Objects - /// contain [at least one response](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#responses-object). + /// contain [at least one response](https://spec.openapis.org/oas/v3.0.4.html#responses-object). /// The specification recommends that if there is only one response then /// it be a successful response. /// @@ -178,7 +178,7 @@ extension Validation { /// Validate that the OpenAPI Document's `Tags` all have unique names. /// /// The OpenAPI Specifcation requires that tag names on the Document - /// [are unique](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#openapi-object). + /// [are unique](https://spec.openapis.org/oas/v3.0.4.html#openapi-object). /// /// - Important: This is included in validation by default. public static var documentTagNamesAreUnique: Validation { @@ -198,7 +198,7 @@ extension Validation { /// A Path Item Parameter's identity is defined as the pairing of its `name` and /// `location`. /// - /// The OpenAPI Specification requires that these parameters [are unique](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#path-item-object). + /// The OpenAPI Specification requires that these parameters [are unique](https://spec.openapis.org/oas/v3.0.4.html#path-item-object). /// /// - Important: This is included in validation by default. /// @@ -216,7 +216,7 @@ extension Validation { /// An Operation's Parameter's identity is defined as the pairing of its `name` and /// `location`. /// - /// The OpenAPI Specification requires that these parameters [are unique](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#operation-object). + /// The OpenAPI Specification requires that these parameters [are unique](https://spec.openapis.org/oas/v3.0.4.html#operation-object). /// /// - Important: This is included in validation by default. /// @@ -230,7 +230,7 @@ extension Validation { /// Validate that all OpenAPI Operation Ids are unique across the whole Document. /// - /// The OpenAPI Specification requires that Operation Ids [are unique](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#operation-object). + /// The OpenAPI Specification requires that Operation Ids [are unique](https://spec.openapis.org/oas/v3.0.4.html#operation-object). /// /// - Important: This is included in validation by default. /// diff --git a/Sources/OpenAPIKit30/XML.swift b/Sources/OpenAPIKit30/XML.swift index 6ee059b49..f7488c309 100644 --- a/Sources/OpenAPIKit30/XML.swift +++ b/Sources/OpenAPIKit30/XML.swift @@ -11,7 +11,7 @@ import Foundation extension OpenAPI { /// OpenAPI Spec "XML Object" /// - /// See [OpenAPI XML Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#xml-object). + /// See [OpenAPI XML Object](https://spec.openapis.org/oas/v3.0.4.html#xml-object). public struct XML: Equatable { public let name: String? public let namespace: URL? diff --git a/Sources/OpenAPIKitCore/Shared/CallbackURL.swift b/Sources/OpenAPIKitCore/Shared/CallbackURL.swift index 5e2d6295d..2da7e90ff 100644 --- a/Sources/OpenAPIKitCore/Shared/CallbackURL.swift +++ b/Sources/OpenAPIKitCore/Shared/CallbackURL.swift @@ -11,7 +11,7 @@ extension Shared { /// A URL template where the placeholders are OpenAPI **Runtime Expressions** instead /// of named variables. /// - /// See [OpenAPI Callback Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#callback-object) and [OpenAPI Runtime Expression](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#runtime-expressions) for more. + /// See [OpenAPI Callback Object](https://spec.openapis.org/oas/v3.0.4.html#callback-object) and [OpenAPI Runtime Expression](https://spec.openapis.org/oas/v3.0.4.html#runtime-expressions) for more. /// public struct CallbackURL: Hashable, RawRepresentable { public let template: URLTemplate diff --git a/Sources/OpenAPIKitCore/Shared/Discriminator.swift b/Sources/OpenAPIKitCore/Shared/Discriminator.swift index 78af32ca3..e2cfe5949 100644 --- a/Sources/OpenAPIKitCore/Shared/Discriminator.swift +++ b/Sources/OpenAPIKitCore/Shared/Discriminator.swift @@ -8,7 +8,7 @@ extension Shared { /// OpenAPI Spec "Disciminator Object" /// - /// See [OpenAPI Discriminator Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#discriminator-object). + /// See [OpenAPI Discriminator Object](https://spec.openapis.org/oas/v3.0.4.html#discriminator-object). public struct Discriminator: Equatable { public let propertyName: String public let mapping: OrderedDictionary? diff --git a/Sources/OpenAPIKitCore/Shared/HttpMethod.swift b/Sources/OpenAPIKitCore/Shared/HttpMethod.swift index 316a15ddd..b5062bcbc 100644 --- a/Sources/OpenAPIKitCore/Shared/HttpMethod.swift +++ b/Sources/OpenAPIKitCore/Shared/HttpMethod.swift @@ -9,7 +9,7 @@ extension Shared { /// Represents the HTTP methods supported by the /// OpenAPI Specification. /// - /// See [OpenAPI Path Item Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#path-item-object) because the supported + /// See [OpenAPI Path Item Object](https://spec.openapis.org/oas/v3.0.4.html#path-item-object) because the supported /// HTTP methods are enumerated as properties on that /// object. public enum HttpMethod: String, CaseIterable { diff --git a/Sources/OpenAPIKitCore/Shared/OAuthFlows.swift b/Sources/OpenAPIKitCore/Shared/OAuthFlows.swift index 1a8253abe..bb977421d 100644 --- a/Sources/OpenAPIKitCore/Shared/OAuthFlows.swift +++ b/Sources/OpenAPIKitCore/Shared/OAuthFlows.swift @@ -10,7 +10,7 @@ import Foundation extension Shared { /// OpenAPI Spec "Oauth Flows Object" /// - /// See [OpenAPI Oauth Flows Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#oauth-flows-object). + /// See [OpenAPI Oauth Flows Object](https://spec.openapis.org/oas/v3.0.4.html#oauth-flows-object). public struct OAuthFlows: Equatable { public let implicit: Implicit? public let password: Password? diff --git a/Sources/OpenAPIKitCore/Shared/Path.swift b/Sources/OpenAPIKitCore/Shared/Path.swift index 4389173e0..0a505b114 100644 --- a/Sources/OpenAPIKitCore/Shared/Path.swift +++ b/Sources/OpenAPIKitCore/Shared/Path.swift @@ -8,8 +8,8 @@ extension Shared { /// OpenAPI Spec "Paths Object" path field pattern support. /// - /// See [OpenAPI Paths Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#paths-object) - /// and [OpenAPI Patterned Fields](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#patterned-fields). + /// See [OpenAPI Paths Object](https://spec.openapis.org/oas/v3.0.4.html#paths-object) + /// and [OpenAPI Patterned Fields](https://spec.openapis.org/oas/v3.0.4.html#patterned-fields). public struct Path: RawRepresentable, Equatable, Hashable { public let components: [String] public let trailingSlash: Bool diff --git a/Sources/OpenAPIKitCore/Shared/ResponseStatusCode.swift b/Sources/OpenAPIKitCore/Shared/ResponseStatusCode.swift index 9c69aeb9e..875097ab2 100644 --- a/Sources/OpenAPIKitCore/Shared/ResponseStatusCode.swift +++ b/Sources/OpenAPIKitCore/Shared/ResponseStatusCode.swift @@ -8,7 +8,7 @@ extension Shared { /// An HTTP Status code or status code range. /// - /// OpenAPI supports one of the following as a key in the [Responses Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#responses-object): + /// OpenAPI supports one of the following as a key in the [Responses Object](https://spec.openapis.org/oas/v3.0.4.html#responses-object): /// - A `default` entry. /// - A specific status code. /// - A status code range. From 88162c7bf845c64c30ab554785d85bde73c22f88 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Fri, 1 Nov 2024 12:34:02 -0500 Subject: [PATCH 086/110] replace spec 3.1.0 links with 3.1.1 links --- README.md | 6 +++--- Sources/OpenAPIKit/Callbacks.swift | 2 +- .../OpenAPIKit/Components Object/Components.swift | 2 +- Sources/OpenAPIKit/Content/Content.swift | 2 +- Sources/OpenAPIKit/Content/ContentEncoding.swift | 2 +- .../OpenAPIKit/Document/DereferencedDocument.swift | 2 +- Sources/OpenAPIKit/Document/Document.swift | 8 ++++---- Sources/OpenAPIKit/Document/DocumentInfo.swift | 6 +++--- Sources/OpenAPIKit/Example.swift | 2 +- Sources/OpenAPIKit/ExternalDocumentation.swift | 2 +- Sources/OpenAPIKit/Header/Header.swift | 2 +- Sources/OpenAPIKit/Link.swift | 4 ++-- Sources/OpenAPIKit/Operation/Operation.swift | 2 +- Sources/OpenAPIKit/Parameter/Parameter.swift | 4 ++-- Sources/OpenAPIKit/Parameter/ParameterContext.swift | 2 +- .../Parameter/ParameterSchemaContext.swift | 6 +++--- Sources/OpenAPIKit/Path Item/PathItem.swift | 2 +- Sources/OpenAPIKit/Request/Request.swift | 2 +- Sources/OpenAPIKit/Response/Response.swift | 2 +- Sources/OpenAPIKit/RuntimeExpression.swift | 2 +- Sources/OpenAPIKit/Schema Object/JSONSchema.swift | 2 +- .../OpenAPIKit/Schema Object/JSONSchemaContext.swift | 2 +- .../OpenAPIKit/Schema Object/TypesAndFormats.swift | 6 +++--- .../Security/DereferencedSecurityRequirement.swift | 2 +- Sources/OpenAPIKit/Security/SecurityScheme.swift | 2 +- Sources/OpenAPIKit/Server.swift | 4 ++-- Sources/OpenAPIKit/Tag.swift | 2 +- .../OpenAPIKit/Validator/Validation+Builtins.swift | 12 ++++++------ Sources/OpenAPIKit/XML.swift | 2 +- Sources/OpenAPIKit30/Link.swift | 4 ++-- documentation/specification_coverage.md | 2 +- 31 files changed, 51 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index a5abf9b53..90597669d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ # OpenAPIKit -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://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md) Documents and their components. +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. 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. @@ -165,7 +165,7 @@ If retaining order is important for your use-case, I recommend the [**Yams**](ht 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. ### OpenAPI Document structure -The types used by this library largely mirror the object definitions found in the OpenAPI specification [version 3.1.0](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md) (`OpenAPIKit` module) and [version 3.0.3](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. +The types used by this library largely mirror the object definitions found in the OpenAPI specification [version 3.1.0](https://spec.openapis.org/oas/v3.1.1.html) (`OpenAPIKit` module) and [version 3.0.3](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. #### Document Root 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). @@ -289,7 +289,7 @@ let document = OpenAPI.Document( ``` #### Specification Extensions -Many OpenAPIKit types support [Specification Extensions](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#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. +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. 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. diff --git a/Sources/OpenAPIKit/Callbacks.swift b/Sources/OpenAPIKit/Callbacks.swift index 7a671bdeb..2d0019ac4 100644 --- a/Sources/OpenAPIKit/Callbacks.swift +++ b/Sources/OpenAPIKit/Callbacks.swift @@ -13,7 +13,7 @@ extension OpenAPI { /// A map from runtime expressions to path items to be used as /// callbacks for the API. The OpenAPI Spec "Callback Object." /// - /// See [OpenAPI Callback Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#callback-object). + /// See [OpenAPI Callback Object](https://spec.openapis.org/oas/v3.1.1.html#callback-object). /// public typealias Callbacks = OrderedDictionary, PathItem>> diff --git a/Sources/OpenAPIKit/Components Object/Components.swift b/Sources/OpenAPIKit/Components Object/Components.swift index cc1996b68..73ca5d480 100644 --- a/Sources/OpenAPIKit/Components Object/Components.swift +++ b/Sources/OpenAPIKit/Components Object/Components.swift @@ -11,7 +11,7 @@ import Foundation extension OpenAPI { /// OpenAPI Spec "Components Object". /// - /// See [OpenAPI Components Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#components-object). + /// See [OpenAPI Components Object](https://spec.openapis.org/oas/v3.1.1.html#components-object). /// /// This is a place to put reusable components to /// be referenced from other parts of the spec. diff --git a/Sources/OpenAPIKit/Content/Content.swift b/Sources/OpenAPIKit/Content/Content.swift index e782c9818..2bdcd9321 100644 --- a/Sources/OpenAPIKit/Content/Content.swift +++ b/Sources/OpenAPIKit/Content/Content.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore extension OpenAPI { /// OpenAPI Spec "Media Type Object" /// - /// See [OpenAPI Media Type Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#media-type-object). + /// See [OpenAPI Media Type Object](https://spec.openapis.org/oas/v3.1.1.html#media-type-object). public struct Content: Equatable, CodableVendorExtendable { public var schema: Either, JSONSchema>? public var example: AnyCodable? diff --git a/Sources/OpenAPIKit/Content/ContentEncoding.swift b/Sources/OpenAPIKit/Content/ContentEncoding.swift index 65d55b754..7910b81a6 100644 --- a/Sources/OpenAPIKit/Content/ContentEncoding.swift +++ b/Sources/OpenAPIKit/Content/ContentEncoding.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore extension OpenAPI.Content { /// OpenAPI Spec "Encoding Object" /// - /// See [OpenAPI Encoding Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#encoding-object). + /// See [OpenAPI Encoding Object](https://spec.openapis.org/oas/v3.1.1.html#encoding-object). public struct Encoding: Equatable { public typealias Style = OpenAPI.Parameter.SchemaContext.Style diff --git a/Sources/OpenAPIKit/Document/DereferencedDocument.swift b/Sources/OpenAPIKit/Document/DereferencedDocument.swift index 7906923fd..eac8afc80 100644 --- a/Sources/OpenAPIKit/Document/DereferencedDocument.swift +++ b/Sources/OpenAPIKit/Document/DereferencedDocument.swift @@ -105,7 +105,7 @@ extension DereferencedDocument { /// each path, traversed in the order the paths appear in /// the document. /// - /// See [Operation Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#operation-object) in the specifcation. + /// See [Operation Object](https://spec.openapis.org/oas/v3.1.1.html#operation-object) in the specifcation. /// public var allOperationIds: [String] { return paths.values diff --git a/Sources/OpenAPIKit/Document/Document.swift b/Sources/OpenAPIKit/Document/Document.swift index ee00946b4..13977f93a 100644 --- a/Sources/OpenAPIKit/Document/Document.swift +++ b/Sources/OpenAPIKit/Document/Document.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore extension OpenAPI { /// The root of an OpenAPI 3.1 document. /// - /// See [OpenAPI Specification](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md). + /// See [OpenAPI Specification](https://spec.openapis.org/oas/v3.1.1.html). /// /// An OpenAPI Document can say a _lot_ about the API it describes. /// A read-through of the specification is highly recommended because @@ -100,7 +100,7 @@ extension OpenAPI { /// /// Closely related to the callbacks feature, this section describes requests initiated other than by an API call, for example by an out of band registration. /// The key name is a unique string to refer to each webhook, while the (optionally referenced) Path Item Object describes a request that may be initiated by the API provider and the expected responses - /// See [OpenAPI Webhook Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#fixed-fields) + /// See [OpenAPI Webhook Object](https://spec.openapis.org/oas/v3.1.1.html#fixed-fields) public var webhooks: OrderedDictionary, OpenAPI.PathItem>> /// A declaration of which security mechanisms can be used across the API. @@ -229,7 +229,7 @@ extension OpenAPI.Document { /// each path, traversed in the order the paths appear in /// the document. /// - /// See [Operation Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#operation-object) in the specifcation. + /// See [Operation Object](https://spec.openapis.org/oas/v3.1.1.html#operation-object) in the specifcation. /// public var allOperationIds: [String] { return paths.values @@ -413,7 +413,7 @@ extension OpenAPI { /// Multiple entries in this dictionary indicate all schemes named are /// required on the same request. /// - /// See [OpenAPI Security Requirement Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#security-requirement-object). + /// See [OpenAPI Security Requirement Object](https://spec.openapis.org/oas/v3.1.1.html#security-requirement-object). public typealias SecurityRequirement = [JSONReference: [String]] } diff --git a/Sources/OpenAPIKit/Document/DocumentInfo.swift b/Sources/OpenAPIKit/Document/DocumentInfo.swift index ddbf3a3ed..60a25431e 100644 --- a/Sources/OpenAPIKit/Document/DocumentInfo.swift +++ b/Sources/OpenAPIKit/Document/DocumentInfo.swift @@ -11,7 +11,7 @@ import Foundation extension OpenAPI.Document { /// OpenAPI Spec "Info Object" /// - /// See [OpenAPI Info Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#info-object). + /// See [OpenAPI Info Object](https://spec.openapis.org/oas/v3.1.1.html#info-object). public struct Info: Equatable, CodableVendorExtendable { public let title: String public let summary: String? @@ -50,7 +50,7 @@ extension OpenAPI.Document { /// OpenAPI Spec "Contact Object" /// - /// See [OpenAPI Contact Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#contact-object). + /// See [OpenAPI Contact Object](https://spec.openapis.org/oas/v3.1.1.html#contact-object). public struct Contact: Equatable, CodableVendorExtendable { public let name: String? public let url: URL? @@ -78,7 +78,7 @@ extension OpenAPI.Document { /// OpenAPI Spec "License Object" /// - /// See [OpenAPI License Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#license-object). + /// See [OpenAPI License Object](https://spec.openapis.org/oas/v3.1.1.html#license-object). public struct License: Equatable, CodableVendorExtendable { public let name: String public let identifier: Identifier? diff --git a/Sources/OpenAPIKit/Example.swift b/Sources/OpenAPIKit/Example.swift index e9c904f7c..cbd6c4863 100644 --- a/Sources/OpenAPIKit/Example.swift +++ b/Sources/OpenAPIKit/Example.swift @@ -11,7 +11,7 @@ import Foundation extension OpenAPI { /// OpenAPI Spec "Example Object" /// - /// See [OpenAPI Example Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#example-object). + /// See [OpenAPI Example Object](https://spec.openapis.org/oas/v3.1.1.html#example-object). public struct Example: Equatable, CodableVendorExtendable { public let summary: String? public let description: String? diff --git a/Sources/OpenAPIKit/ExternalDocumentation.swift b/Sources/OpenAPIKit/ExternalDocumentation.swift index 6fb8a7761..c46562cc3 100644 --- a/Sources/OpenAPIKit/ExternalDocumentation.swift +++ b/Sources/OpenAPIKit/ExternalDocumentation.swift @@ -11,7 +11,7 @@ import Foundation extension OpenAPI { /// OpenAPI Spec "External Documentation Object" /// - /// See [OpenAPI External Documentation Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#external-documentation-object). + /// See [OpenAPI External Documentation Object](https://spec.openapis.org/oas/v3.1.1.html#external-documentation-object). public struct ExternalDocumentation: Equatable, CodableVendorExtendable { public var description: String? public var url: URL diff --git a/Sources/OpenAPIKit/Header/Header.swift b/Sources/OpenAPIKit/Header/Header.swift index 309901af9..e5c36724a 100644 --- a/Sources/OpenAPIKit/Header/Header.swift +++ b/Sources/OpenAPIKit/Header/Header.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore extension OpenAPI { /// OpenAPI Spec "Header Object" /// - /// See [OpenAPI Header Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#header-object). + /// See [OpenAPI Header Object](https://spec.openapis.org/oas/v3.1.1.html#header-object). public struct Header: Equatable, CodableVendorExtendable { public typealias SchemaContext = Parameter.SchemaContext diff --git a/Sources/OpenAPIKit/Link.swift b/Sources/OpenAPIKit/Link.swift index 39a1da918..b67cc7574 100644 --- a/Sources/OpenAPIKit/Link.swift +++ b/Sources/OpenAPIKit/Link.swift @@ -15,7 +15,7 @@ import Foundation extension OpenAPI { /// OpenAPI Spec "Link Object" /// - /// See [OpenAPI Link Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#link-object). + /// See [OpenAPI Link Object](https://spec.openapis.org/oas/v3.1.1.html#link-object). public struct Link: Equatable, CodableVendorExtendable { /// The **OpenAPI**` `operationRef` or `operationId` field, depending on whether /// a `URL` of a remote or local Operation Object or a `operationId` (String) of an @@ -24,7 +24,7 @@ extension OpenAPI { /// A map from parameter names to either runtime expressions that evaluate to values or /// constant values (`AnyCodable`). /// - /// See the docuemntation for the [OpenAPI Link Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#link-object) for more details. + /// See the docuemntation for the [OpenAPI Link Object](https://spec.openapis.org/oas/v3.1.1.html#link-object) for more details. /// /// Empty dictionaries will be omitted from encoding. public var parameters: OrderedDictionary> diff --git a/Sources/OpenAPIKit/Operation/Operation.swift b/Sources/OpenAPIKit/Operation/Operation.swift index ab620e00e..763722983 100644 --- a/Sources/OpenAPIKit/Operation/Operation.swift +++ b/Sources/OpenAPIKit/Operation/Operation.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore extension OpenAPI { /// OpenAPI Spec "Operation Object" /// - /// See [OpenAPI Operation Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#operation-object). + /// See [OpenAPI Operation Object](https://spec.openapis.org/oas/v3.1.1.html#operation-object). public struct Operation: Equatable, CodableVendorExtendable { public var tags: [String]? public var summary: String? diff --git a/Sources/OpenAPIKit/Parameter/Parameter.swift b/Sources/OpenAPIKit/Parameter/Parameter.swift index 9c4cf5611..0805bb807 100644 --- a/Sources/OpenAPIKit/Parameter/Parameter.swift +++ b/Sources/OpenAPIKit/Parameter/Parameter.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore extension OpenAPI { /// OpenAPI Spec "Parameter Object" /// - /// See [OpenAPI Parameter Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#parameter-object). + /// See [OpenAPI Parameter Object](https://spec.openapis.org/oas/v3.1.1.html#parameter-object). public struct Parameter: Equatable, CodableVendorExtendable { public var name: String @@ -162,7 +162,7 @@ extension OpenAPI.Parameter { /// containing exactly the things that differentiate /// one parameter from another, per the specification. /// - /// See [Parameter Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#parameter-object). + /// See [Parameter Object](https://spec.openapis.org/oas/v3.1.1.html#parameter-object). internal struct ParameterIdentity: Hashable { let name: String let location: Context.Location diff --git a/Sources/OpenAPIKit/Parameter/ParameterContext.swift b/Sources/OpenAPIKit/Parameter/ParameterContext.swift index 27ae9c2c0..5f6c5de3a 100644 --- a/Sources/OpenAPIKit/Parameter/ParameterContext.swift +++ b/Sources/OpenAPIKit/Parameter/ParameterContext.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore extension OpenAPI.Parameter { /// OpenAPI Spec "Parameter Object" location-specific configuration. /// - /// See [OpenAPI Parameter Locations](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#parameter-locations). + /// See [OpenAPI Parameter Locations](https://spec.openapis.org/oas/v3.1.1.html#parameter-locations). /// /// Query, Header, and Cookie parameters are /// all optional by default unless you pass diff --git a/Sources/OpenAPIKit/Parameter/ParameterSchemaContext.swift b/Sources/OpenAPIKit/Parameter/ParameterSchemaContext.swift index cd65f89c5..98f5e11e9 100644 --- a/Sources/OpenAPIKit/Parameter/ParameterSchemaContext.swift +++ b/Sources/OpenAPIKit/Parameter/ParameterSchemaContext.swift @@ -10,8 +10,8 @@ import OpenAPIKitCore extension OpenAPI.Parameter { /// OpenAPI Spec "Parameter Object" schema and style configuration. /// - /// See [OpenAPI Parameter Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#parameter-object) - /// and [OpenAPI Style Values](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#style-values). + /// See [OpenAPI Parameter Object](https://spec.openapis.org/oas/v3.1.1.html#parameter-object) + /// and [OpenAPI Style Values](https://spec.openapis.org/oas/v3.1.1.html#style-values). public struct SchemaContext: Equatable { public var style: Style public var explode: Bool @@ -132,7 +132,7 @@ extension OpenAPI.Parameter.SchemaContext.Style { /// per the OpenAPI Specification. /// /// See the `style` fixed field under - /// [OpenAPI Parameter Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#parameter-object). + /// [OpenAPI Parameter Object](https://spec.openapis.org/oas/v3.1.1.html#parameter-object). public static func `default`(for location: OpenAPI.Parameter.Context) -> Self { switch location { case .query: diff --git a/Sources/OpenAPIKit/Path Item/PathItem.swift b/Sources/OpenAPIKit/Path Item/PathItem.swift index 71f213c35..ec4003806 100644 --- a/Sources/OpenAPIKit/Path Item/PathItem.swift +++ b/Sources/OpenAPIKit/Path Item/PathItem.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore extension OpenAPI { /// OpenAPI Spec "Path Item Object" /// - /// See [OpenAPI Path Item Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#path-item-object). + /// See [OpenAPI Path Item Object](https://spec.openapis.org/oas/v3.1.1.html#path-item-object). /// /// In addition to parameters that apply to all endpoints under the current path, /// this type offers access to each possible endpoint operation under properties diff --git a/Sources/OpenAPIKit/Request/Request.swift b/Sources/OpenAPIKit/Request/Request.swift index 9d6df8d4e..346e47a43 100644 --- a/Sources/OpenAPIKit/Request/Request.swift +++ b/Sources/OpenAPIKit/Request/Request.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore extension OpenAPI { /// OpenAPI Spec "Request Body Object" /// - /// See [OpenAPI Request Body Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#request-body-object). + /// See [OpenAPI Request Body Object](https://spec.openapis.org/oas/v3.1.1.html#request-body-object). public struct Request: Equatable, CodableVendorExtendable { public var description: String? public var content: Content.Map diff --git a/Sources/OpenAPIKit/Response/Response.swift b/Sources/OpenAPIKit/Response/Response.swift index 351d487bf..205072cc6 100644 --- a/Sources/OpenAPIKit/Response/Response.swift +++ b/Sources/OpenAPIKit/Response/Response.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore extension OpenAPI { /// OpenAPI Spec "Response Object" /// - /// See [OpenAPI Response Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#response-object). + /// See [OpenAPI Response Object](https://spec.openapis.org/oas/v3.1.1.html#response-object). public struct Response: Equatable, CodableVendorExtendable { public var description: String public var headers: Header.Map? diff --git a/Sources/OpenAPIKit/RuntimeExpression.swift b/Sources/OpenAPIKit/RuntimeExpression.swift index ac168bbef..3256ada18 100644 --- a/Sources/OpenAPIKit/RuntimeExpression.swift +++ b/Sources/OpenAPIKit/RuntimeExpression.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore extension OpenAPI { /// OpenAPI Spec "Runtime Expression" /// - /// See [OpenAPI Runtime Expression[(https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#runtime-expressions). + /// See [OpenAPI Runtime Expression[(https://spec.openapis.org/oas/v3.1.1.html#runtime-expressions). /// public enum RuntimeExpression: RawRepresentable, Equatable { case url diff --git a/Sources/OpenAPIKit/Schema Object/JSONSchema.swift b/Sources/OpenAPIKit/Schema Object/JSONSchema.swift index 599dfcbd1..261561ae1 100644 --- a/Sources/OpenAPIKit/Schema Object/JSONSchema.swift +++ b/Sources/OpenAPIKit/Schema Object/JSONSchema.swift @@ -9,7 +9,7 @@ import OpenAPIKitCore /// OpenAPI "Schema Object" /// -/// See [OpenAPI Schema Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#schema-object). +/// See [OpenAPI Schema Object](https://spec.openapis.org/oas/v3.1.1.html#schema-object). public struct JSONSchema: JSONSchemaContext, HasWarnings { public let warnings: [OpenAPI.Warning] diff --git a/Sources/OpenAPIKit/Schema Object/JSONSchemaContext.swift b/Sources/OpenAPIKit/Schema Object/JSONSchemaContext.swift index e425e4ddc..37857c149 100644 --- a/Sources/OpenAPIKit/Schema Object/JSONSchemaContext.swift +++ b/Sources/OpenAPIKit/Schema Object/JSONSchemaContext.swift @@ -60,7 +60,7 @@ public protocol JSONSchemaContext { /// be placed on a parent object (one level up from an `allOf`, `anyOf`, /// or `oneOf`) as a way to reduce redundancy. /// - /// See [OpenAPI Discriminator Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#discriminator-object). + /// See [OpenAPI Discriminator Object](https://spec.openapis.org/oas/v3.1.1.html#discriminator-object). var discriminator: OpenAPI.Discriminator? { get } /// Get the external docs, if specified. If unspecified, returns `nil`. diff --git a/Sources/OpenAPIKit/Schema Object/TypesAndFormats.swift b/Sources/OpenAPIKit/Schema Object/TypesAndFormats.swift index edb593d96..718b30951 100644 --- a/Sources/OpenAPIKit/Schema Object/TypesAndFormats.swift +++ b/Sources/OpenAPIKit/Schema Object/TypesAndFormats.swift @@ -18,7 +18,7 @@ public protocol SwiftTyped { /// The raw types supported by JSON Schema. /// -/// These are the OpenAPI [data types](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#data-types) +/// These are the OpenAPI [data types](https://spec.openapis.org/oas/v3.1.1.html#data-types) /// and additionally the `object` and `array` /// "compound" data types. /// - boolean @@ -54,7 +54,7 @@ public enum JSONType: String, Codable { /// /// You can also find information on types and /// formats in the OpenAPI Specification's -/// section on [data types](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#data-types). +/// section on [data types](https://spec.openapis.org/oas/v3.1.1.html#data-types). public enum JSONTypeFormat: Equatable { case null case boolean(BooleanFormat) @@ -113,7 +113,7 @@ public enum JSONTypeFormat: Equatable { /// adheres to the [RFC3339](https://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14) /// specification for a "date-time." /// -/// See "formats" under the OpenAPI [data type](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#data-types) +/// See "formats" under the OpenAPI [data type](https://spec.openapis.org/oas/v3.1.1.html#data-types) /// documentation. public protocol OpenAPIFormat: SwiftTyped, Codable, Equatable, RawRepresentable, Validatable where RawValue == String { static var unspecified: Self { get } diff --git a/Sources/OpenAPIKit/Security/DereferencedSecurityRequirement.swift b/Sources/OpenAPIKit/Security/DereferencedSecurityRequirement.swift index 1645310a9..1894eae61 100644 --- a/Sources/OpenAPIKit/Security/DereferencedSecurityRequirement.swift +++ b/Sources/OpenAPIKit/Security/DereferencedSecurityRequirement.swift @@ -63,7 +63,7 @@ public struct DereferencedSecurityRequirement: Equatable { /// not require a specified scope. For other security scheme types, /// the array MUST be empty. /// - /// See [Security Requirement Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#security-requirement-object) for more. + /// See [Security Requirement Object](https://spec.openapis.org/oas/v3.1.1.html#security-requirement-object) for more. public let requiredScopes: [String] } } diff --git a/Sources/OpenAPIKit/Security/SecurityScheme.swift b/Sources/OpenAPIKit/Security/SecurityScheme.swift index 8e487bba7..3de41d5dc 100644 --- a/Sources/OpenAPIKit/Security/SecurityScheme.swift +++ b/Sources/OpenAPIKit/Security/SecurityScheme.swift @@ -11,7 +11,7 @@ import Foundation extension OpenAPI { /// OpenAPI Spec "Security Scheme Object" /// - /// See [OpenAPI Security Scheme Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#security-scheme-object). + /// See [OpenAPI Security Scheme Object](https://spec.openapis.org/oas/v3.1.1.html#security-scheme-object). public struct SecurityScheme: Equatable, CodableVendorExtendable { public var type: SecurityType public var description: String? diff --git a/Sources/OpenAPIKit/Server.swift b/Sources/OpenAPIKit/Server.swift index d21a1b5e7..b96c1c15c 100644 --- a/Sources/OpenAPIKit/Server.swift +++ b/Sources/OpenAPIKit/Server.swift @@ -11,7 +11,7 @@ import Foundation extension OpenAPI { /// OpenAPI Spec "Server Object" /// - /// See [OpenAPI Server Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#server-object). + /// See [OpenAPI Server Object](https://spec.openapis.org/oas/v3.1.1.html#server-object). /// public struct Server: Equatable, CodableVendorExtendable { /// OpenAPI Server URLs can have variable placeholders in them. @@ -63,7 +63,7 @@ extension OpenAPI { extension OpenAPI.Server { /// OpenAPI Spec "Server Variable Object" /// - /// See [OpenAPI Server Variable Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#server-variable-object). + /// See [OpenAPI Server Variable Object](https://spec.openapis.org/oas/v3.1.1.html#server-variable-object). /// public struct Variable: Equatable, CodableVendorExtendable { public var `enum`: [String]? diff --git a/Sources/OpenAPIKit/Tag.swift b/Sources/OpenAPIKit/Tag.swift index 73ff53a64..8023549aa 100644 --- a/Sources/OpenAPIKit/Tag.swift +++ b/Sources/OpenAPIKit/Tag.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore extension OpenAPI { /// OpenAPI Spec "Tag Object" /// - /// See [OpenAPI Tag Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#tag-object). + /// See [OpenAPI Tag Object](https://spec.openapis.org/oas/v3.1.1.html#tag-object). public struct Tag: Equatable, CodableVendorExtendable { public let name: String public let description: String? diff --git a/Sources/OpenAPIKit/Validator/Validation+Builtins.swift b/Sources/OpenAPIKit/Validator/Validation+Builtins.swift index 1fff043a4..bc3354790 100644 --- a/Sources/OpenAPIKit/Validator/Validation+Builtins.swift +++ b/Sources/OpenAPIKit/Validator/Validation+Builtins.swift @@ -14,7 +14,7 @@ extension Validation { /// `PathItem.Map`. /// /// The OpenAPI Specification does not require that the document - /// contain any paths for [security reasons](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#security-filtering) + /// contain any paths for [security reasons](https://spec.openapis.org/oas/v3.1.1.html#security-filtering) /// or even because it only contains webhooks, but authors may still /// want to protect against an empty `PathItem.Map` in some cases. /// @@ -30,7 +30,7 @@ extension Validation { /// one operation. /// /// The OpenAPI Specification does not require that path items - /// contain any operations for [security reasons](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#security-filtering) + /// contain any operations for [security reasons](https://spec.openapis.org/oas/v3.1.1.html#security-filtering) /// but documentation that is public in nature might only ever have /// a `PathItem` with no operations in error. /// @@ -183,7 +183,7 @@ extension Validation { /// Validate that the OpenAPI Document's `Tags` all have unique names. /// /// The OpenAPI Specification requires that tag names on the Document - /// [are unique](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#openapi-object). + /// [are unique](https://spec.openapis.org/oas/v3.1.1.html#openapi-object). /// /// - Important: This is included in validation by default. public static var documentTagNamesAreUnique: Validation { @@ -203,7 +203,7 @@ extension Validation { /// A Path Item Parameter's identity is defined as the pairing of its `name` and /// `location`. /// - /// The OpenAPI Specification requires that these parameters [are unique](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#path-item-object). + /// The OpenAPI Specification requires that these parameters [are unique](https://spec.openapis.org/oas/v3.1.1.html#path-item-object). /// /// - Important: This is included in validation by default. /// @@ -221,7 +221,7 @@ extension Validation { /// An Operation's Parameter's identity is defined as the pairing of its `name` and /// `location`. /// - /// The OpenAPI Specification requires that these parameters [are unique](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#operation-object). + /// The OpenAPI Specification requires that these parameters [are unique](https://spec.openapis.org/oas/v3.1.1.html#operation-object). /// /// - Important: This is included in validation by default. /// @@ -235,7 +235,7 @@ extension Validation { /// Validate that all OpenAPI Operation Ids are unique across the whole Document. /// - /// The OpenAPI Specification requires that Operation Ids [are unique](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#operation-object). + /// The OpenAPI Specification requires that Operation Ids [are unique](https://spec.openapis.org/oas/v3.1.1.html#operation-object). /// /// - Important: This validation does not assert that all path references are valid and found in the /// components for the document. It skips over missing path items. diff --git a/Sources/OpenAPIKit/XML.swift b/Sources/OpenAPIKit/XML.swift index 4793be230..b49d30b0b 100644 --- a/Sources/OpenAPIKit/XML.swift +++ b/Sources/OpenAPIKit/XML.swift @@ -11,7 +11,7 @@ import Foundation extension OpenAPI { /// OpenAPI Spec "XML Object" /// - /// See [OpenAPI XML Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#xml-object). + /// See [OpenAPI XML Object](https://spec.openapis.org/oas/v3.1.1.html#xml-object). public struct XML: Equatable { public let name: String? public let namespace: URL? diff --git a/Sources/OpenAPIKit30/Link.swift b/Sources/OpenAPIKit30/Link.swift index c416e97b7..0cfe551c1 100644 --- a/Sources/OpenAPIKit30/Link.swift +++ b/Sources/OpenAPIKit30/Link.swift @@ -15,7 +15,7 @@ import Foundation extension OpenAPI { /// OpenAPI Spec "Link Object" /// - /// See [OpenAPI Link Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#link-object). + /// See [OpenAPI Link Object](https://spec.openapis.org/oas/v3.1.1.html#link-object). public struct Link: Equatable, CodableVendorExtendable { /// The **OpenAPI**` `operationRef` or `operationId` field, depending on whether /// a `URL` of a remote or local Operation Object or a `operationId` (String) of an @@ -24,7 +24,7 @@ extension OpenAPI { /// A map from parameter names to either runtime expressions that evaluate to values or /// constant values (`AnyCodable`). /// - /// See the docuemntation for the [OpenAPI Link Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#link-object) for more details. + /// See the docuemntation for the [OpenAPI Link Object](https://spec.openapis.org/oas/v3.1.1.html#link-object) for more details. /// /// Empty dictionaries will be omitted from encoding. public var parameters: OrderedDictionary> diff --git a/documentation/specification_coverage.md b/documentation/specification_coverage.md index 097596a5e..e390c7f65 100644 --- a/documentation/specification_coverage.md +++ b/documentation/specification_coverage.md @@ -1,6 +1,6 @@ ## Specification Coverage -The list below is organized like the [OpenAPI Specification 3.1.x](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md) reference. Types that have OpenAPIKit representations are checked off. Types that have different names in OpenAPIKit than they do in the specification have their OpenAPIKit names in parenthesis. +The list below is organized like the [OpenAPI Specification 3.1.x](https://spec.openapis.org/oas/v3.1.1.html) reference. Types that have OpenAPIKit representations are checked off. Types that have different names in OpenAPIKit than they do in the specification have their OpenAPIKit names in parenthesis. For more information on the OpenAPIKit types, see the [full type documentation](https://github.com/mattpolzin/OpenAPIKit/wiki). From 76f3d2f44fe5b988e559e4eab77d1e61bdd9bd1b Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Fri, 1 Nov 2024 12:37:03 -0500 Subject: [PATCH 087/110] default to 3.1.1 when converting. pick up slightly outdated error message change. straggling README changes --- README.md | 4 ++-- Sources/OpenAPIKit/Schema Object/JSONSchemaContext.swift | 2 +- Sources/OpenAPIKitCompat/Compat30To31.swift | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 90597669d..e279b0d23 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,7 @@ let newDoc: OpenAPIKit.OpenAPI.Document oldDoc = try? JSONDecoder().decode(OpenAPI.Document.self, from: someFileData) -newDoc = oldDoc?.convert(to: .v3_1_0) ?? +newDoc = oldDoc?.convert(to: .v3_1_1) ?? (try! JSONDecoder().decode(OpenAPI.Document.self, from: someFileData)) // ^ Here we simply fall-back to 3.1.x if loading as 3.0.x failed. You could do a more // graceful job of this by determining up front which version to attempt to load or by @@ -165,7 +165,7 @@ If retaining order is important for your use-case, I recommend the [**Yams**](ht 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. ### OpenAPI Document structure -The types used by this library largely mirror the object definitions found in the OpenAPI specification [version 3.1.0](https://spec.openapis.org/oas/v3.1.1.html) (`OpenAPIKit` module) and [version 3.0.3](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. +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. #### Document Root 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). diff --git a/Sources/OpenAPIKit/Schema Object/JSONSchemaContext.swift b/Sources/OpenAPIKit/Schema Object/JSONSchemaContext.swift index 37857c149..1c89eacfa 100644 --- a/Sources/OpenAPIKit/Schema Object/JSONSchemaContext.swift +++ b/Sources/OpenAPIKit/Schema Object/JSONSchemaContext.swift @@ -1035,7 +1035,7 @@ extension JSONSchema.CoreContext: Decodable { .underlyingError( InconsistencyError( subjectName: "OpenAPI Schema", - details: "Found 'nullable' property. This property is not supported by OpenAPI v3.1.0. OpenAPIKit has translated it into 'type: [\"null\", ...]'.", + details: "Found 'nullable' property. This property is not supported by OpenAPI v3.1.x. OpenAPIKit has translated it into 'type: [\"null\", ...]'.", codingPath: container.codingPath ) ) diff --git a/Sources/OpenAPIKitCompat/Compat30To31.swift b/Sources/OpenAPIKitCompat/Compat30To31.swift index e7358a667..88192c352 100644 --- a/Sources/OpenAPIKitCompat/Compat30To31.swift +++ b/Sources/OpenAPIKitCompat/Compat30To31.swift @@ -20,7 +20,7 @@ private protocol To31 { } extension OpenAPIKit30.OpenAPI.Document { - fileprivate func to31(version: OpenAPIKit.OpenAPI.Document.Version = .v3_1_0) -> OpenAPIKit.OpenAPI.Document { + fileprivate func to31(version: OpenAPIKit.OpenAPI.Document.Version = .v3_1_1) -> OpenAPIKit.OpenAPI.Document { OpenAPIKit.OpenAPI.Document( openAPIVersion: version, info: info.to31(), From e66baba2755a9be9a3145c252f2ff5dc3dff95b7 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Fri, 1 Nov 2024 12:55:34 -0500 Subject: [PATCH 088/110] update document tests --- .../Document/DocumentTests.swift | 30 +++++++------- .../Document/DocumentTests.swift | 40 +++++++++---------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/Tests/OpenAPIKit30Tests/Document/DocumentTests.swift b/Tests/OpenAPIKit30Tests/Document/DocumentTests.swift index 0d6d7b23d..ccf4d2c55 100644 --- a/Tests/OpenAPIKit30Tests/Document/DocumentTests.swift +++ b/Tests/OpenAPIKit30Tests/Document/DocumentTests.swift @@ -364,7 +364,7 @@ final class DocumentTests: XCTestCase { let docData = """ { - "openapi": "3.0.0", + "openapi": "3.0.4", "info": { "title": "test", "version": "1.0" @@ -409,7 +409,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.0.0", + "openapi" : "3.0.4", "paths" : { } @@ -426,7 +426,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.0.0", + "openapi" : "3.0.4", "paths" : { } @@ -517,7 +517,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.0.0", + "openapi" : "3.0.4", "paths" : { }, @@ -539,7 +539,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.0.0", + "openapi" : "3.0.4", "paths" : { }, @@ -580,7 +580,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.0.0", + "openapi" : "3.0.4", "paths" : { "\\/test" : { "summary" : "hi" @@ -599,7 +599,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.0.0", + "openapi" : "3.0.4", "paths" : { "\\/test" : { "summary" : "hi" @@ -649,7 +649,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.0.0", + "openapi" : "3.0.4", "paths" : { }, @@ -682,7 +682,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.0.0", + "openapi" : "3.0.4", "paths" : { }, @@ -729,7 +729,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.0.0", + "openapi" : "3.0.4", "paths" : { }, @@ -751,7 +751,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.0.0", + "openapi" : "3.0.4", "paths" : { }, @@ -797,7 +797,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.0.0", + "openapi" : "3.0.4", "paths" : { } @@ -817,7 +817,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.0.0", + "openapi" : "3.0.4", "paths" : { } @@ -859,7 +859,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.0.0", + "openapi" : "3.0.4", "paths" : { }, @@ -883,7 +883,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.0.0", + "openapi" : "3.0.4", "paths" : { }, diff --git a/Tests/OpenAPIKitTests/Document/DocumentTests.swift b/Tests/OpenAPIKitTests/Document/DocumentTests.swift index c68703e8d..f66e4ecb0 100644 --- a/Tests/OpenAPIKitTests/Document/DocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/DocumentTests.swift @@ -356,7 +356,7 @@ final class DocumentTests: XCTestCase { let docData = """ { - "openapi": "3.1.0", + "openapi": "3.1.1", "info": { "title": "test", "version": "1.0" @@ -401,7 +401,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.1.0" + "openapi" : "3.1.1" } """ ) @@ -415,7 +415,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.1.0", + "openapi" : "3.1.1", "paths" : { } @@ -503,7 +503,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.1.0", + "openapi" : "3.1.1", "servers" : [ { "url" : "http:\\/\\/google.com" @@ -522,7 +522,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.1.0", + "openapi" : "3.1.1", "paths" : { }, @@ -563,7 +563,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.1.0", + "openapi" : "3.1.1", "paths" : { "\\/test" : { "summary" : "hi" @@ -582,7 +582,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.1.0", + "openapi" : "3.1.1", "paths" : { "\\/test" : { "summary" : "hi" @@ -632,7 +632,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.1.0", + "openapi" : "3.1.1", "security" : [ { "security" : [ @@ -662,7 +662,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.1.0", + "openapi" : "3.1.1", "paths" : { }, @@ -709,7 +709,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.1.0", + "openapi" : "3.1.1", "tags" : [ { "name" : "hi" @@ -728,7 +728,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.1.0", + "openapi" : "3.1.1", "paths" : { }, @@ -774,7 +774,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.1.0" + "openapi" : "3.1.1" } """ ) @@ -791,7 +791,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.1.0", + "openapi" : "3.1.1", "paths" : { } @@ -833,7 +833,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.1.0", + "openapi" : "3.1.1", "x-specialFeature" : [ "hello", "world" @@ -854,7 +854,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.1.0", + "openapi" : "3.1.1", "paths" : { }, @@ -894,7 +894,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.1.0", + "openapi" : "3.1.1", "paths" : { }, @@ -946,7 +946,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.1.0", + "openapi" : "3.1.1", "webhooks" : { "webhook-test" : { "delete" : { @@ -1014,7 +1014,7 @@ extension DocumentTests { "title": "API", "version": "1.0" }, - "openapi": "3.1.0", + "openapi": "3.1.1", "paths": { }, "webhooks": { @@ -1084,7 +1084,7 @@ extension DocumentTests { "title" : "API", "version" : "1.0" }, - "openapi" : "3.1.0", + "openapi" : "3.1.1", "webhooks" : { "webhook-test" : { "delete" : { @@ -1130,7 +1130,7 @@ extension DocumentTests { "title": "API", "version": "1.0" }, - "openapi": "3.1.0", + "openapi": "3.1.1", "webhooks": { "webhook-test": { "delete": { From b73751ae9020f0f04aa052ccaca263d1fedec88c Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Fri, 1 Nov 2024 13:06:18 -0500 Subject: [PATCH 089/110] update error test --- Tests/OpenAPIKitErrorReportingTests/SchemaErrorTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/OpenAPIKitErrorReportingTests/SchemaErrorTests.swift b/Tests/OpenAPIKitErrorReportingTests/SchemaErrorTests.swift index 99074e0dc..cc344e148 100644 --- a/Tests/OpenAPIKitErrorReportingTests/SchemaErrorTests.swift +++ b/Tests/OpenAPIKitErrorReportingTests/SchemaErrorTests.swift @@ -76,7 +76,7 @@ final class SchemaErrorTests: XCTestCase { XCTAssertEqual(openAPIError.localizedDescription, """ - Inconsistency encountered when parsing `OpenAPI Schema`: Found 'nullable' property. This property is not supported by OpenAPI v3.1.0. OpenAPIKit has translated it into 'type: ["null", ...]'.. at path: .paths['/hello/world'].get.responses.200.content['application/json'].schema + Inconsistency encountered when parsing `OpenAPI Schema`: Found 'nullable' property. This property is not supported by OpenAPI v3.1.x. OpenAPIKit has translated it into 'type: ["null", ...]'.. at path: .paths['/hello/world'].get.responses.200.content['application/json'].schema """) XCTAssertEqual(openAPIError.codingPath.map { $0.stringValue }, [ "paths", From 97d026e990504784665b37ca208700966b3ef1a8 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Fri, 1 Nov 2024 17:14:40 -0500 Subject: [PATCH 090/110] 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 091/110] 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) + } } } From 709f6fe5b40e6ae277719d4609373053fc16a292 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Fri, 1 Nov 2024 17:44:49 -0500 Subject: [PATCH 092/110] enable strict concurrency in CI --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6000f3e84..9752fc667 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -29,7 +29,7 @@ jobs: - name: Checkout code uses: actions/checkout@v4 - name: Run tests - run: swift test + run: swift test -Xswiftc -strict-concurrency=complete osx: strategy: fail-fast: false @@ -46,4 +46,4 @@ jobs: - name: Checkout code uses: actions/checkout@v4 - name: Run tests - run: swift test + run: swift test -Xswiftc -strict-concurrency=complete From 3e295c7d56fb7715cac899b4f676f20a55b29db2 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Mon, 27 Jan 2025 19:04:08 -0600 Subject: [PATCH 093/110] Add Sendable to relevant types --- .../OpenAPIKit/CodableVendorExtendable.swift | 2 +- .../Components Object/Components.swift | 2 +- Sources/OpenAPIKit/Content/Content.swift | 2 +- .../OpenAPIKit/Content/ContentEncoding.swift | 2 +- .../DocumentDecodingError.swift | 2 +- .../OperationDecodingError.swift | 2 +- .../PathDecodingError.swift | 2 +- .../ResponseDecodingError.swift | 2 +- Sources/OpenAPIKit/Example.swift | 2 +- .../OpenAPIKit/ExternalDocumentation.swift | 2 +- Sources/OpenAPIKit/ExternalLoader.swift | 2 +- Sources/OpenAPIKit/Header/Header.swift | 2 +- Sources/OpenAPIKit/JSONReference.swift | 8 ++--- Sources/OpenAPIKit/Link.swift | 2 +- Sources/OpenAPIKit/Operation/Operation.swift | 2 +- Sources/OpenAPIKit/Parameter/Parameter.swift | 2 +- .../Parameter/ParameterContext.swift | 2 +- .../Parameter/ParameterSchemaContext.swift | 2 +- Sources/OpenAPIKit/Path Item/PathItem.swift | 2 +- Sources/OpenAPIKit/Request/Request.swift | 2 +- Sources/OpenAPIKit/Response/Response.swift | 2 +- Sources/OpenAPIKit/RuntimeExpression.swift | 4 +-- .../DereferencedJSONSchema.swift | 6 ++-- .../Schema Object/JSONSchema+Combining.swift | 2 +- .../OpenAPIKit/Schema Object/JSONSchema.swift | 6 ++-- .../Schema Object/JSONSchemaContext.swift | 18 +++++----- .../Schema Object/TypesAndFormats.swift | 6 ++-- .../OpenAPIKit/Security/SecurityScheme.swift | 4 +-- Sources/OpenAPIKit/Server.swift | 4 +-- .../Array+ExternallyDereferenceable.swift | 2 +- ...Dictionary+ExternallyDereferenceable.swift | 2 +- ...Dictionary+ExternallyDereferenceable.swift | 2 +- .../CodableVendorExtendable.swift | 2 +- .../Components Object/Components.swift | 2 +- Sources/OpenAPIKit30/Content/Content.swift | 2 +- .../Content/ContentEncoding.swift | 2 +- .../DocumentDecodingError.swift | 2 +- .../OperationDecodingError.swift | 2 +- .../PathDecodingError.swift | 2 +- .../ResponseDecodingError.swift | 2 +- Sources/OpenAPIKit30/Example.swift | 2 +- .../OpenAPIKit30/ExternalDocumentation.swift | 2 +- Sources/OpenAPIKit30/ExternalLoader.swift | 2 +- Sources/OpenAPIKit30/Header/Header.swift | 2 +- Sources/OpenAPIKit30/JSONReference.swift | 6 ++-- Sources/OpenAPIKit30/Link.swift | 2 +- .../OpenAPIKit30/Operation/Operation.swift | 2 +- .../OpenAPIKit30/Parameter/Parameter.swift | 2 +- .../Parameter/ParameterContext.swift | 2 +- .../Parameter/ParameterSchemaContext.swift | 2 +- Sources/OpenAPIKit30/Path Item/PathItem.swift | 2 +- Sources/OpenAPIKit30/Request/Request.swift | 2 +- Sources/OpenAPIKit30/Response/Response.swift | 2 +- Sources/OpenAPIKit30/RuntimeExpression.swift | 4 +-- .../DereferencedJSONSchema.swift | 4 +-- .../Schema Object/JSONSchema+Combining.swift | 2 +- .../Schema Object/JSONSchema.swift | 6 ++-- .../Schema Object/JSONSchemaContext.swift | 18 +++++----- .../Schema Object/TypesAndFormats.swift | 6 ++-- .../Security/SecurityScheme.swift | 4 +-- Sources/OpenAPIKit30/Server.swift | 4 +-- .../Array+ExternallyDereferenceable.swift | 2 +- ...Dictionary+ExternallyDereferenceable.swift | 2 +- ...Dictionary+ExternallyDereferenceable.swift | 2 +- .../AnyCodable/AnyCodable.swift | 33 +++++++++---------- Sources/OpenAPIKitCore/Either/Either.swift | 2 ++ .../OpenAPIDecodingErrors.swift | 2 +- .../RequestDecodingError.swift | 4 +-- .../OrderedDictionary/OrderedDictionary.swift | 4 +++ .../OpenAPIKitCore/Shared/CallbackURL.swift | 2 +- .../OpenAPIKitCore/Shared/ComponentKey.swift | 2 +- .../OpenAPIKitCore/Shared/ContentType.swift | 2 +- .../OpenAPIKitCore/Shared/Discriminator.swift | 2 +- .../OpenAPIKitCore/Shared/HttpMethod.swift | 2 +- .../Shared/JSONSchemaPermissions.swift | 2 +- .../Shared/JSONTypeFormat.swift | 12 +++---- .../OpenAPIKitCore/Shared/OAuthFlows.swift | 12 +++---- .../Shared/ParameterSchemaContextStyle.swift | 2 +- Sources/OpenAPIKitCore/Shared/Path.swift | 2 +- .../Shared/ResponseStatusCode.swift | 6 ++-- .../Shared/SecurityScheme.swift | 2 +- .../URLTemplate/URLTemplate.swift | 4 +-- 82 files changed, 153 insertions(+), 148 deletions(-) diff --git a/Sources/OpenAPIKit/CodableVendorExtendable.swift b/Sources/OpenAPIKit/CodableVendorExtendable.swift index 5f1d6401f..dc3b0b67b 100644 --- a/Sources/OpenAPIKit/CodableVendorExtendable.swift +++ b/Sources/OpenAPIKit/CodableVendorExtendable.swift @@ -98,7 +98,7 @@ extension CodableVendorExtendable { throw VendorExtensionDecodingError.selfIsArrayNotDict } - guard let decodedAny = decoded as? [String: Any] else { + guard let decodedAny = decoded as? [String: any Sendable] else { throw VendorExtensionDecodingError.foundNonStringKeys } diff --git a/Sources/OpenAPIKit/Components Object/Components.swift b/Sources/OpenAPIKit/Components Object/Components.swift index 187aa9c63..de8556e83 100644 --- a/Sources/OpenAPIKit/Components Object/Components.swift +++ b/Sources/OpenAPIKit/Components Object/Components.swift @@ -15,7 +15,7 @@ extension OpenAPI { /// /// This is a place to put reusable components to /// be referenced from other parts of the spec. - public struct Components: Equatable, CodableVendorExtendable { + public struct Components: Equatable, CodableVendorExtendable, Sendable { public var schemas: ComponentDictionary public var responses: ComponentDictionary diff --git a/Sources/OpenAPIKit/Content/Content.swift b/Sources/OpenAPIKit/Content/Content.swift index ef03fdb0e..928c90ac2 100644 --- a/Sources/OpenAPIKit/Content/Content.swift +++ b/Sources/OpenAPIKit/Content/Content.swift @@ -11,7 +11,7 @@ extension OpenAPI { /// OpenAPI Spec "Media Type Object" /// /// See [OpenAPI Media Type Object](https://spec.openapis.org/oas/v3.1.1.html#media-type-object). - public struct Content: Equatable, CodableVendorExtendable { + public struct Content: Equatable, CodableVendorExtendable, Sendable { public var schema: Either, JSONSchema>? public var example: AnyCodable? public var examples: Example.Map? diff --git a/Sources/OpenAPIKit/Content/ContentEncoding.swift b/Sources/OpenAPIKit/Content/ContentEncoding.swift index 7910b81a6..a446fab7d 100644 --- a/Sources/OpenAPIKit/Content/ContentEncoding.swift +++ b/Sources/OpenAPIKit/Content/ContentEncoding.swift @@ -11,7 +11,7 @@ extension OpenAPI.Content { /// OpenAPI Spec "Encoding Object" /// /// See [OpenAPI Encoding Object](https://spec.openapis.org/oas/v3.1.1.html#encoding-object). - public struct Encoding: Equatable { + public struct Encoding: Equatable, Sendable { public typealias Style = OpenAPI.Parameter.SchemaContext.Style public let contentTypes: [OpenAPI.ContentType] diff --git a/Sources/OpenAPIKit/Encoding and Decoding Errors/DocumentDecodingError.swift b/Sources/OpenAPIKit/Encoding and Decoding Errors/DocumentDecodingError.swift index a585f7d93..6786b6034 100644 --- a/Sources/OpenAPIKit/Encoding and Decoding Errors/DocumentDecodingError.swift +++ b/Sources/OpenAPIKit/Encoding and Decoding Errors/DocumentDecodingError.swift @@ -12,7 +12,7 @@ extension OpenAPI.Error.Decoding { public let context: Context public let codingPath: [CodingKey] - public enum Context { + public enum Context: Sendable { case path(Path) case inconsistency(InconsistencyError) case other(Swift.DecodingError) diff --git a/Sources/OpenAPIKit/Encoding and Decoding Errors/OperationDecodingError.swift b/Sources/OpenAPIKit/Encoding and Decoding Errors/OperationDecodingError.swift index 354b16730..c62a3720a 100644 --- a/Sources/OpenAPIKit/Encoding and Decoding Errors/OperationDecodingError.swift +++ b/Sources/OpenAPIKit/Encoding and Decoding Errors/OperationDecodingError.swift @@ -13,7 +13,7 @@ extension OpenAPI.Error.Decoding { public let context: Context internal let relativeCodingPath: [CodingKey] - public enum Context { + public enum Context: Sendable { case request(Request) case response(Response) case inconsistency(InconsistencyError) diff --git a/Sources/OpenAPIKit/Encoding and Decoding Errors/PathDecodingError.swift b/Sources/OpenAPIKit/Encoding and Decoding Errors/PathDecodingError.swift index 4b6e09b50..445604621 100644 --- a/Sources/OpenAPIKit/Encoding and Decoding Errors/PathDecodingError.swift +++ b/Sources/OpenAPIKit/Encoding and Decoding Errors/PathDecodingError.swift @@ -13,7 +13,7 @@ extension OpenAPI.Error.Decoding { public let context: Context internal let relativeCodingPath: [CodingKey] - public enum Context { + public enum Context: Sendable { case endpoint(Operation) case inconsistency(InconsistencyError) case other(Swift.DecodingError) diff --git a/Sources/OpenAPIKit/Encoding and Decoding Errors/ResponseDecodingError.swift b/Sources/OpenAPIKit/Encoding and Decoding Errors/ResponseDecodingError.swift index 7d918a3f6..0089935d5 100644 --- a/Sources/OpenAPIKit/Encoding and Decoding Errors/ResponseDecodingError.swift +++ b/Sources/OpenAPIKit/Encoding and Decoding Errors/ResponseDecodingError.swift @@ -13,7 +13,7 @@ extension OpenAPI.Error.Decoding { public let context: Context internal let relativeCodingPath: [CodingKey] - public enum Context { + public enum Context: Sendable { case inconsistency(InconsistencyError) case other(Swift.DecodingError) case neither(EitherDecodeNoTypesMatchedError) diff --git a/Sources/OpenAPIKit/Example.swift b/Sources/OpenAPIKit/Example.swift index b506c6553..d55528f93 100644 --- a/Sources/OpenAPIKit/Example.swift +++ b/Sources/OpenAPIKit/Example.swift @@ -12,7 +12,7 @@ extension OpenAPI { /// OpenAPI Spec "Example Object" /// /// See [OpenAPI Example Object](https://spec.openapis.org/oas/v3.1.1.html#example-object). - public struct Example: Equatable, CodableVendorExtendable { + public struct Example: Equatable, CodableVendorExtendable, Sendable { public let summary: String? public let description: String? diff --git a/Sources/OpenAPIKit/ExternalDocumentation.swift b/Sources/OpenAPIKit/ExternalDocumentation.swift index e9d1edf85..0582b39d9 100644 --- a/Sources/OpenAPIKit/ExternalDocumentation.swift +++ b/Sources/OpenAPIKit/ExternalDocumentation.swift @@ -12,7 +12,7 @@ extension OpenAPI { /// OpenAPI Spec "External Documentation Object" /// /// See [OpenAPI External Documentation Object](https://spec.openapis.org/oas/v3.1.1.html#external-documentation-object). - public struct ExternalDocumentation: Equatable, CodableVendorExtendable { + public struct ExternalDocumentation: Equatable, CodableVendorExtendable, Sendable { public var description: String? public var url: URL diff --git a/Sources/OpenAPIKit/ExternalLoader.swift b/Sources/OpenAPIKit/ExternalLoader.swift index 28cfa8d39..ba67073de 100644 --- a/Sources/OpenAPIKit/ExternalLoader.swift +++ b/Sources/OpenAPIKit/ExternalLoader.swift @@ -11,7 +11,7 @@ import Foundation /// An `ExternalLoader` enables `OpenAPIKit` to load external references /// without knowing the details of what decoder is being used or how new internal /// references should be named. -public protocol ExternalLoader { +public protocol ExternalLoader where Message: Sendable { /// This can be anything that an implementor of this protocol wants to pass back from /// the `load()` function and have available after all external loading has been done. /// diff --git a/Sources/OpenAPIKit/Header/Header.swift b/Sources/OpenAPIKit/Header/Header.swift index b8e9ab98a..98765de53 100644 --- a/Sources/OpenAPIKit/Header/Header.swift +++ b/Sources/OpenAPIKit/Header/Header.swift @@ -11,7 +11,7 @@ extension OpenAPI { /// OpenAPI Spec "Header Object" /// /// See [OpenAPI Header Object](https://spec.openapis.org/oas/v3.1.1.html#header-object). - public struct Header: Equatable, CodableVendorExtendable { + public struct Header: Equatable, CodableVendorExtendable, Sendable { public typealias SchemaContext = Parameter.SchemaContext public let description: String? diff --git a/Sources/OpenAPIKit/JSONReference.swift b/Sources/OpenAPIKit/JSONReference.swift index 3a5d24e07..3177fb2d3 100644 --- a/Sources/OpenAPIKit/JSONReference.swift +++ b/Sources/OpenAPIKit/JSONReference.swift @@ -39,7 +39,7 @@ import Foundation /// Components Object will be validated when you call `validate()` on an /// `OpenAPI.Document`. /// -public enum JSONReference: Equatable, Hashable, _OpenAPIReference { +public enum JSONReference: Equatable, Hashable, _OpenAPIReference, Sendable { /// The reference is internal to the file. case `internal`(InternalReference) /// The reference refers to another file. @@ -124,7 +124,7 @@ public enum JSONReference: Equatabl /// `JSONReference`. /// /// This reference must start with "#". - public enum InternalReference: LosslessStringConvertible, RawRepresentable, Equatable, Hashable { + public enum InternalReference: LosslessStringConvertible, RawRepresentable, Equatable, Hashable, Sendable { /// The reference refers to a component (i.e. `#/components/...`). case component(name: String) /// The reference refers to some path outside the Components Object. @@ -202,7 +202,7 @@ public enum JSONReference: Equatabl /// /// This path does _not_ start with "#". It starts with a forward slash. By contrast, an /// `InternalReference` starts with "#" and is followed by the start of a `Path`. - public struct Path: ExpressibleByArrayLiteral, ExpressibleByStringLiteral, LosslessStringConvertible, RawRepresentable, Equatable, Hashable { + public struct Path: ExpressibleByArrayLiteral, ExpressibleByStringLiteral, LosslessStringConvertible, RawRepresentable, Equatable, Hashable, Sendable { /// The Path's components. In the `rawValue`, these components are joined /// with forward slashes '/' per the JSON Reference specification. @@ -312,7 +312,7 @@ extension OpenAPI { /// Per the specification, these summary and description overrides are irrelevant /// if the referenced component does not support the given attribute. @dynamicMemberLookup - public struct Reference: Equatable, Hashable, _OpenAPIReference { + public struct Reference: Equatable, Hashable, _OpenAPIReference, Sendable { public let jsonReference: JSONReference public let summary: String? public let description: String? diff --git a/Sources/OpenAPIKit/Link.swift b/Sources/OpenAPIKit/Link.swift index 20c3c0a28..61ceb08b6 100644 --- a/Sources/OpenAPIKit/Link.swift +++ b/Sources/OpenAPIKit/Link.swift @@ -16,7 +16,7 @@ extension OpenAPI { /// OpenAPI Spec "Link Object" /// /// See [OpenAPI Link Object](https://spec.openapis.org/oas/v3.1.1.html#link-object). - public struct Link: Equatable, CodableVendorExtendable { + public struct Link: Equatable, CodableVendorExtendable, Sendable { /// The **OpenAPI**` `operationRef` or `operationId` field, depending on whether /// a `URL` of a remote or local Operation Object or a `operationId` (String) of an /// operation defined in the same document is given. diff --git a/Sources/OpenAPIKit/Operation/Operation.swift b/Sources/OpenAPIKit/Operation/Operation.swift index 06fb4ff50..cf9629059 100644 --- a/Sources/OpenAPIKit/Operation/Operation.swift +++ b/Sources/OpenAPIKit/Operation/Operation.swift @@ -11,7 +11,7 @@ extension OpenAPI { /// OpenAPI Spec "Operation Object" /// /// See [OpenAPI Operation Object](https://spec.openapis.org/oas/v3.1.1.html#operation-object). - public struct Operation: Equatable, CodableVendorExtendable { + public struct Operation: Equatable, CodableVendorExtendable, Sendable { public var tags: [String]? public var summary: String? public var description: String? diff --git a/Sources/OpenAPIKit/Parameter/Parameter.swift b/Sources/OpenAPIKit/Parameter/Parameter.swift index 5ebbb98fe..fc8f150a3 100644 --- a/Sources/OpenAPIKit/Parameter/Parameter.swift +++ b/Sources/OpenAPIKit/Parameter/Parameter.swift @@ -11,7 +11,7 @@ extension OpenAPI { /// OpenAPI Spec "Parameter Object" /// /// See [OpenAPI Parameter Object](https://spec.openapis.org/oas/v3.1.1.html#parameter-object). - public struct Parameter: Equatable, CodableVendorExtendable { + public struct Parameter: Equatable, CodableVendorExtendable, Sendable { public var name: String /// OpenAPI Spec "in" property determines the `Context`. diff --git a/Sources/OpenAPIKit/Parameter/ParameterContext.swift b/Sources/OpenAPIKit/Parameter/ParameterContext.swift index 5f6c5de3a..d017b7c67 100644 --- a/Sources/OpenAPIKit/Parameter/ParameterContext.swift +++ b/Sources/OpenAPIKit/Parameter/ParameterContext.swift @@ -16,7 +16,7 @@ extension OpenAPI.Parameter { /// all optional by default unless you pass /// `required: true` to the context construction. /// Path parameters are always required. - public enum Context: Equatable { + public enum Context: Equatable, Sendable { case query(required: Bool, allowEmptyValue: Bool) case header(required: Bool) case path diff --git a/Sources/OpenAPIKit/Parameter/ParameterSchemaContext.swift b/Sources/OpenAPIKit/Parameter/ParameterSchemaContext.swift index 98f5e11e9..f8828da2e 100644 --- a/Sources/OpenAPIKit/Parameter/ParameterSchemaContext.swift +++ b/Sources/OpenAPIKit/Parameter/ParameterSchemaContext.swift @@ -12,7 +12,7 @@ extension OpenAPI.Parameter { /// /// See [OpenAPI Parameter Object](https://spec.openapis.org/oas/v3.1.1.html#parameter-object) /// and [OpenAPI Style Values](https://spec.openapis.org/oas/v3.1.1.html#style-values). - public struct SchemaContext: Equatable { + public struct SchemaContext: Equatable, Sendable { public var style: Style public var explode: Bool public var allowReserved: Bool //defaults to false diff --git a/Sources/OpenAPIKit/Path Item/PathItem.swift b/Sources/OpenAPIKit/Path Item/PathItem.swift index d3fe1ac89..09d2bf650 100644 --- a/Sources/OpenAPIKit/Path Item/PathItem.swift +++ b/Sources/OpenAPIKit/Path Item/PathItem.swift @@ -21,7 +21,7 @@ extension OpenAPI { /// /// You can access an array of equatable `HttpMethod`/`Operation` paris with the /// `endpoints` property. - public struct PathItem: Equatable, CodableVendorExtendable { + public struct PathItem: Equatable, CodableVendorExtendable, Sendable { public var summary: String? public var description: String? public var servers: [OpenAPI.Server]? diff --git a/Sources/OpenAPIKit/Request/Request.swift b/Sources/OpenAPIKit/Request/Request.swift index 20ffbf522..58e1e01e3 100644 --- a/Sources/OpenAPIKit/Request/Request.swift +++ b/Sources/OpenAPIKit/Request/Request.swift @@ -11,7 +11,7 @@ extension OpenAPI { /// OpenAPI Spec "Request Body Object" /// /// See [OpenAPI Request Body Object](https://spec.openapis.org/oas/v3.1.1.html#request-body-object). - public struct Request: Equatable, CodableVendorExtendable { + public struct Request: Equatable, CodableVendorExtendable, Sendable { public var description: String? public var content: Content.Map public var required: Bool diff --git a/Sources/OpenAPIKit/Response/Response.swift b/Sources/OpenAPIKit/Response/Response.swift index 1696656c2..2124012c3 100644 --- a/Sources/OpenAPIKit/Response/Response.swift +++ b/Sources/OpenAPIKit/Response/Response.swift @@ -11,7 +11,7 @@ extension OpenAPI { /// OpenAPI Spec "Response Object" /// /// See [OpenAPI Response Object](https://spec.openapis.org/oas/v3.1.1.html#response-object). - public struct Response: Equatable, CodableVendorExtendable { + public struct Response: Equatable, CodableVendorExtendable, Sendable { public var description: String public var headers: Header.Map? /// An empty Content map will be omitted from encoding. diff --git a/Sources/OpenAPIKit/RuntimeExpression.swift b/Sources/OpenAPIKit/RuntimeExpression.swift index 3256ada18..19e77c076 100644 --- a/Sources/OpenAPIKit/RuntimeExpression.swift +++ b/Sources/OpenAPIKit/RuntimeExpression.swift @@ -12,7 +12,7 @@ extension OpenAPI { /// /// See [OpenAPI Runtime Expression[(https://spec.openapis.org/oas/v3.1.1.html#runtime-expressions). /// - public enum RuntimeExpression: RawRepresentable, Equatable { + public enum RuntimeExpression: RawRepresentable, Equatable, Sendable { case url case method case statusCode @@ -74,7 +74,7 @@ extension OpenAPI { return nil } - public enum Source: RawRepresentable, Equatable { + public enum Source: RawRepresentable, Equatable, Sendable { /// A reference to one of the header parameters. case header(name: String) /// A reference to one of the query parameters. diff --git a/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift b/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift index f3b061f5c..d1a86b33d 100644 --- a/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift +++ b/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore /// A `JSONSchema` type that guarantees none of its /// nodes are references. @dynamicMemberLookup -public enum DereferencedJSONSchema: Equatable, JSONSchemaContext { +public enum DereferencedJSONSchema: Equatable, JSONSchemaContext, Sendable { public typealias CoreContext = JSONSchema.CoreContext public typealias NumericContext = JSONSchema.NumericContext public typealias IntegerContext = JSONSchema.IntegerContext @@ -265,7 +265,7 @@ extension DereferencedJSONSchema { } /// The context that only applies to `.array` schemas. - public struct ArrayContext: Equatable { + public struct ArrayContext: Equatable, Sendable { /// A JSON Type Node that describes /// the type of each element in the array. public let items: DereferencedJSONSchema? @@ -333,7 +333,7 @@ extension DereferencedJSONSchema { } /// The context that only applies to `.object` schemas. - public struct ObjectContext: Equatable { + public struct ObjectContext: Equatable, Sendable { public let maxProperties: Int? let _minProperties: Int? public let properties: OrderedDictionary diff --git a/Sources/OpenAPIKit/Schema Object/JSONSchema+Combining.swift b/Sources/OpenAPIKit/Schema Object/JSONSchema+Combining.swift index 06179aa46..cac3517a3 100644 --- a/Sources/OpenAPIKit/Schema Object/JSONSchema+Combining.swift +++ b/Sources/OpenAPIKit/Schema Object/JSONSchema+Combining.swift @@ -62,7 +62,7 @@ public func ~=(lhs: JSONSchemaResolutionError, rhs: JSONSchemaResolutionError) - /// I expect this to be an area where I may want to make fixes and add /// errors without breaknig changes, so this annoying workaround for /// the absense of a "non-frozen" enum is a must. -internal enum _JSONSchemaResolutionError: CustomStringConvertible, Equatable { +internal enum _JSONSchemaResolutionError: CustomStringConvertible, Equatable, Sendable { case unsupported(because: String) case typeConflict(original: JSONType, new: JSONType) case formatConflict(original: String, new: String) diff --git a/Sources/OpenAPIKit/Schema Object/JSONSchema.swift b/Sources/OpenAPIKit/Schema Object/JSONSchema.swift index a0547e190..5e3d4a6ab 100644 --- a/Sources/OpenAPIKit/Schema Object/JSONSchema.swift +++ b/Sources/OpenAPIKit/Schema Object/JSONSchema.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore /// OpenAPI "Schema Object" /// /// See [OpenAPI Schema Object](https://spec.openapis.org/oas/v3.1.1.html#schema-object). -public struct JSONSchema: JSONSchemaContext, HasWarnings { +public struct JSONSchema: JSONSchemaContext, HasWarnings, Sendable { public let warnings: [OpenAPI.Warning] public var value: Schema @@ -68,7 +68,7 @@ public struct JSONSchema: JSONSchemaContext, HasWarnings { .init(schema: .fragment(core)) } - public enum Schema: Equatable { + public enum Schema: Equatable, Sendable { /// The null type, which replaces the functionality of the `nullable` property from /// previous versions of the OpenAPI specification. case null(CoreContext) @@ -2151,7 +2151,7 @@ extension JSONSchema: Decodable { throw VendorExtensionDecodingError.selfIsArrayNotDict } - guard let decodedAny = decoded as? [String: Any] else { + guard let decodedAny = decoded as? [String: any Sendable] else { throw VendorExtensionDecodingError.foundNonStringKeys } diff --git a/Sources/OpenAPIKit/Schema Object/JSONSchemaContext.swift b/Sources/OpenAPIKit/Schema Object/JSONSchemaContext.swift index 1c89eacfa..fed5f4556 100644 --- a/Sources/OpenAPIKit/Schema Object/JSONSchemaContext.swift +++ b/Sources/OpenAPIKit/Schema Object/JSONSchemaContext.swift @@ -12,7 +12,7 @@ import OpenAPIKitCore /// A schema context stores information about a schema. /// All schemas can have the contextual information in /// this protocol. -public protocol JSONSchemaContext { +public protocol JSONSchemaContext: Sendable { /// The format of the schema as a string value. /// /// This can be set even when a schema type has @@ -574,8 +574,8 @@ extension JSONSchema { /// `IntegerContext` _can_ be asked for the /// `NumericContext` that would describe it via its /// `numericContext` property. - public struct NumericContext: Equatable { - public struct Bound: Equatable { + public struct NumericContext: Equatable, Sendable { + public struct Bound: Equatable, Sendable { public let value: Double public let exclusive: Bool @@ -610,8 +610,8 @@ extension JSONSchema { } /// The context that only applies to `.integer` schemas. - public struct IntegerContext: Equatable { - public struct Bound: Equatable { + public struct IntegerContext: Equatable, Sendable { + public struct Bound: Equatable, Sendable { public let value: Int public let exclusive: Bool @@ -696,7 +696,7 @@ extension JSONSchema { } /// The context that only applies to `.array` schemas. - public struct ArrayContext: Equatable { + public struct ArrayContext: Equatable, Sendable { /// A JSON Type Node that describes /// the type of each element in the array. public let items: JSONSchema? @@ -729,7 +729,7 @@ extension JSONSchema { } /// The context that only applies to `.object` schemas. - public struct ObjectContext: Equatable { + public struct ObjectContext: Equatable, Sendable { /// The maximum number of properties the object /// is allowed to have. public let maxProperties: Int? @@ -796,7 +796,7 @@ extension JSONSchema { } /// The context that only applies to `.string` schemas. - public struct StringContext: Equatable { + public struct StringContext: Equatable, Sendable { public let maxLength: Int? let _minLength: Int? @@ -833,7 +833,7 @@ extension JSONSchema { extension OpenAPI { /// An encoding, as specified in RFC 2045, part 6.1 and RFC 4648. - public enum ContentEncoding: String, Codable { + public enum ContentEncoding: String, Codable, Sendable { case _7bit = "7bit" case _8bit = "8bit" case binary diff --git a/Sources/OpenAPIKit/Schema Object/TypesAndFormats.swift b/Sources/OpenAPIKit/Schema Object/TypesAndFormats.swift index 718b30951..1dc4771de 100644 --- a/Sources/OpenAPIKit/Schema Object/TypesAndFormats.swift +++ b/Sources/OpenAPIKit/Schema Object/TypesAndFormats.swift @@ -27,7 +27,7 @@ public protocol SwiftTyped { /// - number /// - integer /// - string -public enum JSONType: String, Codable { +public enum JSONType: String, Codable, Sendable { case null = "null" case boolean = "boolean" case object = "object" @@ -55,7 +55,7 @@ public enum JSONType: String, Codable { /// You can also find information on types and /// formats in the OpenAPI Specification's /// section on [data types](https://spec.openapis.org/oas/v3.1.1.html#data-types). -public enum JSONTypeFormat: Equatable { +public enum JSONTypeFormat: Equatable, Sendable { case null case boolean(BooleanFormat) case object(ObjectFormat) @@ -115,7 +115,7 @@ public enum JSONTypeFormat: Equatable { /// /// See "formats" under the OpenAPI [data type](https://spec.openapis.org/oas/v3.1.1.html#data-types) /// documentation. -public protocol OpenAPIFormat: SwiftTyped, Codable, Equatable, RawRepresentable, Validatable where RawValue == String { +public protocol OpenAPIFormat: SwiftTyped, Codable, Equatable, RawRepresentable, Validatable, Sendable where RawValue == String { static var unspecified: Self { get } var jsonType: JSONType { get } diff --git a/Sources/OpenAPIKit/Security/SecurityScheme.swift b/Sources/OpenAPIKit/Security/SecurityScheme.swift index ace997c6d..eec43ef35 100644 --- a/Sources/OpenAPIKit/Security/SecurityScheme.swift +++ b/Sources/OpenAPIKit/Security/SecurityScheme.swift @@ -12,7 +12,7 @@ extension OpenAPI { /// OpenAPI Spec "Security Scheme Object" /// /// See [OpenAPI Security Scheme Object](https://spec.openapis.org/oas/v3.1.1.html#security-scheme-object). - public struct SecurityScheme: Equatable, CodableVendorExtendable { + public struct SecurityScheme: Equatable, CodableVendorExtendable, Sendable { public var type: SecurityType public var description: String? @@ -53,7 +53,7 @@ extension OpenAPI { return .init(type: .mutualTLS, description: description) } - public enum SecurityType: Equatable { + public enum SecurityType: Equatable, Sendable { case apiKey(name: String, location: Location) case http(scheme: String, bearerFormat: String?) case oauth2(flows: OAuthFlows) diff --git a/Sources/OpenAPIKit/Server.swift b/Sources/OpenAPIKit/Server.swift index de109d7f3..5ca7f810a 100644 --- a/Sources/OpenAPIKit/Server.swift +++ b/Sources/OpenAPIKit/Server.swift @@ -13,7 +13,7 @@ extension OpenAPI { /// /// See [OpenAPI Server Object](https://spec.openapis.org/oas/v3.1.1.html#server-object). /// - public struct Server: Equatable, CodableVendorExtendable { + public struct Server: Equatable, CodableVendorExtendable, Sendable { /// OpenAPI Server URLs can have variable placeholders in them. /// The `urlTemplate` can be asked for a well-formed Foundation /// `URL` if all variables in it have been replaced by constant values. @@ -65,7 +65,7 @@ extension OpenAPI.Server { /// /// See [OpenAPI Server Variable Object](https://spec.openapis.org/oas/v3.1.1.html#server-variable-object). /// - public struct Variable: Equatable, CodableVendorExtendable { + public struct Variable: Equatable, CodableVendorExtendable, Sendable { public var `enum`: [String]? public var `default`: String public var description: String? diff --git a/Sources/OpenAPIKit/Utility/Array+ExternallyDereferenceable.swift b/Sources/OpenAPIKit/Utility/Array+ExternallyDereferenceable.swift index 3a959b7ce..ca1f3dd54 100644 --- a/Sources/OpenAPIKit/Utility/Array+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit/Utility/Array+ExternallyDereferenceable.swift @@ -4,7 +4,7 @@ import OpenAPIKitCore -extension Array where Element: ExternallyDereferenceable { +extension Array where Element: ExternallyDereferenceable & Sendable { public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { try await withThrowingTaskGroup(of: (Int, (Element, OpenAPI.Components, [Loader.Message])).self) { group in diff --git a/Sources/OpenAPIKit/Utility/Dictionary+ExternallyDereferenceable.swift b/Sources/OpenAPIKit/Utility/Dictionary+ExternallyDereferenceable.swift index ae0696e24..0bc061a0f 100644 --- a/Sources/OpenAPIKit/Utility/Dictionary+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit/Utility/Dictionary+ExternallyDereferenceable.swift @@ -5,7 +5,7 @@ import OpenAPIKitCore -extension Dictionary where Value: ExternallyDereferenceable { +extension Dictionary where Key: Sendable, Value: ExternallyDereferenceable & Sendable { public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { try await withThrowingTaskGroup(of: (Key, Value, OpenAPI.Components, [Loader.Message]).self) { group in diff --git a/Sources/OpenAPIKit/Utility/OrderedDictionary+ExternallyDereferenceable.swift b/Sources/OpenAPIKit/Utility/OrderedDictionary+ExternallyDereferenceable.swift index 1c882a7ce..66e78ab4f 100644 --- a/Sources/OpenAPIKit/Utility/OrderedDictionary+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit/Utility/OrderedDictionary+ExternallyDereferenceable.swift @@ -7,7 +7,7 @@ import OpenAPIKitCore -extension OrderedDictionary where Value: ExternallyDereferenceable { +extension OrderedDictionary where Key: Sendable, Value: ExternallyDereferenceable & Sendable { public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { try await withThrowingTaskGroup(of: (Key, Value, OpenAPI.Components, [Loader.Message]).self) { group in diff --git a/Sources/OpenAPIKit30/CodableVendorExtendable.swift b/Sources/OpenAPIKit30/CodableVendorExtendable.swift index 5f1d6401f..dc3b0b67b 100644 --- a/Sources/OpenAPIKit30/CodableVendorExtendable.swift +++ b/Sources/OpenAPIKit30/CodableVendorExtendable.swift @@ -98,7 +98,7 @@ extension CodableVendorExtendable { throw VendorExtensionDecodingError.selfIsArrayNotDict } - guard let decodedAny = decoded as? [String: Any] else { + guard let decodedAny = decoded as? [String: any Sendable] else { throw VendorExtensionDecodingError.foundNonStringKeys } diff --git a/Sources/OpenAPIKit30/Components Object/Components.swift b/Sources/OpenAPIKit30/Components Object/Components.swift index 4e5325ecf..929807d65 100644 --- a/Sources/OpenAPIKit30/Components Object/Components.swift +++ b/Sources/OpenAPIKit30/Components Object/Components.swift @@ -15,7 +15,7 @@ extension OpenAPI { /// /// This is a place to put reusable components to /// be referenced from other parts of the spec. - public struct Components: Equatable, CodableVendorExtendable { + public struct Components: Equatable, CodableVendorExtendable, Sendable { public var schemas: ComponentDictionary public var responses: ComponentDictionary diff --git a/Sources/OpenAPIKit30/Content/Content.swift b/Sources/OpenAPIKit30/Content/Content.swift index 996352e1e..8ade49c91 100644 --- a/Sources/OpenAPIKit30/Content/Content.swift +++ b/Sources/OpenAPIKit30/Content/Content.swift @@ -11,7 +11,7 @@ extension OpenAPI { /// OpenAPI Spec "Media Type Object" /// /// See [OpenAPI Media Type Object](https://spec.openapis.org/oas/v3.0.4.html#media-type-object). - public struct Content: Equatable, CodableVendorExtendable { + public struct Content: Equatable, CodableVendorExtendable, Sendable { public var schema: Either, JSONSchema>? public var example: AnyCodable? public var examples: Example.Map? diff --git a/Sources/OpenAPIKit30/Content/ContentEncoding.swift b/Sources/OpenAPIKit30/Content/ContentEncoding.swift index 24317003c..656e43e7f 100644 --- a/Sources/OpenAPIKit30/Content/ContentEncoding.swift +++ b/Sources/OpenAPIKit30/Content/ContentEncoding.swift @@ -11,7 +11,7 @@ extension OpenAPI.Content { /// OpenAPI Spec "Encoding Object" /// /// See [OpenAPI Encoding Object](https://spec.openapis.org/oas/v3.0.4.html#encoding-object). - public struct Encoding: Equatable { + public struct Encoding: Equatable, Sendable { public typealias Style = OpenAPI.Parameter.SchemaContext.Style public let contentType: OpenAPI.ContentType? diff --git a/Sources/OpenAPIKit30/Encoding and Decoding Errors/DocumentDecodingError.swift b/Sources/OpenAPIKit30/Encoding and Decoding Errors/DocumentDecodingError.swift index a585f7d93..6786b6034 100644 --- a/Sources/OpenAPIKit30/Encoding and Decoding Errors/DocumentDecodingError.swift +++ b/Sources/OpenAPIKit30/Encoding and Decoding Errors/DocumentDecodingError.swift @@ -12,7 +12,7 @@ extension OpenAPI.Error.Decoding { public let context: Context public let codingPath: [CodingKey] - public enum Context { + public enum Context: Sendable { case path(Path) case inconsistency(InconsistencyError) case other(Swift.DecodingError) diff --git a/Sources/OpenAPIKit30/Encoding and Decoding Errors/OperationDecodingError.swift b/Sources/OpenAPIKit30/Encoding and Decoding Errors/OperationDecodingError.swift index 354b16730..c62a3720a 100644 --- a/Sources/OpenAPIKit30/Encoding and Decoding Errors/OperationDecodingError.swift +++ b/Sources/OpenAPIKit30/Encoding and Decoding Errors/OperationDecodingError.swift @@ -13,7 +13,7 @@ extension OpenAPI.Error.Decoding { public let context: Context internal let relativeCodingPath: [CodingKey] - public enum Context { + public enum Context: Sendable { case request(Request) case response(Response) case inconsistency(InconsistencyError) diff --git a/Sources/OpenAPIKit30/Encoding and Decoding Errors/PathDecodingError.swift b/Sources/OpenAPIKit30/Encoding and Decoding Errors/PathDecodingError.swift index c2809d9be..af865d31e 100644 --- a/Sources/OpenAPIKit30/Encoding and Decoding Errors/PathDecodingError.swift +++ b/Sources/OpenAPIKit30/Encoding and Decoding Errors/PathDecodingError.swift @@ -13,7 +13,7 @@ extension OpenAPI.Error.Decoding { public let context: Context internal let relativeCodingPath: [CodingKey] - public enum Context { + public enum Context: Sendable { case endpoint(Operation) case inconsistency(InconsistencyError) case other(Swift.DecodingError) diff --git a/Sources/OpenAPIKit30/Encoding and Decoding Errors/ResponseDecodingError.swift b/Sources/OpenAPIKit30/Encoding and Decoding Errors/ResponseDecodingError.swift index 7d918a3f6..0089935d5 100644 --- a/Sources/OpenAPIKit30/Encoding and Decoding Errors/ResponseDecodingError.swift +++ b/Sources/OpenAPIKit30/Encoding and Decoding Errors/ResponseDecodingError.swift @@ -13,7 +13,7 @@ extension OpenAPI.Error.Decoding { public let context: Context internal let relativeCodingPath: [CodingKey] - public enum Context { + public enum Context: Sendable { case inconsistency(InconsistencyError) case other(Swift.DecodingError) case neither(EitherDecodeNoTypesMatchedError) diff --git a/Sources/OpenAPIKit30/Example.swift b/Sources/OpenAPIKit30/Example.swift index bbac01c7b..b4e564ebd 100644 --- a/Sources/OpenAPIKit30/Example.swift +++ b/Sources/OpenAPIKit30/Example.swift @@ -12,7 +12,7 @@ extension OpenAPI { /// OpenAPI Spec "Example Object" /// /// See [OpenAPI Example Object](https://spec.openapis.org/oas/v3.0.4.html#example-object). - public struct Example: Equatable, CodableVendorExtendable { + public struct Example: Equatable, CodableVendorExtendable, Sendable { public let summary: String? public let description: String? diff --git a/Sources/OpenAPIKit30/ExternalDocumentation.swift b/Sources/OpenAPIKit30/ExternalDocumentation.swift index 5af22c0af..a79ae106a 100644 --- a/Sources/OpenAPIKit30/ExternalDocumentation.swift +++ b/Sources/OpenAPIKit30/ExternalDocumentation.swift @@ -12,7 +12,7 @@ extension OpenAPI { /// OpenAPI Spec "External Documentation Object" /// /// See [OpenAPI External Documentation Object](https://spec.openapis.org/oas/v3.0.4.html#external-documentation-object). - public struct ExternalDocumentation: Equatable, CodableVendorExtendable { + public struct ExternalDocumentation: Equatable, CodableVendorExtendable, Sendable { public var description: String? public var url: URL diff --git a/Sources/OpenAPIKit30/ExternalLoader.swift b/Sources/OpenAPIKit30/ExternalLoader.swift index 28cfa8d39..ba67073de 100644 --- a/Sources/OpenAPIKit30/ExternalLoader.swift +++ b/Sources/OpenAPIKit30/ExternalLoader.swift @@ -11,7 +11,7 @@ import Foundation /// An `ExternalLoader` enables `OpenAPIKit` to load external references /// without knowing the details of what decoder is being used or how new internal /// references should be named. -public protocol ExternalLoader { +public protocol ExternalLoader where Message: Sendable { /// This can be anything that an implementor of this protocol wants to pass back from /// the `load()` function and have available after all external loading has been done. /// diff --git a/Sources/OpenAPIKit30/Header/Header.swift b/Sources/OpenAPIKit30/Header/Header.swift index 707996941..219a80ce4 100644 --- a/Sources/OpenAPIKit30/Header/Header.swift +++ b/Sources/OpenAPIKit30/Header/Header.swift @@ -11,7 +11,7 @@ extension OpenAPI { /// OpenAPI Spec "Header Object" /// /// See [OpenAPI Header Object](https://spec.openapis.org/oas/v3.0.4.html#header-object). - public struct Header: Equatable, CodableVendorExtendable { + public struct Header: Equatable, CodableVendorExtendable, Sendable { public typealias SchemaContext = Parameter.SchemaContext public let description: String? diff --git a/Sources/OpenAPIKit30/JSONReference.swift b/Sources/OpenAPIKit30/JSONReference.swift index e39883bc5..8057bf6e9 100644 --- a/Sources/OpenAPIKit30/JSONReference.swift +++ b/Sources/OpenAPIKit30/JSONReference.swift @@ -39,7 +39,7 @@ import Foundation /// Components Object will be validated when you call `validate()` on an /// `OpenAPI.Document`. /// -public enum JSONReference: Equatable, Hashable, _OpenAPIReference { +public enum JSONReference: Equatable, Hashable, _OpenAPIReference, Sendable { /// The reference is internal to the file. case `internal`(InternalReference) /// The reference refers to another file. @@ -112,7 +112,7 @@ public enum JSONReference: Equatabl /// `JSONReference`. /// /// This reference must start with "#". - public enum InternalReference: LosslessStringConvertible, RawRepresentable, Equatable, Hashable { + public enum InternalReference: LosslessStringConvertible, RawRepresentable, Equatable, Hashable, Sendable { /// The reference refers to a component (i.e. `#/components/...`). case component(name: String) /// The reference refers to some path outside the Components Object. @@ -190,7 +190,7 @@ public enum JSONReference: Equatabl /// /// This path does _not_ start with "#". It starts with a forward slash. By contrast, an /// `InternalReference` starts with "#" and is followed by the start of a `Path`. - public struct Path: ExpressibleByArrayLiteral, ExpressibleByStringLiteral, LosslessStringConvertible, RawRepresentable, Equatable, Hashable { + public struct Path: ExpressibleByArrayLiteral, ExpressibleByStringLiteral, LosslessStringConvertible, RawRepresentable, Equatable, Hashable, Sendable { /// The Path's components. In the `rawValue`, these components are joined /// with forward slashes '/' per the JSON Reference specification. diff --git a/Sources/OpenAPIKit30/Link.swift b/Sources/OpenAPIKit30/Link.swift index e1748963d..4b6d46d2a 100644 --- a/Sources/OpenAPIKit30/Link.swift +++ b/Sources/OpenAPIKit30/Link.swift @@ -16,7 +16,7 @@ extension OpenAPI { /// OpenAPI Spec "Link Object" /// /// See [OpenAPI Link Object](https://spec.openapis.org/oas/v3.1.1.html#link-object). - public struct Link: Equatable, CodableVendorExtendable { + public struct Link: Equatable, CodableVendorExtendable, Sendable { /// The **OpenAPI**` `operationRef` or `operationId` field, depending on whether /// a `URL` of a remote or local Operation Object or a `operationId` (String) of an /// operation defined in the same document is given. diff --git a/Sources/OpenAPIKit30/Operation/Operation.swift b/Sources/OpenAPIKit30/Operation/Operation.swift index cda2c0217..8d97ced60 100644 --- a/Sources/OpenAPIKit30/Operation/Operation.swift +++ b/Sources/OpenAPIKit30/Operation/Operation.swift @@ -11,7 +11,7 @@ extension OpenAPI { /// OpenAPI Spec "Operation Object" /// /// See [OpenAPI Operation Object](https://spec.openapis.org/oas/v3.0.4.html#operation-object). - public struct Operation: Equatable, CodableVendorExtendable { + public struct Operation: Equatable, CodableVendorExtendable, Sendable { public var tags: [String]? public var summary: String? public var description: String? diff --git a/Sources/OpenAPIKit30/Parameter/Parameter.swift b/Sources/OpenAPIKit30/Parameter/Parameter.swift index e7d2a89a2..d69e0cb1e 100644 --- a/Sources/OpenAPIKit30/Parameter/Parameter.swift +++ b/Sources/OpenAPIKit30/Parameter/Parameter.swift @@ -11,7 +11,7 @@ extension OpenAPI { /// OpenAPI Spec "Parameter Object" /// /// See [OpenAPI Parameter Object](https://spec.openapis.org/oas/v3.0.4.html#parameter-object). - public struct Parameter: Equatable, CodableVendorExtendable { + public struct Parameter: Equatable, CodableVendorExtendable, Sendable { public var name: String /// OpenAPI Spec "in" property determines the `Context`. diff --git a/Sources/OpenAPIKit30/Parameter/ParameterContext.swift b/Sources/OpenAPIKit30/Parameter/ParameterContext.swift index 112a08273..3bfb1428a 100644 --- a/Sources/OpenAPIKit30/Parameter/ParameterContext.swift +++ b/Sources/OpenAPIKit30/Parameter/ParameterContext.swift @@ -16,7 +16,7 @@ extension OpenAPI.Parameter { /// all optional by default unless you pass /// `required: true` to the context construction. /// Path parameters are always required. - public enum Context: Equatable { + public enum Context: Equatable, Sendable { case query(required: Bool, allowEmptyValue: Bool) case header(required: Bool) case path diff --git a/Sources/OpenAPIKit30/Parameter/ParameterSchemaContext.swift b/Sources/OpenAPIKit30/Parameter/ParameterSchemaContext.swift index 3b4b2d600..7dddf716d 100644 --- a/Sources/OpenAPIKit30/Parameter/ParameterSchemaContext.swift +++ b/Sources/OpenAPIKit30/Parameter/ParameterSchemaContext.swift @@ -12,7 +12,7 @@ extension OpenAPI.Parameter { /// /// See [OpenAPI Parameter Object](https://spec.openapis.org/oas/v3.0.4.html#parameter-object) /// and [OpenAPI Style Values](https://spec.openapis.org/oas/v3.0.4.html#style-values). - public struct SchemaContext: Equatable { + public struct SchemaContext: Equatable, Sendable { public var style: Style public var explode: Bool public var allowReserved: Bool //defaults to false diff --git a/Sources/OpenAPIKit30/Path Item/PathItem.swift b/Sources/OpenAPIKit30/Path Item/PathItem.swift index cb420b7b2..463f5610b 100644 --- a/Sources/OpenAPIKit30/Path Item/PathItem.swift +++ b/Sources/OpenAPIKit30/Path Item/PathItem.swift @@ -21,7 +21,7 @@ extension OpenAPI { /// /// You can access an array of equatable `HttpMethod`/`Operation` paris with the /// `endpoints` property. - public struct PathItem: Equatable, CodableVendorExtendable { + public struct PathItem: Equatable, CodableVendorExtendable, Sendable { public var summary: String? public var description: String? public var servers: [OpenAPI.Server]? diff --git a/Sources/OpenAPIKit30/Request/Request.swift b/Sources/OpenAPIKit30/Request/Request.swift index a1ca23db6..c0c9b5e20 100644 --- a/Sources/OpenAPIKit30/Request/Request.swift +++ b/Sources/OpenAPIKit30/Request/Request.swift @@ -11,7 +11,7 @@ extension OpenAPI { /// OpenAPI Spec "Request Body Object" /// /// See [OpenAPI Request Body Object](https://spec.openapis.org/oas/v3.0.4.html#request-body-object). - public struct Request: Equatable, CodableVendorExtendable { + public struct Request: Equatable, CodableVendorExtendable, Sendable { public var description: String? public var content: Content.Map public var required: Bool diff --git a/Sources/OpenAPIKit30/Response/Response.swift b/Sources/OpenAPIKit30/Response/Response.swift index 76b7da87c..31c990437 100644 --- a/Sources/OpenAPIKit30/Response/Response.swift +++ b/Sources/OpenAPIKit30/Response/Response.swift @@ -11,7 +11,7 @@ extension OpenAPI { /// OpenAPI Spec "Response Object" /// /// See [OpenAPI Response Object](https://spec.openapis.org/oas/v3.0.4.html#response-object). - public struct Response: Equatable, CodableVendorExtendable { + public struct Response: Equatable, CodableVendorExtendable, Sendable { public var description: String public var headers: Header.Map? /// An empty Content map will be omitted from encoding. diff --git a/Sources/OpenAPIKit30/RuntimeExpression.swift b/Sources/OpenAPIKit30/RuntimeExpression.swift index 0736f1b3e..7e6c1a79e 100644 --- a/Sources/OpenAPIKit30/RuntimeExpression.swift +++ b/Sources/OpenAPIKit30/RuntimeExpression.swift @@ -12,7 +12,7 @@ extension OpenAPI { /// /// See [OpenAPI Runtime Expression[(https://spec.openapis.org/oas/v3.0.4.html#runtime-expressions). /// - public enum RuntimeExpression: RawRepresentable, Equatable { + public enum RuntimeExpression: RawRepresentable, Equatable, Sendable { case url case method case statusCode @@ -74,7 +74,7 @@ extension OpenAPI { return nil } - public enum Source: RawRepresentable, Equatable { + public enum Source: RawRepresentable, Equatable, Sendable { /// A reference to one of the header parameters. case header(name: String) /// A reference to one of the query parameters. diff --git a/Sources/OpenAPIKit30/Schema Object/DereferencedJSONSchema.swift b/Sources/OpenAPIKit30/Schema Object/DereferencedJSONSchema.swift index f749f549a..496ec328e 100644 --- a/Sources/OpenAPIKit30/Schema Object/DereferencedJSONSchema.swift +++ b/Sources/OpenAPIKit30/Schema Object/DereferencedJSONSchema.swift @@ -162,7 +162,7 @@ extension DereferencedJSONSchema { } /// The context that only applies to `.array` schemas. - public struct ArrayContext: Equatable { + public struct ArrayContext: Equatable, Sendable { /// A JSON Type Node that describes /// the type of each element in the array. public let items: DereferencedJSONSchema? @@ -230,7 +230,7 @@ extension DereferencedJSONSchema { } /// The context that only applies to `.object` schemas. - public struct ObjectContext: Equatable { + public struct ObjectContext: Equatable, Sendable { public let maxProperties: Int? let _minProperties: Int? public let properties: OrderedDictionary diff --git a/Sources/OpenAPIKit30/Schema Object/JSONSchema+Combining.swift b/Sources/OpenAPIKit30/Schema Object/JSONSchema+Combining.swift index e8097fed5..dd213437a 100644 --- a/Sources/OpenAPIKit30/Schema Object/JSONSchema+Combining.swift +++ b/Sources/OpenAPIKit30/Schema Object/JSONSchema+Combining.swift @@ -62,7 +62,7 @@ public func ~=(lhs: JSONSchemaResolutionError, rhs: JSONSchemaResolutionError) - /// I expect this to be an area where I may want to make fixes and add /// errors without breaknig changes, so this annoying workaround for /// the absense of a "non-frozen" enum is a must. -internal enum _JSONSchemaResolutionError: CustomStringConvertible, Equatable { +internal enum _JSONSchemaResolutionError: CustomStringConvertible, Equatable, Sendable { case unsupported(because: String) case typeConflict(original: JSONType, new: JSONType) case formatConflict(original: String, new: String) diff --git a/Sources/OpenAPIKit30/Schema Object/JSONSchema.swift b/Sources/OpenAPIKit30/Schema Object/JSONSchema.swift index 3a2413897..d53a086ad 100644 --- a/Sources/OpenAPIKit30/Schema Object/JSONSchema.swift +++ b/Sources/OpenAPIKit30/Schema Object/JSONSchema.swift @@ -10,7 +10,7 @@ import OpenAPIKitCore /// OpenAPI "Schema Object" /// /// See [OpenAPI Schema Object](https://spec.openapis.org/oas/v3.0.4.html#schema-object). -public struct JSONSchema: JSONSchemaContext, HasWarnings, VendorExtendable { +public struct JSONSchema: JSONSchemaContext, HasWarnings, VendorExtendable, Sendable { public let warnings: [OpenAPI.Warning] public let value: Schema @@ -74,7 +74,7 @@ public struct JSONSchema: JSONSchemaContext, HasWarnings, VendorExtendable { .init(schema: .fragment(core)) } - public enum Schema: Equatable { + public enum Schema: Equatable, Sendable { case boolean(CoreContext) case number(CoreContext, NumericContext) case integer(CoreContext, IntegerContext) @@ -1901,7 +1901,7 @@ extension JSONSchema: Decodable { throw VendorExtensionDecodingError.selfIsArrayNotDict } - guard let decodedAny = decoded as? [String: Any] else { + guard let decodedAny = decoded as? [String: any Sendable] else { throw VendorExtensionDecodingError.foundNonStringKeys } diff --git a/Sources/OpenAPIKit30/Schema Object/JSONSchemaContext.swift b/Sources/OpenAPIKit30/Schema Object/JSONSchemaContext.swift index 8d2ee88b5..c41485aea 100644 --- a/Sources/OpenAPIKit30/Schema Object/JSONSchemaContext.swift +++ b/Sources/OpenAPIKit30/Schema Object/JSONSchemaContext.swift @@ -12,7 +12,7 @@ import OpenAPIKitCore /// A schema context stores information about a schema. /// All schemas can have the contextual information in /// this protocol. -public protocol JSONSchemaContext { +public protocol JSONSchemaContext: Sendable { /// The format of the schema as a string value. /// /// This can be set even when a schema type has @@ -403,8 +403,8 @@ extension JSONSchema { /// `IntegerContext` _can_ be asked for the /// `NumericContext` that would describe it via its /// `numericContext` property. - public struct NumericContext: Equatable { - public struct Bound: Equatable { + public struct NumericContext: Equatable, Sendable { + public struct Bound: Equatable, Sendable { public let value: Double public let exclusive: Bool @@ -439,8 +439,8 @@ extension JSONSchema { } /// The context that only applies to `.integer` schemas. - public struct IntegerContext: Equatable { - public struct Bound: Equatable { + public struct IntegerContext: Equatable, Sendable { + public struct Bound: Equatable, Sendable { public let value: Int public let exclusive: Bool @@ -525,7 +525,7 @@ extension JSONSchema { } /// The context that only applies to `.array` schemas. - public struct ArrayContext: Equatable { + public struct ArrayContext: Equatable, Sendable { /// A JSON Type Node that describes /// the type of each element in the array. public let items: JSONSchema? @@ -558,7 +558,7 @@ extension JSONSchema { } /// The context that only applies to `.object` schemas. - public struct ObjectContext: Equatable { + public struct ObjectContext: Equatable, Sendable { /// The maximum number of properties the object /// is allowed to have. public let maxProperties: Int? @@ -625,7 +625,7 @@ extension JSONSchema { } /// The context that only applies to `.string` schemas. - public struct StringContext: Equatable { + public struct StringContext: Equatable, Sendable { public let maxLength: Int? let _minLength: Int? @@ -653,7 +653,7 @@ extension JSONSchema { } /// The context that only applies to `.reference` schemas. - public struct ReferenceContext: Equatable { + public struct ReferenceContext: Equatable, Sendable { public let required: Bool public init(required: Bool = true) { diff --git a/Sources/OpenAPIKit30/Schema Object/TypesAndFormats.swift b/Sources/OpenAPIKit30/Schema Object/TypesAndFormats.swift index 464da411a..e01b3e9f3 100644 --- a/Sources/OpenAPIKit30/Schema Object/TypesAndFormats.swift +++ b/Sources/OpenAPIKit30/Schema Object/TypesAndFormats.swift @@ -27,7 +27,7 @@ public protocol SwiftTyped { /// - number /// - integer /// - string -public enum JSONType: String, Codable { +public enum JSONType: String, Codable, Sendable { case boolean = "boolean" case object = "object" case array = "array" @@ -54,7 +54,7 @@ public enum JSONType: String, Codable { /// You can also find information on types and /// formats in the OpenAPI Specification's /// section on [data types](https://spec.openapis.org/oas/v3.0.4.html#data-types). -public enum JSONTypeFormat: Equatable { +public enum JSONTypeFormat: Equatable, Sendable { case boolean(BooleanFormat) case object(ObjectFormat) case array(ArrayFormat) @@ -109,7 +109,7 @@ public enum JSONTypeFormat: Equatable { /// /// See "formats" under the OpenAPI [data type](https://spec.openapis.org/oas/v3.0.4.html#data-types) /// documentation. -public protocol OpenAPIFormat: SwiftTyped, Codable, Equatable, RawRepresentable, Validatable where RawValue == String { +public protocol OpenAPIFormat: SwiftTyped, Codable, Equatable, RawRepresentable, Validatable, Sendable where RawValue == String { static var unspecified: Self { get } var jsonType: JSONType { get } diff --git a/Sources/OpenAPIKit30/Security/SecurityScheme.swift b/Sources/OpenAPIKit30/Security/SecurityScheme.swift index 167d85ee4..86c76e019 100644 --- a/Sources/OpenAPIKit30/Security/SecurityScheme.swift +++ b/Sources/OpenAPIKit30/Security/SecurityScheme.swift @@ -12,7 +12,7 @@ extension OpenAPI { /// OpenAPI Spec "Security Scheme Object" /// /// See [OpenAPI Security Scheme Object](https://spec.openapis.org/oas/v3.0.4.html#security-scheme-object). - public struct SecurityScheme: Equatable, CodableVendorExtendable { + public struct SecurityScheme: Equatable, CodableVendorExtendable, Sendable { public var type: SecurityType public var description: String? @@ -49,7 +49,7 @@ extension OpenAPI { return .init(type: .openIdConnect(openIdConnectUrl: url), description: description) } - public enum SecurityType: Equatable { + public enum SecurityType: Equatable, Sendable { case apiKey(name: String, location: Location) case http(scheme: String, bearerFormat: String?) case oauth2(flows: OAuthFlows) diff --git a/Sources/OpenAPIKit30/Server.swift b/Sources/OpenAPIKit30/Server.swift index ec63110b2..8d1ed69d8 100644 --- a/Sources/OpenAPIKit30/Server.swift +++ b/Sources/OpenAPIKit30/Server.swift @@ -13,7 +13,7 @@ extension OpenAPI { /// /// See [OpenAPI Server Object](https://spec.openapis.org/oas/v3.0.4.html#server-object). /// - public struct Server: Equatable, CodableVendorExtendable { + public struct Server: Equatable, CodableVendorExtendable, Sendable { /// OpenAPI Server URLs can have variable placeholders in them. /// The `urlTemplate` can be asked for a well-formed Foundation /// `URL` if all variables in it have been replaced by constant values. @@ -65,7 +65,7 @@ extension OpenAPI.Server { /// /// See [OpenAPI Server Variable Object](https://spec.openapis.org/oas/v3.0.4.html#server-variable-object). /// - public struct Variable: Equatable, CodableVendorExtendable { + public struct Variable: Equatable, CodableVendorExtendable, Sendable { public var `enum`: [String] public var `default`: String public var description: String? diff --git a/Sources/OpenAPIKit30/Utility/Array+ExternallyDereferenceable.swift b/Sources/OpenAPIKit30/Utility/Array+ExternallyDereferenceable.swift index 3a959b7ce..ca1f3dd54 100644 --- a/Sources/OpenAPIKit30/Utility/Array+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit30/Utility/Array+ExternallyDereferenceable.swift @@ -4,7 +4,7 @@ import OpenAPIKitCore -extension Array where Element: ExternallyDereferenceable { +extension Array where Element: ExternallyDereferenceable & Sendable { public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { try await withThrowingTaskGroup(of: (Int, (Element, OpenAPI.Components, [Loader.Message])).self) { group in diff --git a/Sources/OpenAPIKit30/Utility/Dictionary+ExternallyDereferenceable.swift b/Sources/OpenAPIKit30/Utility/Dictionary+ExternallyDereferenceable.swift index 1369bb788..16ddfcd8d 100644 --- a/Sources/OpenAPIKit30/Utility/Dictionary+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit30/Utility/Dictionary+ExternallyDereferenceable.swift @@ -5,7 +5,7 @@ import OpenAPIKitCore -extension Dictionary where Value: ExternallyDereferenceable { +extension Dictionary where Key: Sendable, Value: ExternallyDereferenceable & Sendable { public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { try await withThrowingTaskGroup(of: (Key, Value, OpenAPI.Components, [Loader.Message]).self) { group in diff --git a/Sources/OpenAPIKit30/Utility/OrderedDictionary+ExternallyDereferenceable.swift b/Sources/OpenAPIKit30/Utility/OrderedDictionary+ExternallyDereferenceable.swift index 1c882a7ce..66e78ab4f 100644 --- a/Sources/OpenAPIKit30/Utility/OrderedDictionary+ExternallyDereferenceable.swift +++ b/Sources/OpenAPIKit30/Utility/OrderedDictionary+ExternallyDereferenceable.swift @@ -7,7 +7,7 @@ import OpenAPIKitCore -extension OrderedDictionary where Value: ExternallyDereferenceable { +extension OrderedDictionary where Key: Sendable, Value: ExternallyDereferenceable & Sendable { public func externallyDereferenced(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) { try await withThrowingTaskGroup(of: (Key, Value, OpenAPI.Components, [Loader.Message]).self) { group in diff --git a/Sources/OpenAPIKitCore/AnyCodable/AnyCodable.swift b/Sources/OpenAPIKitCore/AnyCodable/AnyCodable.swift index 8b86f9978..f697d360c 100644 --- a/Sources/OpenAPIKitCore/AnyCodable/AnyCodable.swift +++ b/Sources/OpenAPIKitCore/AnyCodable/AnyCodable.swift @@ -32,12 +32,21 @@ import Foundation and other collections that require `Encodable` or `Decodable` conformance by declaring their contained type to be `AnyCodable`. */ -public struct AnyCodable { +public struct AnyCodable: @unchecked Sendable { + // IMPORTANT: + // We rely on the fact that AnyCodable can only be constructed with an initializer that knows + // the type of its argument to be Sendable in order to confidently state that AnyCodable itself + // is @unchecked Sendable. public let value: Any - public init(_ value: T?) { + public init(_ value: T?) { self.value = value ?? () } + + // Dangerous, but we use this below where we must transform AnyCodable by e.g. mapping on its value + fileprivate init(trusted value: Any) { + self.value = value + } } extension AnyCodable: Encodable { @@ -83,9 +92,9 @@ extension AnyCodable: Encodable { try container.encode(date) case let url as URL: try container.encode(url) - case let array as [Any?]: + case let array as [(any Sendable)?]: try container.encode(array.map { AnyCodable($0) }) - case let dictionary as [String: Any?]: + case let dictionary as [String: (any Sendable)?]: try container.encode(dictionary.mapValues { AnyCodable($0) }) case let encodableValue as Encodable: try container.encode(encodableValue) @@ -146,9 +155,9 @@ extension AnyCodable: Decodable { } else if let string = try? container.decode(String.self) { self.init(string) } else if let array = try? container.decode([AnyCodable].self) { - self.init(array.map { $0.value }) + self.init(trusted: array.map { $0.value }) } else if let dictionary = try? container.decode([String: AnyCodable].self) { - self.init(dictionary.mapValues { $0.value }) + self.init(trusted: dictionary.mapValues { $0.value }) } else { throw DecodingError.dataCorruptedError(in: container, debugDescription: "AnyCodable value cannot be decoded") } @@ -249,12 +258,10 @@ extension AnyCodable: ExpressibleByBooleanLiteral {} extension AnyCodable: ExpressibleByIntegerLiteral {} extension AnyCodable: ExpressibleByFloatLiteral {} extension AnyCodable: ExpressibleByStringLiteral {} -extension AnyCodable: ExpressibleByArrayLiteral {} -extension AnyCodable: ExpressibleByDictionaryLiteral {} extension AnyCodable { public init(nilLiteral _: ()) { - self.init(nil as Any?) + self.init(trusted: (nil as Any?) as Any) } public init(booleanLiteral value: Bool) { @@ -272,12 +279,4 @@ extension AnyCodable { public init(stringLiteral value: String) { self.init(value) } - - public init(arrayLiteral elements: Any...) { - self.init(elements) - } - - public init(dictionaryLiteral elements: (AnyHashable, Any)...) { - self.init([AnyHashable: Any](elements, uniquingKeysWith: { first, _ in first })) - } } diff --git a/Sources/OpenAPIKitCore/Either/Either.swift b/Sources/OpenAPIKitCore/Either/Either.swift index 0e8450080..e5c24ad69 100644 --- a/Sources/OpenAPIKitCore/Either/Either.swift +++ b/Sources/OpenAPIKitCore/Either/Either.swift @@ -56,3 +56,5 @@ public enum Either { extension Either: Equatable where A: Equatable, B: Equatable {} +extension Either: Sendable where A: Sendable, B: Sendable {} + diff --git a/Sources/OpenAPIKitCore/Encoding and Decoding Errors And Warnings/OpenAPIDecodingErrors.swift b/Sources/OpenAPIKitCore/Encoding and Decoding Errors And Warnings/OpenAPIDecodingErrors.swift index 6b748536e..9861837e7 100644 --- a/Sources/OpenAPIKitCore/Encoding and Decoding Errors And Warnings/OpenAPIDecodingErrors.swift +++ b/Sources/OpenAPIKitCore/Encoding and Decoding Errors And Warnings/OpenAPIDecodingErrors.swift @@ -37,7 +37,7 @@ public protocol PathContextError { var codingPath: [CodingKey] { get } } -public protocol OpenAPIError: Swift.Error, CustomStringConvertible, PathContextError { +public protocol OpenAPIError: Swift.Error, CustomStringConvertible, PathContextError, Sendable { /// The subject of the error (i.e. the thing being worked with /// when the error occurred). /// diff --git a/Sources/OpenAPIKitCore/Encoding and Decoding Errors And Warnings/RequestDecodingError.swift b/Sources/OpenAPIKitCore/Encoding and Decoding Errors And Warnings/RequestDecodingError.swift index af8d921ba..776bf23fa 100644 --- a/Sources/OpenAPIKitCore/Encoding and Decoding Errors And Warnings/RequestDecodingError.swift +++ b/Sources/OpenAPIKitCore/Encoding and Decoding Errors And Warnings/RequestDecodingError.swift @@ -6,11 +6,11 @@ // extension Error.Decoding { - public struct Request: OpenAPIError { + public struct Request: OpenAPIError, Sendable { public let context: Context internal let relativeCodingPath: [CodingKey] - public enum Context { + public enum Context: Sendable { case inconsistency(InconsistencyError) case other(Swift.DecodingError) case neither(EitherDecodeNoTypesMatchedError) diff --git a/Sources/OpenAPIKitCore/OrderedDictionary/OrderedDictionary.swift b/Sources/OpenAPIKitCore/OrderedDictionary/OrderedDictionary.swift index 8a9cd91a8..ffadaf393 100644 --- a/Sources/OpenAPIKitCore/OrderedDictionary/OrderedDictionary.swift +++ b/Sources/OpenAPIKitCore/OrderedDictionary/OrderedDictionary.swift @@ -274,6 +274,10 @@ extension OrderedDictionary: Equatable where Value: Equatable { } } +// MARK: - Sendable + +extension OrderedDictionary: Sendable where Key: Sendable, Value: Sendable {} + // MARK: - Codable public struct AnyCodingKey: CodingKey { diff --git a/Sources/OpenAPIKitCore/Shared/CallbackURL.swift b/Sources/OpenAPIKitCore/Shared/CallbackURL.swift index 2da7e90ff..d828a4598 100644 --- a/Sources/OpenAPIKitCore/Shared/CallbackURL.swift +++ b/Sources/OpenAPIKitCore/Shared/CallbackURL.swift @@ -13,7 +13,7 @@ extension Shared { /// /// See [OpenAPI Callback Object](https://spec.openapis.org/oas/v3.0.4.html#callback-object) and [OpenAPI Runtime Expression](https://spec.openapis.org/oas/v3.0.4.html#runtime-expressions) for more. /// - public struct CallbackURL: Hashable, RawRepresentable { + public struct CallbackURL: Hashable, RawRepresentable, Sendable { public let template: URLTemplate /// The string value of the URL without variable replacement. diff --git a/Sources/OpenAPIKitCore/Shared/ComponentKey.swift b/Sources/OpenAPIKitCore/Shared/ComponentKey.swift index 251ad343a..454f3f1e5 100644 --- a/Sources/OpenAPIKitCore/Shared/ComponentKey.swift +++ b/Sources/OpenAPIKitCore/Shared/ComponentKey.swift @@ -12,7 +12,7 @@ extension Shared { /// /// These keys must match the regex /// `^[a-zA-Z0-9\.\-_]+$`. - public struct ComponentKey: RawRepresentable, ExpressibleByStringLiteral, Codable, Equatable, Hashable, StringConvertibleHintProvider { + public struct ComponentKey: RawRepresentable, ExpressibleByStringLiteral, Codable, Equatable, Hashable, StringConvertibleHintProvider, Sendable { public let rawValue: String public init(stringLiteral value: StringLiteralType) { diff --git a/Sources/OpenAPIKitCore/Shared/ContentType.swift b/Sources/OpenAPIKitCore/Shared/ContentType.swift index 839759aee..75fc456ce 100644 --- a/Sources/OpenAPIKitCore/Shared/ContentType.swift +++ b/Sources/OpenAPIKitCore/Shared/ContentType.swift @@ -7,7 +7,7 @@ extension Shared { /// The Content Type of an API request or response body. - public struct ContentType: Codable, Equatable, Hashable, RawRepresentable, HasWarnings { + public struct ContentType: Codable, Equatable, Hashable, RawRepresentable, HasWarnings, Sendable { internal let underlyingType: Builtin public let warnings: [Warning] diff --git a/Sources/OpenAPIKitCore/Shared/Discriminator.swift b/Sources/OpenAPIKitCore/Shared/Discriminator.swift index e2cfe5949..b1c1716bd 100644 --- a/Sources/OpenAPIKitCore/Shared/Discriminator.swift +++ b/Sources/OpenAPIKitCore/Shared/Discriminator.swift @@ -9,7 +9,7 @@ extension Shared { /// OpenAPI Spec "Disciminator Object" /// /// See [OpenAPI Discriminator Object](https://spec.openapis.org/oas/v3.0.4.html#discriminator-object). - public struct Discriminator: Equatable { + public struct Discriminator: Equatable, Sendable { public let propertyName: String public let mapping: OrderedDictionary? diff --git a/Sources/OpenAPIKitCore/Shared/HttpMethod.swift b/Sources/OpenAPIKitCore/Shared/HttpMethod.swift index b5062bcbc..57481265e 100644 --- a/Sources/OpenAPIKitCore/Shared/HttpMethod.swift +++ b/Sources/OpenAPIKitCore/Shared/HttpMethod.swift @@ -12,7 +12,7 @@ extension Shared { /// See [OpenAPI Path Item Object](https://spec.openapis.org/oas/v3.0.4.html#path-item-object) because the supported /// HTTP methods are enumerated as properties on that /// object. - public enum HttpMethod: String, CaseIterable { + public enum HttpMethod: String, CaseIterable, Sendable { case get = "GET" case post = "POST" case patch = "PATCH" diff --git a/Sources/OpenAPIKitCore/Shared/JSONSchemaPermissions.swift b/Sources/OpenAPIKitCore/Shared/JSONSchemaPermissions.swift index c6363d646..de0b674cc 100644 --- a/Sources/OpenAPIKitCore/Shared/JSONSchemaPermissions.swift +++ b/Sources/OpenAPIKitCore/Shared/JSONSchemaPermissions.swift @@ -6,7 +6,7 @@ // extension Shared { - public enum JSONSchemaPermissions: String, Codable { + public enum JSONSchemaPermissions: String, Codable, Sendable { case readOnly case writeOnly case readWrite diff --git a/Sources/OpenAPIKitCore/Shared/JSONTypeFormat.swift b/Sources/OpenAPIKitCore/Shared/JSONTypeFormat.swift index fb5db6591..ce81677ca 100644 --- a/Sources/OpenAPIKitCore/Shared/JSONTypeFormat.swift +++ b/Sources/OpenAPIKitCore/Shared/JSONTypeFormat.swift @@ -13,7 +13,7 @@ extension Shared { /// type, but it is still important to be able to specify a format without /// a type. This can come into play when writing fragments of schemas /// to be combined later. - public enum AnyFormat: RawRepresentable, Equatable { + public enum AnyFormat: RawRepresentable, Equatable, Sendable { case generic case other(String) @@ -40,7 +40,7 @@ extension Shared { } /// The allowed "format" properties for `.boolean` schemas. - public enum BooleanFormat: RawRepresentable, Equatable { + public enum BooleanFormat: RawRepresentable, Equatable, Sendable { case generic case other(String) @@ -67,7 +67,7 @@ extension Shared { } /// The allowed "format" properties for `.object` schemas. - public enum ObjectFormat: RawRepresentable, Equatable { + public enum ObjectFormat: RawRepresentable, Equatable, Sendable { case generic case other(String) @@ -94,7 +94,7 @@ extension Shared { } /// The allowed "format" properties for `.array` schemas. - public enum ArrayFormat: RawRepresentable, Equatable { + public enum ArrayFormat: RawRepresentable, Equatable, Sendable { case generic case other(String) @@ -121,7 +121,7 @@ extension Shared { } /// The allowed "format" properties for `.number` schemas. - public enum NumberFormat: RawRepresentable, Equatable { + public enum NumberFormat: RawRepresentable, Equatable, Sendable { case generic case float case double @@ -154,7 +154,7 @@ extension Shared { } /// The allowed "format" properties for `.integer` schemas. - public enum IntegerFormat: RawRepresentable, Equatable { + public enum IntegerFormat: RawRepresentable, Equatable, Sendable { case generic case int32 case int64 diff --git a/Sources/OpenAPIKitCore/Shared/OAuthFlows.swift b/Sources/OpenAPIKitCore/Shared/OAuthFlows.swift index bb977421d..7eaab4b05 100644 --- a/Sources/OpenAPIKitCore/Shared/OAuthFlows.swift +++ b/Sources/OpenAPIKitCore/Shared/OAuthFlows.swift @@ -11,7 +11,7 @@ extension Shared { /// OpenAPI Spec "Oauth Flows Object" /// /// See [OpenAPI Oauth Flows Object](https://spec.openapis.org/oas/v3.0.4.html#oauth-flows-object). - public struct OAuthFlows: Equatable { + public struct OAuthFlows: Equatable, Sendable { public let implicit: Implicit? public let password: Password? public let clientCredentials: ClientCredentials? @@ -35,13 +35,13 @@ extension Shared.OAuthFlows { public typealias Scope = String public typealias ScopeDescription = String - public struct CommonFields: Equatable { + public struct CommonFields: Equatable, Sendable { public let refreshUrl: URL? public let scopes: OrderedDictionary } @dynamicMemberLookup - public struct Implicit: Equatable { + public struct Implicit: Equatable, Sendable { private let common: CommonFields public let authorizationUrl: URL @@ -56,7 +56,7 @@ extension Shared.OAuthFlows { } @dynamicMemberLookup - public struct Password: Equatable { + public struct Password: Equatable, Sendable { private let common: CommonFields public let tokenUrl: URL @@ -71,7 +71,7 @@ extension Shared.OAuthFlows { } @dynamicMemberLookup - public struct ClientCredentials: Equatable { + public struct ClientCredentials: Equatable, Sendable { private let common: CommonFields public let tokenUrl: URL @@ -86,7 +86,7 @@ extension Shared.OAuthFlows { } @dynamicMemberLookup - public struct AuthorizationCode: Equatable { + public struct AuthorizationCode: Equatable, Sendable { private let common: CommonFields public let authorizationUrl: URL public let tokenUrl: URL diff --git a/Sources/OpenAPIKitCore/Shared/ParameterSchemaContextStyle.swift b/Sources/OpenAPIKitCore/Shared/ParameterSchemaContextStyle.swift index e28e9c5be..a3166d6dc 100644 --- a/Sources/OpenAPIKitCore/Shared/ParameterSchemaContextStyle.swift +++ b/Sources/OpenAPIKitCore/Shared/ParameterSchemaContextStyle.swift @@ -6,7 +6,7 @@ // extension Shared { - public enum ParameterSchemaContextStyle: String, CaseIterable, Codable { + public enum ParameterSchemaContextStyle: String, CaseIterable, Codable, Sendable { case form case simple case matrix diff --git a/Sources/OpenAPIKitCore/Shared/Path.swift b/Sources/OpenAPIKitCore/Shared/Path.swift index 0a505b114..8e73ad831 100644 --- a/Sources/OpenAPIKitCore/Shared/Path.swift +++ b/Sources/OpenAPIKitCore/Shared/Path.swift @@ -10,7 +10,7 @@ extension Shared { /// /// See [OpenAPI Paths Object](https://spec.openapis.org/oas/v3.0.4.html#paths-object) /// and [OpenAPI Patterned Fields](https://spec.openapis.org/oas/v3.0.4.html#patterned-fields). - public struct Path: RawRepresentable, Equatable, Hashable { + public struct Path: RawRepresentable, Equatable, Hashable, Sendable { public let components: [String] public let trailingSlash: Bool diff --git a/Sources/OpenAPIKitCore/Shared/ResponseStatusCode.swift b/Sources/OpenAPIKitCore/Shared/ResponseStatusCode.swift index 875097ab2..6bdcd7476 100644 --- a/Sources/OpenAPIKitCore/Shared/ResponseStatusCode.swift +++ b/Sources/OpenAPIKitCore/Shared/ResponseStatusCode.swift @@ -18,7 +18,7 @@ extension Shared { /// You can use integer literals to specify an exact status code. /// /// Status code ranges are named in the `StatusCode.Range` enum. For example, the "1XX" range (100-199) can be written as either `.range(.information)` or as `.range(.init(rawValue: "1XX"))`. - public struct ResponseStatusCode: RawRepresentable, Equatable, Hashable, HasWarnings { + public struct ResponseStatusCode: RawRepresentable, Equatable, Hashable, HasWarnings, Sendable { public typealias RawValue = String public let warnings: [Warning] @@ -34,13 +34,13 @@ extension Shared { public static func range(_ range: Range) -> Self { .init(value: .range(range)) } public static func status(code: Int) -> Self { .init(value: .status(code: code)) } - public enum Code: Equatable, Hashable { + public enum Code: Equatable, Hashable, Sendable { case `default` case range(Range) case status(code: Int) } - public enum Range: String { + public enum Range: String, Sendable { /// Status Code `100-199` case information = "1XX" /// Status Code `200-299` diff --git a/Sources/OpenAPIKitCore/Shared/SecurityScheme.swift b/Sources/OpenAPIKitCore/Shared/SecurityScheme.swift index 4c24567b5..e230cea58 100644 --- a/Sources/OpenAPIKitCore/Shared/SecurityScheme.swift +++ b/Sources/OpenAPIKitCore/Shared/SecurityScheme.swift @@ -6,7 +6,7 @@ // extension Shared { - public enum SecuritySchemeLocation: String, Codable, Equatable { + public enum SecuritySchemeLocation: String, Codable, Equatable, Sendable { case query case header case cookie diff --git a/Sources/OpenAPIKitCore/URLTemplate/URLTemplate.swift b/Sources/OpenAPIKitCore/URLTemplate/URLTemplate.swift index 480d09034..164549d3b 100644 --- a/Sources/OpenAPIKitCore/URLTemplate/URLTemplate.swift +++ b/Sources/OpenAPIKitCore/URLTemplate/URLTemplate.swift @@ -41,7 +41,7 @@ import Foundation /// into the template. You can also choose to only replace some of the variables this /// way. /// -public struct URLTemplate: Hashable, RawRepresentable { +public struct URLTemplate: Hashable, RawRepresentable, Sendable { /// The string value of the URL. /// @@ -126,7 +126,7 @@ extension URLTemplate { /// URL Template components are either variables that can take on /// different values depending on the context or they are constant /// unchanging parts of the URL. - public enum Component: Hashable, RawRepresentable { + public enum Component: Hashable, RawRepresentable, Sendable { case variable(name: String) case constant(String) From 02f51055b2580aae44556700c778fcc5781f3e23 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Mon, 27 Jan 2025 19:36:30 -0600 Subject: [PATCH 094/110] fixing tests --- Tests/AnyCodableTests/AnyCodableTests.swift | 10 ++-- .../ComponentErrorTests.swift | 2 +- .../DocumentErrorTests.swift | 2 +- .../Helpers.swift | 2 +- .../JSONReferenceErrorTests.swift | 2 +- .../OperationErrorTests.swift | 2 +- .../PathsErrorTests.swift | 2 +- .../RequestContentMapErrorTests.swift | 2 +- .../RequestContentSchemaErrorTests.swift | 2 +- .../RequestErrorTests.swift | 2 +- .../ResponseErrorTests.swift | 2 +- .../SchemaErrorTests.swift | 2 +- .../SecuritySchemeErrorTests.swift | 2 +- .../GitHubAPITests.swift | 2 +- .../GoogleBooksAPITests.swift | 2 +- .../PetStoreAPITests.swift | 2 +- .../SwaggerDocSamplesTests.swift | 2 +- .../TomTomAPITests.swift | 2 +- Tests/OpenAPIKit30Tests/ComponentsTests.swift | 4 +- .../Content/ContentTests.swift | 8 +-- .../Document/DocumentInfoTests.swift | 12 ++-- .../Document/DocumentTests.swift | 4 +- .../ExternalDereferencingDocumentTests.swift | 2 +- .../Operation/OperationTests.swift | 6 +- .../Parameter/ParameterTests.swift | 4 +- .../Path Item/PathItemTests.swift | 4 +- .../Schema Object/JSONSchemaTests.swift | 60 +++++++++---------- .../Schema Object/SchemaObjectYamsTests.swift | 2 +- Tests/OpenAPIKit30Tests/ServerTests.swift | 4 +- Tests/OpenAPIKit30Tests/TestHelpers.swift | 2 +- .../Validator/ValidatorTests.swift | 48 +++++++-------- .../VendorExtendableTests.swift | 16 ++--- .../ComponentErrorTests.swift | 2 +- .../DocumentErrorTests.swift | 2 +- .../Helpers.swift | 2 +- .../JSONReferenceErrorTests.swift | 2 +- .../OperationErrorTests.swift | 2 +- .../PathsErrorTests.swift | 2 +- .../RequestContentMapErrorTests.swift | 2 +- .../RequestContentSchemaErrorTests.swift | 2 +- .../RequestErrorTests.swift | 2 +- .../ResponseErrorTests.swift | 2 +- .../SchemaErrorTests.swift | 2 +- .../SecuritySchemeErrorTests.swift | 2 +- .../TemplateAPITests.swift | 2 +- Tests/OpenAPIKitTests/ComponentsTests.swift | 4 +- .../Content/ContentTests.swift | 8 +-- .../Document/DocumentInfoTests.swift | 12 ++-- .../Document/DocumentTests.swift | 4 +- .../ExternalDereferencingDocumentTests.swift | 2 +- .../Operation/OperationTests.swift | 6 +- .../Parameter/ParameterTests.swift | 4 +- .../Path Item/PathItemTests.swift | 4 +- .../Schema Object/JSONSchemaTests.swift | 58 +++++++++--------- .../Schema Object/SchemaObjectYamsTests.swift | 2 +- Tests/OpenAPIKitTests/ServerTests.swift | 4 +- Tests/OpenAPIKitTests/TestHelpers.swift | 2 +- .../Validator/BuiltinValidationTests.swift | 8 +-- .../Validator/ValidatorTests.swift | 48 +++++++-------- .../VendorExtendableTests.swift | 16 ++--- .../OrderedDictionaryTests.swift | 2 +- 61 files changed, 215 insertions(+), 215 deletions(-) diff --git a/Tests/AnyCodableTests/AnyCodableTests.swift b/Tests/AnyCodableTests/AnyCodableTests.swift index 98f09913f..6dfde77c5 100644 --- a/Tests/AnyCodableTests/AnyCodableTests.swift +++ b/Tests/AnyCodableTests/AnyCodableTests.swift @@ -9,8 +9,8 @@ class AnyCodableTests: XCTestCase { let _: AnyCodable = 10 let _: AnyCodable = 3.4 let _: AnyCodable = "hello" - let _: AnyCodable = ["hi", "there"] - let _: AnyCodable = ["hi": "there"] + let _: AnyCodable = .init(["hi", "there"]) + let _: AnyCodable = .init(["hi": "there"]) } func testEquality() throws { @@ -153,12 +153,12 @@ class AnyCodableTests: XCTestCase { "boolean": true, "integer": 1, "string": "string", - "array": [1, 2, 3], - "nested": [ + "array": .init([1, 2, 3]), + "nested": .init([ "a": "alpha", "b": "bravo", "c": "charlie", - ], + ]), ] let result = try testStringFromEncoding(of: dictionary) diff --git a/Tests/OpenAPIKit30ErrorReportingTests/ComponentErrorTests.swift b/Tests/OpenAPIKit30ErrorReportingTests/ComponentErrorTests.swift index a33ed3ca0..53f247b55 100644 --- a/Tests/OpenAPIKit30ErrorReportingTests/ComponentErrorTests.swift +++ b/Tests/OpenAPIKit30ErrorReportingTests/ComponentErrorTests.swift @@ -8,7 +8,7 @@ import Foundation import XCTest import OpenAPIKit30 -import Yams +@preconcurrency import Yams final class ComponentErrorTests: XCTestCase { diff --git a/Tests/OpenAPIKit30ErrorReportingTests/DocumentErrorTests.swift b/Tests/OpenAPIKit30ErrorReportingTests/DocumentErrorTests.swift index ef2b434b6..919c52132 100644 --- a/Tests/OpenAPIKit30ErrorReportingTests/DocumentErrorTests.swift +++ b/Tests/OpenAPIKit30ErrorReportingTests/DocumentErrorTests.swift @@ -8,7 +8,7 @@ import Foundation import XCTest import OpenAPIKit30 -import Yams +@preconcurrency import Yams final class DocumentErrorTests: XCTestCase { diff --git a/Tests/OpenAPIKit30ErrorReportingTests/Helpers.swift b/Tests/OpenAPIKit30ErrorReportingTests/Helpers.swift index f067827ec..2a3cb4e13 100644 --- a/Tests/OpenAPIKit30ErrorReportingTests/Helpers.swift +++ b/Tests/OpenAPIKit30ErrorReportingTests/Helpers.swift @@ -6,6 +6,6 @@ // import Foundation -import Yams +@preconcurrency import Yams let testDecoder = YAMLDecoder() diff --git a/Tests/OpenAPIKit30ErrorReportingTests/JSONReferenceErrorTests.swift b/Tests/OpenAPIKit30ErrorReportingTests/JSONReferenceErrorTests.swift index a35f01cc4..e6acb4d05 100644 --- a/Tests/OpenAPIKit30ErrorReportingTests/JSONReferenceErrorTests.swift +++ b/Tests/OpenAPIKit30ErrorReportingTests/JSONReferenceErrorTests.swift @@ -8,7 +8,7 @@ import Foundation import XCTest import OpenAPIKit30 -import Yams +@preconcurrency import Yams final class JSONReferenceErrorTests: XCTestCase { func test_referenceFailedToParse() { diff --git a/Tests/OpenAPIKit30ErrorReportingTests/OperationErrorTests.swift b/Tests/OpenAPIKit30ErrorReportingTests/OperationErrorTests.swift index 805273faa..94f876bb6 100644 --- a/Tests/OpenAPIKit30ErrorReportingTests/OperationErrorTests.swift +++ b/Tests/OpenAPIKit30ErrorReportingTests/OperationErrorTests.swift @@ -8,7 +8,7 @@ import Foundation import XCTest import OpenAPIKit30 -import Yams +@preconcurrency import Yams final class OperationErrorTests: XCTestCase { func test_missingResponses() { diff --git a/Tests/OpenAPIKit30ErrorReportingTests/PathsErrorTests.swift b/Tests/OpenAPIKit30ErrorReportingTests/PathsErrorTests.swift index c69c553cd..bba441e24 100644 --- a/Tests/OpenAPIKit30ErrorReportingTests/PathsErrorTests.swift +++ b/Tests/OpenAPIKit30ErrorReportingTests/PathsErrorTests.swift @@ -8,7 +8,7 @@ import Foundation import XCTest import OpenAPIKit30 -import Yams +@preconcurrency import Yams final class PathsErrorTests: XCTestCase { func test_missingPaths() { diff --git a/Tests/OpenAPIKit30ErrorReportingTests/RequestContentMapErrorTests.swift b/Tests/OpenAPIKit30ErrorReportingTests/RequestContentMapErrorTests.swift index c68ba6bf7..75ac121c0 100644 --- a/Tests/OpenAPIKit30ErrorReportingTests/RequestContentMapErrorTests.swift +++ b/Tests/OpenAPIKit30ErrorReportingTests/RequestContentMapErrorTests.swift @@ -8,7 +8,7 @@ import Foundation import XCTest import OpenAPIKit30 -import Yams +@preconcurrency import Yams final class RequestContentMapErrorTests: XCTestCase { /** diff --git a/Tests/OpenAPIKit30ErrorReportingTests/RequestContentSchemaErrorTests.swift b/Tests/OpenAPIKit30ErrorReportingTests/RequestContentSchemaErrorTests.swift index 45ec737d3..6aec769f8 100644 --- a/Tests/OpenAPIKit30ErrorReportingTests/RequestContentSchemaErrorTests.swift +++ b/Tests/OpenAPIKit30ErrorReportingTests/RequestContentSchemaErrorTests.swift @@ -8,7 +8,7 @@ import Foundation import XCTest import OpenAPIKit30 -import Yams +@preconcurrency import Yams final class RequestContentSchemaErrorTests: XCTestCase { func test_wrongTypeContentSchemaTypeProperty() { diff --git a/Tests/OpenAPIKit30ErrorReportingTests/RequestErrorTests.swift b/Tests/OpenAPIKit30ErrorReportingTests/RequestErrorTests.swift index 0289fc510..6cb3a10c4 100644 --- a/Tests/OpenAPIKit30ErrorReportingTests/RequestErrorTests.swift +++ b/Tests/OpenAPIKit30ErrorReportingTests/RequestErrorTests.swift @@ -8,7 +8,7 @@ import Foundation import XCTest import OpenAPIKit30 -import Yams +@preconcurrency import Yams final class RequestErrorTests: XCTestCase { func test_wrongTypeRequest() { diff --git a/Tests/OpenAPIKit30ErrorReportingTests/ResponseErrorTests.swift b/Tests/OpenAPIKit30ErrorReportingTests/ResponseErrorTests.swift index 59c33d57a..2d7552afd 100644 --- a/Tests/OpenAPIKit30ErrorReportingTests/ResponseErrorTests.swift +++ b/Tests/OpenAPIKit30ErrorReportingTests/ResponseErrorTests.swift @@ -8,7 +8,7 @@ import Foundation import XCTest import OpenAPIKit30 -import Yams +@preconcurrency import Yams final class ResponseErrorTests: XCTestCase { func test_headerWithContentAndSchema() { diff --git a/Tests/OpenAPIKit30ErrorReportingTests/SchemaErrorTests.swift b/Tests/OpenAPIKit30ErrorReportingTests/SchemaErrorTests.swift index 90f2b197d..3667ed4fc 100644 --- a/Tests/OpenAPIKit30ErrorReportingTests/SchemaErrorTests.swift +++ b/Tests/OpenAPIKit30ErrorReportingTests/SchemaErrorTests.swift @@ -8,7 +8,7 @@ import Foundation import XCTest import OpenAPIKit30 -import Yams +@preconcurrency import Yams final class SchemaErrorTests: XCTestCase { func test_nonIntegerMaximumForIntegerSchema() { diff --git a/Tests/OpenAPIKit30ErrorReportingTests/SecuritySchemeErrorTests.swift b/Tests/OpenAPIKit30ErrorReportingTests/SecuritySchemeErrorTests.swift index 59989c334..ab03977fc 100644 --- a/Tests/OpenAPIKit30ErrorReportingTests/SecuritySchemeErrorTests.swift +++ b/Tests/OpenAPIKit30ErrorReportingTests/SecuritySchemeErrorTests.swift @@ -8,7 +8,7 @@ import Foundation import XCTest import OpenAPIKit30 -import Yams +@preconcurrency import Yams final class SecuritySchemeErrorTests: XCTestCase { func test_missingSecuritySchemeError() { diff --git a/Tests/OpenAPIKit30RealSpecSuite/GitHubAPITests.swift b/Tests/OpenAPIKit30RealSpecSuite/GitHubAPITests.swift index b0d0229f5..3a23f3c1a 100644 --- a/Tests/OpenAPIKit30RealSpecSuite/GitHubAPITests.swift +++ b/Tests/OpenAPIKit30RealSpecSuite/GitHubAPITests.swift @@ -7,7 +7,7 @@ import XCTest import OpenAPIKit30 -import Yams +@preconcurrency import Yams import Foundation #if canImport(FoundationNetworking) import FoundationNetworking diff --git a/Tests/OpenAPIKit30RealSpecSuite/GoogleBooksAPITests.swift b/Tests/OpenAPIKit30RealSpecSuite/GoogleBooksAPITests.swift index 6dd077c22..76ccce43e 100644 --- a/Tests/OpenAPIKit30RealSpecSuite/GoogleBooksAPITests.swift +++ b/Tests/OpenAPIKit30RealSpecSuite/GoogleBooksAPITests.swift @@ -7,7 +7,7 @@ import XCTest import OpenAPIKit30 -import Yams +@preconcurrency import Yams import Foundation #if canImport(FoundationNetworking) import FoundationNetworking diff --git a/Tests/OpenAPIKit30RealSpecSuite/PetStoreAPITests.swift b/Tests/OpenAPIKit30RealSpecSuite/PetStoreAPITests.swift index d33d12aa8..c0ee2a65b 100644 --- a/Tests/OpenAPIKit30RealSpecSuite/PetStoreAPITests.swift +++ b/Tests/OpenAPIKit30RealSpecSuite/PetStoreAPITests.swift @@ -7,7 +7,7 @@ import XCTest import OpenAPIKit30 -import Yams +@preconcurrency import Yams import Foundation #if canImport(FoundationNetworking) import FoundationNetworking diff --git a/Tests/OpenAPIKit30RealSpecSuite/SwaggerDocSamplesTests.swift b/Tests/OpenAPIKit30RealSpecSuite/SwaggerDocSamplesTests.swift index 1344f66b9..c4455dd69 100644 --- a/Tests/OpenAPIKit30RealSpecSuite/SwaggerDocSamplesTests.swift +++ b/Tests/OpenAPIKit30RealSpecSuite/SwaggerDocSamplesTests.swift @@ -8,7 +8,7 @@ import Foundation import XCTest import OpenAPIKit30 -import Yams +@preconcurrency import Yams final class SwaggerDocSamplesTests: XCTestCase { func test_allOfExample() throws { diff --git a/Tests/OpenAPIKit30RealSpecSuite/TomTomAPITests.swift b/Tests/OpenAPIKit30RealSpecSuite/TomTomAPITests.swift index 00e338422..1f717e23f 100644 --- a/Tests/OpenAPIKit30RealSpecSuite/TomTomAPITests.swift +++ b/Tests/OpenAPIKit30RealSpecSuite/TomTomAPITests.swift @@ -7,7 +7,7 @@ import XCTest import OpenAPIKit30 -import Yams +@preconcurrency import Yams import Foundation #if canImport(FoundationNetworking) import FoundationNetworking diff --git a/Tests/OpenAPIKit30Tests/ComponentsTests.swift b/Tests/OpenAPIKit30Tests/ComponentsTests.swift index 957ebcb2e..bbdd577a7 100644 --- a/Tests/OpenAPIKit30Tests/ComponentsTests.swift +++ b/Tests/OpenAPIKit30Tests/ComponentsTests.swift @@ -309,7 +309,7 @@ extension ComponentsTests { ) ] ], - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) let encoded = try orderUnstableTestStringFromEncoding(of: t1) @@ -504,7 +504,7 @@ extension ComponentsTests { ) ] ], - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) ) } diff --git a/Tests/OpenAPIKit30Tests/Content/ContentTests.swift b/Tests/OpenAPIKit30Tests/Content/ContentTests.swift index b6c92b549..5d5ee4671 100644 --- a/Tests/OpenAPIKit30Tests/Content/ContentTests.swift +++ b/Tests/OpenAPIKit30Tests/Content/ContentTests.swift @@ -205,7 +205,7 @@ extension ContentTests { func test_exampleAndSchemaContent_encode() { let content = OpenAPI.Content(schema: .init(.object(properties: ["hello": .string])), - example: [ "hello": "world" ]) + example: .init([ "hello": "world" ])) let encodedContent = try! orderUnstableTestStringFromEncoding(of: content) assertJSONEquivalent( @@ -260,7 +260,7 @@ extension ContentTests { func test_examplesAndSchemaContent_encode() { let content = OpenAPI.Content(schema: .init(.object(properties: ["hello": .string])), - examples: ["hello": .b(OpenAPI.Example(value: .init([ "hello": "world" ])))]) + examples: ["hello": .b(OpenAPI.Example(value: .init(.init([ "hello": "world" ]))))]) let encodedContent = try! orderUnstableTestStringFromEncoding(of: content) assertJSONEquivalent( @@ -405,7 +405,7 @@ extension ContentTests { func test_vendorExtensions_encode() { let content = OpenAPI.Content( schema: .init(.string), - vendorExtensions: [ "x-hello": [ "world": 123 ] ] + vendorExtensions: [ "x-hello": .init([ "world": 123 ]) ] ) let encodedContent = try! orderUnstableTestStringFromEncoding(of: content) @@ -428,7 +428,7 @@ extension ContentTests { func test_vendorExtensions_encode_fixKey() { let content = OpenAPI.Content( schema: .init(.string), - vendorExtensions: [ "hello": [ "world": 123 ] ] + vendorExtensions: [ "hello": .init([ "world": 123 ]) ] ) let encodedContent = try! orderUnstableTestStringFromEncoding(of: content) diff --git a/Tests/OpenAPIKit30Tests/Document/DocumentInfoTests.swift b/Tests/OpenAPIKit30Tests/Document/DocumentInfoTests.swift index 4483be005..9533de090 100644 --- a/Tests/OpenAPIKit30Tests/Document/DocumentInfoTests.swift +++ b/Tests/OpenAPIKit30Tests/Document/DocumentInfoTests.swift @@ -103,7 +103,7 @@ extension DocumentInfoTests { let license = OpenAPI.Document.Info.License( name: "MIT", url: URL(string: "http://website.com")!, - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) let encodedLicense = try orderUnstableTestStringFromEncoding(of: license) @@ -142,7 +142,7 @@ extension DocumentInfoTests { OpenAPI.Document.Info.License( name: "MIT", url: URL(string: "http://website.com")!, - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) ) } @@ -240,7 +240,7 @@ extension DocumentInfoTests { func test_contact_vendorExtensions_encode() throws { let contact = OpenAPI.Document.Info.Contact( email: "email", - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) let encodedContact = try orderUnstableTestStringFromEncoding(of: contact) @@ -276,7 +276,7 @@ extension DocumentInfoTests { contact, .init( email: "email", - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) ) } @@ -508,7 +508,7 @@ extension DocumentInfoTests { title: "title", license: .init(name: "license"), version: "1.0", - vendorExtensions: ["x-speacialFeature": ["hello", "world"]] + vendorExtensions: ["x-speacialFeature": .init(["hello", "world"])] ) let encodedInfo = try orderUnstableTestStringFromEncoding(of: info) @@ -554,7 +554,7 @@ extension DocumentInfoTests { title: "title", license: .init(name: "license"), version: "1.0", - vendorExtensions: ["x-speacialFeature": ["hello", "world"]] + vendorExtensions: ["x-speacialFeature": .init(["hello", "world"])] ) ) } diff --git a/Tests/OpenAPIKit30Tests/Document/DocumentTests.swift b/Tests/OpenAPIKit30Tests/Document/DocumentTests.swift index ccf4d2c55..d46d29b52 100644 --- a/Tests/OpenAPIKit30Tests/Document/DocumentTests.swift +++ b/Tests/OpenAPIKit30Tests/Document/DocumentTests.swift @@ -844,7 +844,7 @@ extension DocumentTests { paths: [:], components: .noComponents, externalDocs: .init(url: URL(string: "http://google.com")!), - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) let encodedDocument = try orderUnstableTestStringFromEncoding(of: document) @@ -903,7 +903,7 @@ extension DocumentTests { paths: [:], components: .noComponents, externalDocs: .init(url: URL(string: "http://google.com")!), - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) ) } diff --git a/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift b/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift index d9863a92c..00af97ac5 100644 --- a/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift +++ b/Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift @@ -3,7 +3,7 @@ // import Foundation -import Yams +@preconcurrency import Yams import OpenAPIKit30 import XCTest diff --git a/Tests/OpenAPIKit30Tests/Operation/OperationTests.swift b/Tests/OpenAPIKit30Tests/Operation/OperationTests.swift index a09c535d7..f773beabc 100644 --- a/Tests/OpenAPIKit30Tests/Operation/OperationTests.swift +++ b/Tests/OpenAPIKit30Tests/Operation/OperationTests.swift @@ -7,7 +7,7 @@ import XCTest import OpenAPIKit30 -import Yams +@preconcurrency import Yams final class OperationTests: XCTestCase { func test_init() { @@ -126,7 +126,7 @@ extension OperationTests { deprecated: true, security: [[.component(named: "security"): []]], servers: [.init(url: URL(string: "https://google.com")!)], - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) let encodedOperation = try orderUnstableTestStringFromEncoding(of: operation) @@ -298,7 +298,7 @@ extension OperationTests { deprecated: true, security: [[.component(named: "security"): []]], servers: [.init(url: URL(string: "https://google.com")!)], - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) ) diff --git a/Tests/OpenAPIKit30Tests/Parameter/ParameterTests.swift b/Tests/OpenAPIKit30Tests/Parameter/ParameterTests.swift index 5a263a46d..58e28d611 100644 --- a/Tests/OpenAPIKit30Tests/Parameter/ParameterTests.swift +++ b/Tests/OpenAPIKit30Tests/Parameter/ParameterTests.swift @@ -827,7 +827,7 @@ extension ParameterTests { context: .path, schema: .string, description: "world", - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) let encodedParameter = try orderUnstableTestStringFromEncoding(of: parameter) @@ -880,7 +880,7 @@ extension ParameterTests { context: .path, schema: .string, description: "world", - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) ) } diff --git a/Tests/OpenAPIKit30Tests/Path Item/PathItemTests.swift b/Tests/OpenAPIKit30Tests/Path Item/PathItemTests.swift index aee98705d..7a03c7578 100644 --- a/Tests/OpenAPIKit30Tests/Path Item/PathItemTests.swift +++ b/Tests/OpenAPIKit30Tests/Path Item/PathItemTests.swift @@ -175,7 +175,7 @@ extension PathItemTests { description: "description", servers: [OpenAPI.Server(url: URL(string: "http://google.com")!)], parameters: [.parameter(name: "hello", context: .query, schema: .string)], - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) let encodedPathItem = try orderUnstableTestStringFromEncoding(of: pathItem) @@ -245,7 +245,7 @@ extension PathItemTests { description: "description", servers: [OpenAPI.Server(url: URL(string: "http://google.com")!)], parameters: [.parameter(name: "hello", context: .query, schema: .string)], - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) ) } diff --git a/Tests/OpenAPIKit30Tests/Schema Object/JSONSchemaTests.swift b/Tests/OpenAPIKit30Tests/Schema Object/JSONSchemaTests.swift index 18dda979f..8d5319e63 100644 --- a/Tests/OpenAPIKit30Tests/Schema Object/JSONSchemaTests.swift +++ b/Tests/OpenAPIKit30Tests/Schema Object/JSONSchemaTests.swift @@ -1057,15 +1057,15 @@ final class SchemaObjectTests: XCTestCase { func test_withInitalAllowedValues() { let boolean = JSONSchema.boolean(.init(format: .unspecified, required: true, allowedValues: [false])) - let object = JSONSchema.object(.init(format: .unspecified, required: true, allowedValues: [[:]]), .init(properties: [:])) - let array = JSONSchema.array(.init(format: .unspecified, required: true, allowedValues: [[false]]), .init(items: .boolean(.init(format: .unspecified, required: true)))) + let object = JSONSchema.object(.init(format: .unspecified, required: true, allowedValues: [.init([:])]), .init(properties: [:])) + let array = JSONSchema.array(.init(format: .unspecified, required: true, allowedValues: [.init([false])]), .init(items: .boolean(.init(format: .unspecified, required: true)))) let number = JSONSchema.number(.init(format: .unspecified, required: true, allowedValues: [2.5]), .init()) let integer = JSONSchema.integer(.init(format: .unspecified, required: true, allowedValues: [5]), .init()) let string = JSONSchema.string(.init(format: .unspecified, required: true, allowedValues: ["hello"]), .init()) let fragment = JSONSchema.fragment(.init(allowedValues: [false])) XCTAssertEqual(boolean.allowedValues, [false]) - XCTAssertEqual(object.allowedValues, [[:]]) + XCTAssertEqual(object.allowedValues, [.init([:])]) XCTAssertEqual(array.allowedValues?[0].value as! [Bool], [false]) XCTAssertEqual(number.allowedValues, [2.5]) XCTAssertEqual(integer.allowedValues, [5]) @@ -1077,9 +1077,9 @@ final class SchemaObjectTests: XCTestCase { let boolean = JSONSchema.boolean(.init(format: .unspecified, required: true)) .with(allowedValues: [false]) let object = JSONSchema.object(.init(format: .unspecified, required: true), .init(properties: [:])) - .with(allowedValues: [[:]]) + .with(allowedValues: [.init([:])]) let array = JSONSchema.array(.init(format: .unspecified, required: true), .init(items: .boolean(.init(format: .unspecified, required: true)))) - .with(allowedValues: [[false]]) + .with(allowedValues: [.init([false])]) let number = JSONSchema.number(.init(format: .unspecified, required: true), .init()) .with(allowedValues: [2.5]) let integer = JSONSchema.integer(.init(format: .unspecified, required: true), .init()) @@ -1118,16 +1118,16 @@ final class SchemaObjectTests: XCTestCase { func test_withInitalDefaultValue() { let boolean = JSONSchema.boolean(.init(format: .unspecified, required: true, defaultValue: false)) - let object = JSONSchema.object(.init(format: .unspecified, required: true, defaultValue: [:]), .init(properties: [:])) - let array = JSONSchema.array(.init(format: .unspecified, required: true, defaultValue: [false]), .init(items: .boolean(.init(format: .unspecified, required: true)))) + let object = JSONSchema.object(.init(format: .unspecified, required: true, defaultValue: .init([:])), .init(properties: [:])) + let array = JSONSchema.array(.init(format: .unspecified, required: true, defaultValue: .init([false])), .init(items: .boolean(.init(format: .unspecified, required: true)))) let number = JSONSchema.number(.init(format: .unspecified, required: true, defaultValue: 2.5), .init()) let integer = JSONSchema.integer(.init(format: .unspecified, required: true, defaultValue: 5), .init()) let string = JSONSchema.string(.init(format: .unspecified, required: true, defaultValue: "hello"), .init()) let fragment = JSONSchema.fragment(.init(defaultValue: false)) XCTAssertEqual(boolean.defaultValue, false) - XCTAssertEqual(object.defaultValue, [:]) - XCTAssertEqual(array.defaultValue, [false]) + XCTAssertEqual(object.defaultValue, .init([:])) + XCTAssertEqual(array.defaultValue, .init([false])) XCTAssertEqual(number.defaultValue, 2.5) XCTAssertEqual(integer.defaultValue, 5) XCTAssertEqual(string.defaultValue, "hello") @@ -1138,9 +1138,9 @@ final class SchemaObjectTests: XCTestCase { let boolean = JSONSchema.boolean(.init(format: .unspecified, required: true)) .with(defaultValue: false) let object = JSONSchema.object(.init(format: .unspecified, required: true), .init(properties: [:])) - .with(defaultValue: [:]) + .with(defaultValue: .init([:])) let array = JSONSchema.array(.init(format: .unspecified, required: true), .init(items: .boolean(.init(format: .unspecified, required: true)))) - .with(defaultValue: [false]) + .with(defaultValue: .init([false])) let number = JSONSchema.number(.init(format: .unspecified, required: true), .init()) .with(defaultValue: 2.5) let integer = JSONSchema.integer(.init(format: .unspecified, required: true), .init()) @@ -1163,7 +1163,7 @@ final class SchemaObjectTests: XCTestCase { XCTAssertEqual(boolean.defaultValue, false) XCTAssertEqual(object.defaultValue, AnyCodable([String: String]())) - XCTAssertEqual(array.defaultValue, [false]) + XCTAssertEqual(array.defaultValue, .init([false])) XCTAssertEqual(number.defaultValue, 2.5) XCTAssertEqual(integer.defaultValue, 5) XCTAssertEqual(string.defaultValue, "hello") @@ -1178,7 +1178,7 @@ final class SchemaObjectTests: XCTestCase { } func test_withInitialExample() { - let object = JSONSchema.object(.init(format: .unspecified, required: true, example: [:]), .init(properties: [:])) + let object = JSONSchema.object(.init(format: .unspecified, required: true, example: .init([:])), .init(properties: [:])) let fragment = JSONSchema.fragment(.init(example: "hi")) // nonsense @@ -1202,7 +1202,7 @@ final class SchemaObjectTests: XCTestCase { let object = try JSONSchema.object(.init(format: .unspecified, required: true), .init(properties: [:])) .with(example: AnyCodable([String: String]())) let array = try JSONSchema.array(.init(), .init()) - .with(example: ["hello"]) + .with(example: .init(["hello"])) let boolean = try JSONSchema.boolean(.init(format: .unspecified, required: true)) .with(example: true) @@ -1216,17 +1216,17 @@ final class SchemaObjectTests: XCTestCase { .with(example: "hello world") let allOf = try JSONSchema.all(of: [.string(.init(), .init())]) - .with(example: ["hello"]) + .with(example: .init(["hello"])) let anyOf = try JSONSchema.any(of: [object]) - .with(example: ["hello"]) + .with(example: .init(["hello"])) let oneOf = try JSONSchema.one(of: [object]) - .with(example: ["hello"]) + .with(example: .init(["hello"])) let not = try JSONSchema.not(object) - .with(example: ["hello"]) + .with(example: .init(["hello"])) let fragment = try JSONSchema.fragment(.init()).with(example: "hi") XCTAssertThrowsError(try JSONSchema.reference(.external(URL(string: "hello/world.json#/hello")!)) - .with(example: ["hello"])) + .with(example: .init(["hello"]))) XCTAssertEqual(object.example?.value as? [String: String], [:]) XCTAssertEqual(array.example?.value as? [String], ["hello"]) @@ -1812,7 +1812,7 @@ extension SchemaObjectTests { XCTAssertEqual(deprecatedObject, JSONSchema.object(.init(format: .generic, deprecated: true), .init(properties: [:]))) XCTAssertEqual(allowedValueObject.allowedValues?[0].value as! [String: Bool], ["hello": false]) XCTAssertEqual(allowedValueObject.jsonTypeFormat, .object(.generic)) - XCTAssertEqual(defaultValueObject.defaultValue, ["hello": false]) + XCTAssertEqual(defaultValueObject.defaultValue, .init(["hello": false])) XCTAssertEqual(discriminatorObject, JSONSchema.object(discriminator: .init(propertyName: "hello"))) guard case let .object(_, contextB) = allowedValueObject.value else { @@ -2749,7 +2749,7 @@ extension SchemaObjectTests { XCTAssertEqual(nullableObject, JSONSchema.object(.init(format: .generic, nullable: true, example: AnyCodable(["hello": true])), .init(properties: [:]))) XCTAssertEqual(allowedValueObject.allowedValues?[0].value as! [String: Bool], ["hello": false]) XCTAssertEqual(allowedValueObject.jsonTypeFormat, .object(.generic)) - XCTAssertEqual(allowedValueObject.example, ["hello" : true]) + XCTAssertEqual(allowedValueObject.example, .init(["hello" : true])) guard case let .object(_, contextB) = allowedValueObject.value else { XCTFail("expected object to be parsed as object") @@ -3137,9 +3137,9 @@ extension SchemaObjectTests { let writeOnlyArray = JSONSchema.array(.init(format: .unspecified, required: true, permissions: .writeOnly), .init()) let deprecatedArray = JSONSchema.array(.init(format: .unspecified, required: true, deprecated: true), .init()) let allowedValueArray = JSONSchema.array(.init(format: .unspecified, required: true), .init()) - .with(allowedValues: [[10]]) + .with(allowedValues: [.init([10])]) let defaultValueArray = JSONSchema.array(.init(format: .unspecified, required: true), .init()) - .with(defaultValue: [10]) + .with(defaultValue: .init([10])) let discriminatorArray = JSONSchema.array(.init(format: .unspecified, required: true, discriminator: .init(propertyName: "hello")), .init()) testAllSharedSimpleContextEncoding( @@ -3190,7 +3190,7 @@ extension SchemaObjectTests { XCTAssertEqual(writeOnlyArray, JSONSchema.array(.init(format: .generic, permissions: .writeOnly), .init())) XCTAssertEqual(deprecatedArray, JSONSchema.array(.init(format: .generic, deprecated: true), .init())) XCTAssertEqual(allowedValueArray.allowedValues?[0].value as! [Bool], [false]) - XCTAssertEqual(defaultValueArray.defaultValue, [false]) + XCTAssertEqual(defaultValueArray.defaultValue, .init([false])) XCTAssertEqual(discriminatorArray, JSONSchema.array(discriminator: .init(propertyName: "hello"))) guard case let .array(_, contextB) = allowedValueArray.value else { @@ -3223,7 +3223,7 @@ extension SchemaObjectTests { let optionalArray = JSONSchema.array(.init(format: .unspecified, required: false), .init(items: .boolean(.init(format: .unspecified, required: false)))) let nullableArray = JSONSchema.array(.init(format: .unspecified, required: true, nullable: true), .init(items: .boolean(.init(format: .unspecified, required: false)))) let allowedValueArray = JSONSchema.array(.init(format: .unspecified, required: true), .init(items: .boolean(.init(format: .unspecified, required: false)))) - .with(allowedValues: [[10]]) + .with(allowedValues: [.init([10])]) testEncodingPropertyLines(entity: requiredArray, propertyLines: [ @@ -3301,7 +3301,7 @@ extension SchemaObjectTests { let optionalArray = JSONSchema.array(.init(format: .unspecified, required: false), .init(uniqueItems: true)) let nullableArray = JSONSchema.array(.init(format: .unspecified, required: true, nullable: true), .init(uniqueItems: true)) let allowedValueArray = JSONSchema.array(.init(format: .unspecified, required: true), .init(uniqueItems: true)) - .with(allowedValues: [[10]]) + .with(allowedValues: [.init([10])]) testEncodingPropertyLines( entity: requiredArray, @@ -3371,7 +3371,7 @@ extension SchemaObjectTests { let optionalArray = JSONSchema.array(.init(format: .unspecified, required: false), .init(maxItems: 2)) let nullableArray = JSONSchema.array(.init(format: .unspecified, required: true, nullable: true), .init(maxItems: 2)) let allowedValueArray = JSONSchema.array(.init(format: .unspecified, required: true), .init(maxItems: 2)) - .with(allowedValues: [[10]]) + .with(allowedValues: [.init([10])]) testEncodingPropertyLines(entity: requiredArray, propertyLines: [ @@ -3429,7 +3429,7 @@ extension SchemaObjectTests { let optionalArray = JSONSchema.array(.init(format: .unspecified, required: false), .init(minItems: 2)) let nullableArray = JSONSchema.array(.init(format: .unspecified, required: true, nullable: true), .init(minItems: 2)) let allowedValueArray = JSONSchema.array(.init(format: .unspecified, required: true), .init(minItems: 2)) - .with(allowedValues: [[10]]) + .with(allowedValues: [.init([10])]) testEncodingPropertyLines(entity: requiredArray, propertyLines: [ @@ -5955,8 +5955,8 @@ extension SchemaObjectTests { "hello": .boolean ], allowedValues: [ - [ "hello": true], - [ "hello": false] + .init([ "hello": true]), + .init([ "hello": false]) ] ) let addProp1 = JSONSchema.object( diff --git a/Tests/OpenAPIKit30Tests/Schema Object/SchemaObjectYamsTests.swift b/Tests/OpenAPIKit30Tests/Schema Object/SchemaObjectYamsTests.swift index 0528c0811..b191ea907 100644 --- a/Tests/OpenAPIKit30Tests/Schema Object/SchemaObjectYamsTests.swift +++ b/Tests/OpenAPIKit30Tests/Schema Object/SchemaObjectYamsTests.swift @@ -13,7 +13,7 @@ import Foundation import XCTest import OpenAPIKit30 -import Yams +@preconcurrency import Yams final class SchemaObjectYamsTests: XCTestCase { func test_floatingPointWholeNumberIntegerDecode() throws { diff --git a/Tests/OpenAPIKit30Tests/ServerTests.swift b/Tests/OpenAPIKit30Tests/ServerTests.swift index ac9638575..0b670697e 100644 --- a/Tests/OpenAPIKit30Tests/ServerTests.swift +++ b/Tests/OpenAPIKit30Tests/ServerTests.swift @@ -170,7 +170,7 @@ extension ServerTests { vendorExtensions: [ "x-otherThing": 1234 ] ) ], - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) ) } @@ -187,7 +187,7 @@ extension ServerTests { vendorExtensions: [ "x-otherThing": 1234 ] ) ], - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) let encodedServer = try orderUnstableTestStringFromEncoding(of: server) diff --git a/Tests/OpenAPIKit30Tests/TestHelpers.swift b/Tests/OpenAPIKit30Tests/TestHelpers.swift index eadd92726..336866d5d 100644 --- a/Tests/OpenAPIKit30Tests/TestHelpers.swift +++ b/Tests/OpenAPIKit30Tests/TestHelpers.swift @@ -6,7 +6,7 @@ // import Foundation -import Yams +@preconcurrency import Yams import XCTest fileprivate let foundationTestEncoder = { () -> JSONEncoder in diff --git a/Tests/OpenAPIKit30Tests/Validator/ValidatorTests.swift b/Tests/OpenAPIKit30Tests/Validator/ValidatorTests.swift index 7ccf0caee..bc3d4555d 100644 --- a/Tests/OpenAPIKit30Tests/Validator/ValidatorTests.swift +++ b/Tests/OpenAPIKit30Tests/Validator/ValidatorTests.swift @@ -85,7 +85,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -112,7 +112,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -144,7 +144,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -180,7 +180,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -212,7 +212,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -242,7 +242,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -278,7 +278,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -312,7 +312,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -324,7 +324,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -360,7 +360,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -396,7 +396,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -433,7 +433,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -470,7 +470,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -506,7 +506,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -688,7 +688,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -722,7 +722,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hiya", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -818,7 +818,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -869,7 +869,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -920,7 +920,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hiya", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -958,7 +958,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hiya", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -1002,7 +1002,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hiya", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -1045,7 +1045,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hiya", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -1335,7 +1335,7 @@ final class ValidatorTests: XCTestCase { vendorExtensions: [ "x-string": "hiya", "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]), "x-float": AnyCodable(22.5 as Float), "x-bool": true @@ -1373,7 +1373,7 @@ final class ValidatorTests: XCTestCase { vendorExtensions: [ "x-string": "hiya", "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]), "x-float": AnyCodable(22.5 as Float), "x-bool": true diff --git a/Tests/OpenAPIKit30Tests/VendorExtendableTests.swift b/Tests/OpenAPIKit30Tests/VendorExtendableTests.swift index 35cfa9e65..229621c29 100644 --- a/Tests/OpenAPIKit30Tests/VendorExtendableTests.swift +++ b/Tests/OpenAPIKit30Tests/VendorExtendableTests.swift @@ -39,13 +39,13 @@ final class VendorExtendableTests: XCTestCase { func test_encodeSuccess() throws { let test = TestStruct(vendorExtensions: [ "x-tension": "hello", - "x-two": [ + "x-two": .init([ "cool", "beans" - ], - "x-three": [ + ]), + "x-three": .init([ "nested": 10 - ] + ]) ]) let _ = try JSONEncoder().encode(test) @@ -85,13 +85,13 @@ extension VendorExtendableTests { func test_encode() throws { let test = TestStruct(vendorExtensions: [ "x-tension": "hello", - "x-two": [ + "x-two": .init([ "cool", "beans" - ], - "x-three": [ + ]), + "x-three": .init([ "nested": 10 - ] + ]) ]) let encoded = try orderUnstableTestStringFromEncoding(of: test) diff --git a/Tests/OpenAPIKitErrorReportingTests/ComponentErrorTests.swift b/Tests/OpenAPIKitErrorReportingTests/ComponentErrorTests.swift index 3c3f726e2..60e010320 100644 --- a/Tests/OpenAPIKitErrorReportingTests/ComponentErrorTests.swift +++ b/Tests/OpenAPIKitErrorReportingTests/ComponentErrorTests.swift @@ -8,7 +8,7 @@ import Foundation import XCTest import OpenAPIKit -import Yams +@preconcurrency import Yams final class ComponentErrorTests: XCTestCase { diff --git a/Tests/OpenAPIKitErrorReportingTests/DocumentErrorTests.swift b/Tests/OpenAPIKitErrorReportingTests/DocumentErrorTests.swift index 25ac8ba44..9d3a03d26 100644 --- a/Tests/OpenAPIKitErrorReportingTests/DocumentErrorTests.swift +++ b/Tests/OpenAPIKitErrorReportingTests/DocumentErrorTests.swift @@ -8,7 +8,7 @@ import Foundation import XCTest import OpenAPIKit -import Yams +@preconcurrency import Yams final class DocumentErrorTests: XCTestCase { diff --git a/Tests/OpenAPIKitErrorReportingTests/Helpers.swift b/Tests/OpenAPIKitErrorReportingTests/Helpers.swift index f067827ec..2a3cb4e13 100644 --- a/Tests/OpenAPIKitErrorReportingTests/Helpers.swift +++ b/Tests/OpenAPIKitErrorReportingTests/Helpers.swift @@ -6,6 +6,6 @@ // import Foundation -import Yams +@preconcurrency import Yams let testDecoder = YAMLDecoder() diff --git a/Tests/OpenAPIKitErrorReportingTests/JSONReferenceErrorTests.swift b/Tests/OpenAPIKitErrorReportingTests/JSONReferenceErrorTests.swift index 413df97d6..66167bd49 100644 --- a/Tests/OpenAPIKitErrorReportingTests/JSONReferenceErrorTests.swift +++ b/Tests/OpenAPIKitErrorReportingTests/JSONReferenceErrorTests.swift @@ -8,7 +8,7 @@ import Foundation import XCTest import OpenAPIKit -import Yams +@preconcurrency import Yams final class JSONReferenceErrorTests: XCTestCase { func test_referenceFailedToParse() { diff --git a/Tests/OpenAPIKitErrorReportingTests/OperationErrorTests.swift b/Tests/OpenAPIKitErrorReportingTests/OperationErrorTests.swift index 1743af2ac..d7e0129e2 100644 --- a/Tests/OpenAPIKitErrorReportingTests/OperationErrorTests.swift +++ b/Tests/OpenAPIKitErrorReportingTests/OperationErrorTests.swift @@ -8,7 +8,7 @@ import Foundation import XCTest import OpenAPIKit -import Yams +@preconcurrency import Yams final class OperationErrorTests: XCTestCase { func test_wrongTypeTags() { diff --git a/Tests/OpenAPIKitErrorReportingTests/PathsErrorTests.swift b/Tests/OpenAPIKitErrorReportingTests/PathsErrorTests.swift index 69ccb32da..e5dd3b760 100644 --- a/Tests/OpenAPIKitErrorReportingTests/PathsErrorTests.swift +++ b/Tests/OpenAPIKitErrorReportingTests/PathsErrorTests.swift @@ -8,7 +8,7 @@ import Foundation import XCTest import OpenAPIKit -import Yams +@preconcurrency import Yams final class PathsErrorTests: XCTestCase { func test_badPathReference() { diff --git a/Tests/OpenAPIKitErrorReportingTests/RequestContentMapErrorTests.swift b/Tests/OpenAPIKitErrorReportingTests/RequestContentMapErrorTests.swift index 6bc8a6ef2..685b52402 100644 --- a/Tests/OpenAPIKitErrorReportingTests/RequestContentMapErrorTests.swift +++ b/Tests/OpenAPIKitErrorReportingTests/RequestContentMapErrorTests.swift @@ -8,7 +8,7 @@ import Foundation import XCTest import OpenAPIKit -import Yams +@preconcurrency import Yams final class RequestContentMapErrorTests: XCTestCase { /** diff --git a/Tests/OpenAPIKitErrorReportingTests/RequestContentSchemaErrorTests.swift b/Tests/OpenAPIKitErrorReportingTests/RequestContentSchemaErrorTests.swift index e6217bfb4..17690166d 100644 --- a/Tests/OpenAPIKitErrorReportingTests/RequestContentSchemaErrorTests.swift +++ b/Tests/OpenAPIKitErrorReportingTests/RequestContentSchemaErrorTests.swift @@ -8,7 +8,7 @@ import Foundation import XCTest import OpenAPIKit -import Yams +@preconcurrency import Yams final class RequestContentSchemaErrorTests: XCTestCase { func test_wrongTypeContentSchemaTypeProperty() { diff --git a/Tests/OpenAPIKitErrorReportingTests/RequestErrorTests.swift b/Tests/OpenAPIKitErrorReportingTests/RequestErrorTests.swift index b8edd5b00..184349ee6 100644 --- a/Tests/OpenAPIKitErrorReportingTests/RequestErrorTests.swift +++ b/Tests/OpenAPIKitErrorReportingTests/RequestErrorTests.swift @@ -8,7 +8,7 @@ import Foundation import XCTest import OpenAPIKit -import Yams +@preconcurrency import Yams final class RequestErrorTests: XCTestCase { func test_wrongTypeRequest() { diff --git a/Tests/OpenAPIKitErrorReportingTests/ResponseErrorTests.swift b/Tests/OpenAPIKitErrorReportingTests/ResponseErrorTests.swift index 91332e3e7..99323a737 100644 --- a/Tests/OpenAPIKitErrorReportingTests/ResponseErrorTests.swift +++ b/Tests/OpenAPIKitErrorReportingTests/ResponseErrorTests.swift @@ -8,7 +8,7 @@ import Foundation import XCTest import OpenAPIKit -import Yams +@preconcurrency import Yams final class ResponseErrorTests: XCTestCase { func test_headerWithContentAndSchema() { diff --git a/Tests/OpenAPIKitErrorReportingTests/SchemaErrorTests.swift b/Tests/OpenAPIKitErrorReportingTests/SchemaErrorTests.swift index cc344e148..ede89a305 100644 --- a/Tests/OpenAPIKitErrorReportingTests/SchemaErrorTests.swift +++ b/Tests/OpenAPIKitErrorReportingTests/SchemaErrorTests.swift @@ -8,7 +8,7 @@ import Foundation import XCTest import OpenAPIKit -import Yams +@preconcurrency import Yams final class SchemaErrorTests: XCTestCase { func test_nonIntegerMaximumForIntegerSchema() { diff --git a/Tests/OpenAPIKitErrorReportingTests/SecuritySchemeErrorTests.swift b/Tests/OpenAPIKitErrorReportingTests/SecuritySchemeErrorTests.swift index 5e26ad145..f5f2e2081 100644 --- a/Tests/OpenAPIKitErrorReportingTests/SecuritySchemeErrorTests.swift +++ b/Tests/OpenAPIKitErrorReportingTests/SecuritySchemeErrorTests.swift @@ -8,7 +8,7 @@ import Foundation import XCTest import OpenAPIKit -import Yams +@preconcurrency import Yams final class SecuritySchemeErrorTests: XCTestCase { func test_missingSecuritySchemeError() { diff --git a/Tests/OpenAPIKitRealSpecSuite/TemplateAPITests.swift b/Tests/OpenAPIKitRealSpecSuite/TemplateAPITests.swift index c9dd6ebff..bfbda7d5d 100644 --- a/Tests/OpenAPIKitRealSpecSuite/TemplateAPITests.swift +++ b/Tests/OpenAPIKitRealSpecSuite/TemplateAPITests.swift @@ -17,7 +17,7 @@ import XCTest import OpenAPIKit -import Yams +@preconcurrency import Yams import Foundation #if canImport(FoundationNetworking) import FoundationNetworking diff --git a/Tests/OpenAPIKitTests/ComponentsTests.swift b/Tests/OpenAPIKitTests/ComponentsTests.swift index bf1f7e776..00c5b10bd 100644 --- a/Tests/OpenAPIKitTests/ComponentsTests.swift +++ b/Tests/OpenAPIKitTests/ComponentsTests.swift @@ -319,7 +319,7 @@ extension ComponentsTests { pathItems: [ "ten": .init(get: .init(responses: [200: .response(description: "response")])) ], - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) let encoded = try orderUnstableTestStringFromEncoding(of: t1) @@ -541,7 +541,7 @@ extension ComponentsTests { pathItems: [ "ten": .init(get: .init(responses: [200: .response(description: "response")])) ], - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) ) } diff --git a/Tests/OpenAPIKitTests/Content/ContentTests.swift b/Tests/OpenAPIKitTests/Content/ContentTests.swift index 300bf67ee..583cc44b0 100644 --- a/Tests/OpenAPIKitTests/Content/ContentTests.swift +++ b/Tests/OpenAPIKitTests/Content/ContentTests.swift @@ -205,7 +205,7 @@ extension ContentTests { func test_exampleAndSchemaContent_encode() { let content = OpenAPI.Content(schema: .init(.object(properties: ["hello": .string])), - example: [ "hello": "world" ]) + example: .init([ "hello": "world" ])) let encodedContent = try! orderUnstableTestStringFromEncoding(of: content) assertJSONEquivalent( @@ -260,7 +260,7 @@ extension ContentTests { func test_examplesAndSchemaContent_encode() { let content = OpenAPI.Content(schema: .init(.object(properties: ["hello": .string])), - examples: ["hello": .b(OpenAPI.Example(value: .init([ "hello": "world" ])))]) + examples: ["hello": .b(OpenAPI.Example(value: .init(.init([ "hello": "world" ]))))]) let encodedContent = try! orderUnstableTestStringFromEncoding(of: content) assertJSONEquivalent( @@ -405,7 +405,7 @@ extension ContentTests { func test_vendorExtensions_encode() { let content = OpenAPI.Content( schema: .init(.string), - vendorExtensions: [ "x-hello": [ "world": 123 ] ] + vendorExtensions: [ "x-hello": .init([ "world": 123 ]) ] ) let encodedContent = try! orderUnstableTestStringFromEncoding(of: content) @@ -428,7 +428,7 @@ extension ContentTests { func test_vendorExtensions_encode_fixKey() { let content = OpenAPI.Content( schema: .init(.string), - vendorExtensions: [ "hello": [ "world": 123 ] ] + vendorExtensions: [ "hello": .init([ "world": 123 ]) ] ) let encodedContent = try! orderUnstableTestStringFromEncoding(of: content) diff --git a/Tests/OpenAPIKitTests/Document/DocumentInfoTests.swift b/Tests/OpenAPIKitTests/Document/DocumentInfoTests.swift index 298e7ee58..6106b934e 100644 --- a/Tests/OpenAPIKitTests/Document/DocumentInfoTests.swift +++ b/Tests/OpenAPIKitTests/Document/DocumentInfoTests.swift @@ -137,7 +137,7 @@ extension DocumentInfoTests { let license = OpenAPI.Document.Info.License( name: "MIT", url: URL(string: "http://website.com")!, - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) let encodedLicense = try orderUnstableTestStringFromEncoding(of: license) @@ -176,7 +176,7 @@ extension DocumentInfoTests { OpenAPI.Document.Info.License( name: "MIT", url: URL(string: "http://website.com")!, - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) ) } @@ -274,7 +274,7 @@ extension DocumentInfoTests { func test_contact_vendorExtensions_encode() throws { let contact = OpenAPI.Document.Info.Contact( email: "email", - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) let encodedContact = try orderUnstableTestStringFromEncoding(of: contact) @@ -310,7 +310,7 @@ extension DocumentInfoTests { contact, .init( email: "email", - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) ) } @@ -584,7 +584,7 @@ extension DocumentInfoTests { title: "title", license: .init(name: "license"), version: "1.0", - vendorExtensions: ["x-speacialFeature": ["hello", "world"]] + vendorExtensions: ["x-speacialFeature": .init(["hello", "world"])] ) let encodedInfo = try orderUnstableTestStringFromEncoding(of: info) @@ -630,7 +630,7 @@ extension DocumentInfoTests { title: "title", license: .init(name: "license"), version: "1.0", - vendorExtensions: ["x-speacialFeature": ["hello", "world"]] + vendorExtensions: ["x-speacialFeature": .init(["hello", "world"])] ) ) } diff --git a/Tests/OpenAPIKitTests/Document/DocumentTests.swift b/Tests/OpenAPIKitTests/Document/DocumentTests.swift index f66e4ecb0..70d632195 100644 --- a/Tests/OpenAPIKitTests/Document/DocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/DocumentTests.swift @@ -818,7 +818,7 @@ extension DocumentTests { paths: [:], components: .noComponents, externalDocs: .init(url: URL(string: "http://google.com")!), - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) let encodedDocument = try orderUnstableTestStringFromEncoding(of: document) @@ -874,7 +874,7 @@ extension DocumentTests { paths: [:], components: .noComponents, externalDocs: .init(url: URL(string: "http://google.com")!), - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) ) } diff --git a/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift b/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift index 3aa95b8f0..4f977a3d2 100644 --- a/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift @@ -3,7 +3,7 @@ // import Foundation -import Yams +@preconcurrency import Yams import OpenAPIKit import XCTest diff --git a/Tests/OpenAPIKitTests/Operation/OperationTests.swift b/Tests/OpenAPIKitTests/Operation/OperationTests.swift index 5bd0c88cd..a5bea626f 100644 --- a/Tests/OpenAPIKitTests/Operation/OperationTests.swift +++ b/Tests/OpenAPIKitTests/Operation/OperationTests.swift @@ -7,7 +7,7 @@ import XCTest import OpenAPIKit -import Yams +@preconcurrency import Yams final class OperationTests: XCTestCase { func test_init() { @@ -138,7 +138,7 @@ extension OperationTests { deprecated: true, security: [[.component(named: "security"): []]], servers: [.init(url: URL(string: "https://google.com")!)], - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) let encodedOperation = try orderUnstableTestStringFromEncoding(of: operation) @@ -312,7 +312,7 @@ extension OperationTests { deprecated: true, security: [[.component(named: "security"): []]], servers: [.init(url: URL(string: "https://google.com")!)], - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) ) diff --git a/Tests/OpenAPIKitTests/Parameter/ParameterTests.swift b/Tests/OpenAPIKitTests/Parameter/ParameterTests.swift index e47093f44..6e86c40aa 100644 --- a/Tests/OpenAPIKitTests/Parameter/ParameterTests.swift +++ b/Tests/OpenAPIKitTests/Parameter/ParameterTests.swift @@ -827,7 +827,7 @@ extension ParameterTests { context: .path, schema: .string, description: "world", - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) let encodedParameter = try orderUnstableTestStringFromEncoding(of: parameter) @@ -880,7 +880,7 @@ extension ParameterTests { context: .path, schema: .string, description: "world", - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) ) } diff --git a/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift b/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift index 22c9e2601..ae47ff9e0 100644 --- a/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift +++ b/Tests/OpenAPIKitTests/Path Item/PathItemTests.swift @@ -176,7 +176,7 @@ extension PathItemTests { description: "description", servers: [OpenAPI.Server(url: URL(string: "http://google.com")!)], parameters: [.parameter(name: "hello", context: .query, schema: .string)], - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) let encodedPathItem = try orderUnstableTestStringFromEncoding(of: pathItem) @@ -246,7 +246,7 @@ extension PathItemTests { description: "description", servers: [OpenAPI.Server(url: URL(string: "http://google.com")!)], parameters: [.parameter(name: "hello", context: .query, schema: .string)], - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) ) } diff --git a/Tests/OpenAPIKitTests/Schema Object/JSONSchemaTests.swift b/Tests/OpenAPIKitTests/Schema Object/JSONSchemaTests.swift index 85f30f083..2f7ca5a47 100644 --- a/Tests/OpenAPIKitTests/Schema Object/JSONSchemaTests.swift +++ b/Tests/OpenAPIKitTests/Schema Object/JSONSchemaTests.swift @@ -1320,8 +1320,8 @@ final class SchemaObjectTests: XCTestCase { func test_withInitalAllowedValues() { let null = JSONSchema.null(.init(allowedValues: [nil])) let boolean = JSONSchema.boolean(.init(format: .unspecified, required: true, allowedValues: [false])) - let object = JSONSchema.object(.init(format: .unspecified, required: true, allowedValues: [[:]]), .init(properties: [:])) - let array = JSONSchema.array(.init(format: .unspecified, required: true, allowedValues: [[false]]), .init(items: .boolean(.init(format: .unspecified, required: true)))) + let object = JSONSchema.object(.init(format: .unspecified, required: true, allowedValues: [.init([:])]), .init(properties: [:])) + let array = JSONSchema.array(.init(format: .unspecified, required: true, allowedValues: [.init([false])]), .init(items: .boolean(.init(format: .unspecified, required: true)))) let number = JSONSchema.number(.init(format: .unspecified, required: true, allowedValues: [2.5]), .init()) let integer = JSONSchema.integer(.init(format: .unspecified, required: true, allowedValues: [5]), .init()) let string = JSONSchema.string(.init(format: .unspecified, required: true, allowedValues: ["hello"]), .init()) @@ -1329,7 +1329,7 @@ final class SchemaObjectTests: XCTestCase { XCTAssertEqual(null.allowedValues?[0].description, "nil") XCTAssertEqual(boolean.allowedValues, [false]) - XCTAssertEqual(object.allowedValues, [[:]]) + XCTAssertEqual(object.allowedValues, [.init([:])]) XCTAssertEqual(array.allowedValues?[0].value as! [Bool], [false]) XCTAssertEqual(number.allowedValues, [2.5]) XCTAssertEqual(integer.allowedValues, [5]) @@ -1342,9 +1342,9 @@ final class SchemaObjectTests: XCTestCase { let boolean = JSONSchema.boolean(.init(format: .unspecified, required: true)) .with(allowedValues: [false]) let object = JSONSchema.object(.init(format: .unspecified, required: true), .init(properties: [:])) - .with(allowedValues: [[:]]) + .with(allowedValues: [.init([:])]) let array = JSONSchema.array(.init(format: .unspecified, required: true), .init(items: .boolean(.init(format: .unspecified, required: true)))) - .with(allowedValues: [[false]]) + .with(allowedValues: [.init([false])]) let number = JSONSchema.number(.init(format: .unspecified, required: true), .init()) .with(allowedValues: [2.5]) let integer = JSONSchema.integer(.init(format: .unspecified, required: true), .init()) @@ -1384,8 +1384,8 @@ final class SchemaObjectTests: XCTestCase { func test_withInitalDefaultValue() { let null = JSONSchema.null(.init(defaultValue: nil)) let boolean = JSONSchema.boolean(.init(format: .unspecified, required: true, defaultValue: false)) - let object = JSONSchema.object(.init(format: .unspecified, required: true, defaultValue: [:]), .init(properties: [:])) - let array = JSONSchema.array(.init(format: .unspecified, required: true, defaultValue: [false]), .init(items: .boolean(.init(format: .unspecified, required: true)))) + let object = JSONSchema.object(.init(format: .unspecified, required: true, defaultValue: .init([:])), .init(properties: [:])) + let array = JSONSchema.array(.init(format: .unspecified, required: true, defaultValue: .init([false])), .init(items: .boolean(.init(format: .unspecified, required: true)))) let number = JSONSchema.number(.init(format: .unspecified, required: true, defaultValue: 2.5), .init()) let integer = JSONSchema.integer(.init(format: .unspecified, required: true, defaultValue: 5), .init()) let string = JSONSchema.string(.init(format: .unspecified, required: true, defaultValue: "hello"), .init()) @@ -1393,8 +1393,8 @@ final class SchemaObjectTests: XCTestCase { XCTAssertNil(null.defaultValue) XCTAssertEqual(boolean.defaultValue, false) - XCTAssertEqual(object.defaultValue, [:]) - XCTAssertEqual(array.defaultValue, [false]) + XCTAssertEqual(object.defaultValue, .init([:])) + XCTAssertEqual(array.defaultValue, .init([false])) XCTAssertEqual(number.defaultValue, 2.5) XCTAssertEqual(integer.defaultValue, 5) XCTAssertEqual(string.defaultValue, "hello") @@ -1406,9 +1406,9 @@ final class SchemaObjectTests: XCTestCase { let boolean = JSONSchema.boolean(.init(format: .unspecified, required: true)) .with(defaultValue: false) let object = JSONSchema.object(.init(format: .unspecified, required: true), .init(properties: [:])) - .with(defaultValue: [:]) + .with(defaultValue: .init([:])) let array = JSONSchema.array(.init(format: .unspecified, required: true), .init(items: .boolean(.init(format: .unspecified, required: true)))) - .with(defaultValue: [false]) + .with(defaultValue: .init([false])) let number = JSONSchema.number(.init(format: .unspecified, required: true), .init()) .with(defaultValue: 2.5) let integer = JSONSchema.integer(.init(format: .unspecified, required: true), .init()) @@ -1432,7 +1432,7 @@ final class SchemaObjectTests: XCTestCase { XCTAssertEqual(null.defaultValue!, nil) XCTAssertEqual(boolean.defaultValue, false) XCTAssertEqual(object.defaultValue, AnyCodable([String: String]())) - XCTAssertEqual(array.defaultValue, [false]) + XCTAssertEqual(array.defaultValue, .init([false])) XCTAssertEqual(number.defaultValue, 2.5) XCTAssertEqual(integer.defaultValue, 5) XCTAssertEqual(string.defaultValue, "hello") @@ -1447,7 +1447,7 @@ final class SchemaObjectTests: XCTestCase { } func test_withInitialExample() { - let object = JSONSchema.object(.init(format: .unspecified, required: true, examples: [[:]]), .init(properties: [:])) + let object = JSONSchema.object(.init(format: .unspecified, required: true, examples: [.init([:])]), .init(properties: [:])) let fragment = JSONSchema.fragment(.init(examples: ["hi"])) let null = JSONSchema.null(.init(examples: ["null"])) @@ -1477,7 +1477,7 @@ final class SchemaObjectTests: XCTestCase { let object = try JSONSchema.object(.init(format: .unspecified, required: true), .init(properties: [:])) .with(example: AnyCodable([String: String]())) let array = try JSONSchema.array(.init(), .init()) - .with(example: ["hello"]) + .with(example: .init(["hello"])) let boolean = try JSONSchema.boolean(.init(format: .unspecified, required: true)) .with(example: true) @@ -1491,13 +1491,13 @@ final class SchemaObjectTests: XCTestCase { .with(example: "hello world") let allOf = try JSONSchema.all(of: [.string(.init(), .init())]) - .with(example: ["hello"]) + .with(example: .init(["hello"])) let anyOf = try JSONSchema.any(of: [object]) - .with(example: ["hello"]) + .with(example: .init(["hello"])) let oneOf = try JSONSchema.one(of: [object]) - .with(example: ["hello"]) + .with(example: .init(["hello"])) let not = try JSONSchema.not(object) - .with(example: ["hello"]) + .with(example: .init(["hello"])) let fragment = try JSONSchema.fragment(.init()).with(example: "hi") let reference = try JSONSchema.reference(.external(URL(string: "hello/world.json#/hello")!),.init()).with(example: "hi") @@ -2356,7 +2356,7 @@ extension SchemaObjectTests { XCTAssertEqual(constValueObject.allowedValues?[0].value as! [String: Bool], ["hello": false]) XCTAssertEqual(allowedValueObject.allowedValues?[0].value as! [String: Bool], ["hello": false]) XCTAssertEqual(allowedValueObject.jsonTypeFormat, .object(.generic)) - XCTAssertEqual(defaultValueObject.defaultValue, ["hello": false]) + XCTAssertEqual(defaultValueObject.defaultValue, .init(["hello": false])) XCTAssertEqual(discriminatorObject, JSONSchema.object(discriminator: .init(propertyName: "hello"))) guard case let .object(_, contextB) = allowedValueObject.value else { @@ -3406,7 +3406,7 @@ extension SchemaObjectTests { XCTAssertEqual(nullableObject, JSONSchema.object(.init(format: .generic, nullable: true, examples: [AnyCodable(["hello": true])]), .init(properties: [:]))) XCTAssertEqual(allowedValueObject.allowedValues?[0].value as! [String: Bool], ["hello": false]) XCTAssertEqual(allowedValueObject.jsonTypeFormat, .object(.generic)) - XCTAssertEqual(allowedValueObject.examples, [["hello" : true]]) + XCTAssertEqual(allowedValueObject.examples, [.init(["hello" : true])]) guard case let .object(_, contextB) = allowedValueObject.value else { XCTFail("expected object to be parsed as object") @@ -3793,9 +3793,9 @@ extension SchemaObjectTests { let writeOnlyArray = JSONSchema.array(.init(format: .unspecified, required: true, permissions: .writeOnly), .init()) let deprecatedArray = JSONSchema.array(.init(format: .unspecified, required: true, deprecated: true), .init()) let allowedValueArray = JSONSchema.array(.init(format: .unspecified, required: true), .init()) - .with(allowedValues: [[10]]) + .with(allowedValues: [.init([10])]) let defaultValueArray = JSONSchema.array(.init(format: .unspecified, required: true), .init()) - .with(defaultValue: [10]) + .with(defaultValue: .init([10])) let discriminatorArray = JSONSchema.array(.init(format: .unspecified, required: true, discriminator: .init(propertyName: "hello")), .init()) testAllSharedSimpleContextEncoding( @@ -3852,7 +3852,7 @@ extension SchemaObjectTests { XCTAssertEqual(writeOnlyArray, JSONSchema.array(.init(format: .generic, permissions: .writeOnly), .init())) XCTAssertEqual(deprecatedArray, JSONSchema.array(.init(format: .generic, deprecated: true), .init())) XCTAssertEqual(allowedValueArray.allowedValues?[0].value as! [Bool], [false]) - XCTAssertEqual(defaultValueArray.defaultValue, [false]) + XCTAssertEqual(defaultValueArray.defaultValue, .init([false])) XCTAssertEqual(discriminatorArray, JSONSchema.array(discriminator: .init(propertyName: "hello"))) guard case let .array(_, contextB) = allowedValueArray.value else { @@ -3887,7 +3887,7 @@ extension SchemaObjectTests { let optionalArray = JSONSchema.array(.init(format: .unspecified, required: false), .init(items: .boolean(.init(format: .unspecified, required: false)))) let nullableArray = JSONSchema.array(.init(format: .unspecified, required: true, nullable: true), .init(items: .boolean(.init(format: .unspecified, required: false)))) let allowedValueArray = JSONSchema.array(.init(format: .unspecified, required: true), .init(items: .boolean(.init(format: .unspecified, required: false)))) - .with(allowedValues: [[10]]) + .with(allowedValues: [.init([10])]) testEncodingPropertyLines(entity: requiredArray, propertyLines: [ @@ -3965,7 +3965,7 @@ extension SchemaObjectTests { let optionalArray = JSONSchema.array(.init(format: .unspecified, required: false), .init(uniqueItems: true)) let nullableArray = JSONSchema.array(.init(format: .unspecified, required: true, nullable: true), .init(uniqueItems: true)) let allowedValueArray = JSONSchema.array(.init(format: .unspecified, required: true), .init(uniqueItems: true)) - .with(allowedValues: [[10]]) + .with(allowedValues: [.init([10])]) testEncodingPropertyLines( entity: requiredArray, @@ -4035,7 +4035,7 @@ extension SchemaObjectTests { let optionalArray = JSONSchema.array(.init(format: .unspecified, required: false), .init(maxItems: 2)) let nullableArray = JSONSchema.array(.init(format: .unspecified, required: true, nullable: true), .init(maxItems: 2)) let allowedValueArray = JSONSchema.array(.init(format: .unspecified, required: true), .init(maxItems: 2)) - .with(allowedValues: [[10]]) + .with(allowedValues: [.init([10])]) testEncodingPropertyLines(entity: requiredArray, propertyLines: [ @@ -4093,7 +4093,7 @@ extension SchemaObjectTests { let optionalArray = JSONSchema.array(.init(format: .unspecified, required: false), .init(minItems: 2)) let nullableArray = JSONSchema.array(.init(format: .unspecified, required: true, nullable: true), .init(minItems: 2)) let allowedValueArray = JSONSchema.array(.init(format: .unspecified, required: true), .init(minItems: 2)) - .with(allowedValues: [[10]]) + .with(allowedValues: [.init([10])]) testEncodingPropertyLines(entity: requiredArray, propertyLines: [ @@ -6830,8 +6830,8 @@ extension SchemaObjectTests { "hello": .boolean ], allowedValues: [ - [ "hello": true], - [ "hello": false] + .init([ "hello": true]), + .init([ "hello": false]) ], anchor: "test", dynamicAnchor: "test2", diff --git a/Tests/OpenAPIKitTests/Schema Object/SchemaObjectYamsTests.swift b/Tests/OpenAPIKitTests/Schema Object/SchemaObjectYamsTests.swift index 1d32f5d72..17bf08876 100644 --- a/Tests/OpenAPIKitTests/Schema Object/SchemaObjectYamsTests.swift +++ b/Tests/OpenAPIKitTests/Schema Object/SchemaObjectYamsTests.swift @@ -13,7 +13,7 @@ import Foundation import XCTest import OpenAPIKit -import Yams +@preconcurrency import Yams final class SchemaObjectYamsTests: XCTestCase { func test_nullTypeDecode() throws { diff --git a/Tests/OpenAPIKitTests/ServerTests.swift b/Tests/OpenAPIKitTests/ServerTests.swift index a9874050b..ea3a034d9 100644 --- a/Tests/OpenAPIKitTests/ServerTests.swift +++ b/Tests/OpenAPIKitTests/ServerTests.swift @@ -170,7 +170,7 @@ extension ServerTests { vendorExtensions: [ "x-otherThing": 1234 ] ) ], - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) ) } @@ -187,7 +187,7 @@ extension ServerTests { vendorExtensions: [ "x-otherThing": 1234 ] ) ], - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) let encodedServer = try orderUnstableTestStringFromEncoding(of: server) diff --git a/Tests/OpenAPIKitTests/TestHelpers.swift b/Tests/OpenAPIKitTests/TestHelpers.swift index eadd92726..336866d5d 100644 --- a/Tests/OpenAPIKitTests/TestHelpers.swift +++ b/Tests/OpenAPIKitTests/TestHelpers.swift @@ -6,7 +6,7 @@ // import Foundation -import Yams +@preconcurrency import Yams import XCTest fileprivate let foundationTestEncoder = { () -> JSONEncoder in diff --git a/Tests/OpenAPIKitTests/Validator/BuiltinValidationTests.swift b/Tests/OpenAPIKitTests/Validator/BuiltinValidationTests.swift index 389ee2ddf..d4cd8b3d0 100644 --- a/Tests/OpenAPIKitTests/Validator/BuiltinValidationTests.swift +++ b/Tests/OpenAPIKitTests/Validator/BuiltinValidationTests.swift @@ -330,7 +330,7 @@ final class BuiltinValidationTests: XCTestCase { vendorExtensions: [ "x-otherThing": 1234 ] ) ], - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) let document = OpenAPI.Document( info: .init(title: "test", version: "1.0"), @@ -357,7 +357,7 @@ final class BuiltinValidationTests: XCTestCase { vendorExtensions: [ "x-otherThing": 1234 ] ) ], - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) let document = OpenAPI.Document( info: .init(title: "test", version: "1.0"), @@ -381,7 +381,7 @@ final class BuiltinValidationTests: XCTestCase { vendorExtensions: [ "x-otherThing": 1234 ] ) ], - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) let document = OpenAPI.Document( info: .init(title: "test", version: "1.0"), @@ -408,7 +408,7 @@ final class BuiltinValidationTests: XCTestCase { vendorExtensions: [ "x-otherThing": 1234 ] ) ], - vendorExtensions: ["x-specialFeature": ["hello", "world"]] + vendorExtensions: ["x-specialFeature": .init(["hello", "world"])] ) let document = OpenAPI.Document( info: .init(title: "test", version: "1.0"), diff --git a/Tests/OpenAPIKitTests/Validator/ValidatorTests.swift b/Tests/OpenAPIKitTests/Validator/ValidatorTests.swift index 3f49c5e4f..d8351cade 100644 --- a/Tests/OpenAPIKitTests/Validator/ValidatorTests.swift +++ b/Tests/OpenAPIKitTests/Validator/ValidatorTests.swift @@ -85,7 +85,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -112,7 +112,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -144,7 +144,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -180,7 +180,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -212,7 +212,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -242,7 +242,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -278,7 +278,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -312,7 +312,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -324,7 +324,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -360,7 +360,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -396,7 +396,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -433,7 +433,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -470,7 +470,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -506,7 +506,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -688,7 +688,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -722,7 +722,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hiya", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -818,7 +818,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -869,7 +869,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hello", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -920,7 +920,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hiya", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -958,7 +958,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hiya", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -1002,7 +1002,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hiya", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -1045,7 +1045,7 @@ final class ValidatorTests: XCTestCase { "x-string": "hiya", "x-int": 2244, "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]) ] ) @@ -1339,7 +1339,7 @@ final class ValidatorTests: XCTestCase { vendorExtensions: [ "x-string": "hiya", "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]), "x-float": AnyCodable(22.5 as Float), "x-bool": true @@ -1377,7 +1377,7 @@ final class ValidatorTests: XCTestCase { vendorExtensions: [ "x-string": "hiya", "x-double": 10.5, - "x-dict": [ "string": "world"], + "x-dict": .init([ "string": "world"]), "x-array": AnyCodable(["hello", nil, "world"]), "x-float": AnyCodable(22.5 as Float), "x-bool": true diff --git a/Tests/OpenAPIKitTests/VendorExtendableTests.swift b/Tests/OpenAPIKitTests/VendorExtendableTests.swift index da10a74db..b6fe00ed2 100644 --- a/Tests/OpenAPIKitTests/VendorExtendableTests.swift +++ b/Tests/OpenAPIKitTests/VendorExtendableTests.swift @@ -39,13 +39,13 @@ final class VendorExtendableTests: XCTestCase { func test_encodeSuccess() throws { let test = TestStruct(vendorExtensions: [ "x-tension": "hello", - "x-two": [ + "x-two": .init([ "cool", "beans" - ], - "x-three": [ + ]), + "x-three": .init([ "nested": 10 - ] + ]) ]) let _ = try JSONEncoder().encode(test) @@ -85,13 +85,13 @@ extension VendorExtendableTests { func test_encode() throws { let test = TestStruct(vendorExtensions: [ "x-tension": "hello", - "x-two": [ + "x-two": .init([ "cool", "beans" - ], - "x-three": [ + ]), + "x-three": .init([ "nested": 10 - ] + ]) ]) let encoded = try orderUnstableTestStringFromEncoding(of: test) diff --git a/Tests/OrderedDictionaryTests/OrderedDictionaryTests.swift b/Tests/OrderedDictionaryTests/OrderedDictionaryTests.swift index 06235553e..3b5b3903b 100644 --- a/Tests/OrderedDictionaryTests/OrderedDictionaryTests.swift +++ b/Tests/OrderedDictionaryTests/OrderedDictionaryTests.swift @@ -7,7 +7,7 @@ import OpenAPIKitCore import XCTest -import Yams +@preconcurrency import Yams final class OrderedDictionaryTests: XCTestCase { func test_initGrouping() { From 852e86d492c1f513418b6c39dc4a4c6f9e846ab7 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 28 Jan 2025 09:30:55 -0600 Subject: [PATCH 095/110] stop printing StackFoo during test --- Tests/OpenAPIKitCoreTests/URLTemplate/URLTemplateTests.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/OpenAPIKitCoreTests/URLTemplate/URLTemplateTests.swift b/Tests/OpenAPIKitCoreTests/URLTemplate/URLTemplateTests.swift index 79110a6af..84dff9f63 100644 --- a/Tests/OpenAPIKitCoreTests/URLTemplate/URLTemplateTests.swift +++ b/Tests/OpenAPIKitCoreTests/URLTemplate/URLTemplateTests.swift @@ -395,7 +395,6 @@ extension URLTemplateTests { StackFoo.self, from: data ) - print(document) } func test_avoid_stack_overflow() async throws { From 2ee233c2219522d1c26306b638c483ded71262c3 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 28 Jan 2025 09:49:43 -0600 Subject: [PATCH 096/110] fix AnyCodable encoding of nil --- Sources/OpenAPIKitCore/AnyCodable/AnyCodable.swift | 14 ++++++++++++++ .../URLTemplate/URLTemplateTests.swift | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Sources/OpenAPIKitCore/AnyCodable/AnyCodable.swift b/Sources/OpenAPIKitCore/AnyCodable/AnyCodable.swift index f697d360c..25c9de012 100644 --- a/Sources/OpenAPIKitCore/AnyCodable/AnyCodable.swift +++ b/Sources/OpenAPIKitCore/AnyCodable/AnyCodable.swift @@ -49,10 +49,24 @@ public struct AnyCodable: @unchecked Sendable { } } +protocol _Optional { + var isNil: Bool { get } +} + +extension Optional: _Optional { + var isNil: Bool { return self == nil } +} + extension AnyCodable: Encodable { public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() + // special nil case + if let optionalValue = value as? _Optional, optionalValue.isNil { + try container.encodeNil() + return + } + switch value { #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) case let number as NSNumber: diff --git a/Tests/OpenAPIKitCoreTests/URLTemplate/URLTemplateTests.swift b/Tests/OpenAPIKitCoreTests/URLTemplate/URLTemplateTests.swift index 84dff9f63..a0f9c12b6 100644 --- a/Tests/OpenAPIKitCoreTests/URLTemplate/URLTemplateTests.swift +++ b/Tests/OpenAPIKitCoreTests/URLTemplate/URLTemplateTests.swift @@ -391,7 +391,7 @@ extension URLTemplateTests { } """.utf8) - let document = try JSONDecoder().decode( + let _ = try JSONDecoder().decode( StackFoo.self, from: data ) From 15f8f3492c96984858c3a70390e9c7f6081567d4 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 28 Jan 2025 10:40:51 -0600 Subject: [PATCH 097/110] bug fixes for AnyCodable null-like types --- .../AnyCodable/AnyCodable.swift | 33 ++++++++++++++++++- Tests/AnyCodableTests/AnyCodableTests.swift | 22 ++++++++++--- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/Sources/OpenAPIKitCore/AnyCodable/AnyCodable.swift b/Sources/OpenAPIKitCore/AnyCodable/AnyCodable.swift index 25c9de012..da4e022e9 100644 --- a/Sources/OpenAPIKitCore/AnyCodable/AnyCodable.swift +++ b/Sources/OpenAPIKitCore/AnyCodable/AnyCodable.swift @@ -31,6 +31,13 @@ import Foundation You can encode or decode mixed-type values in dictionaries and other collections that require `Encodable` or `Decodable` conformance by declaring their contained type to be `AnyCodable`. + + Note that there are some caveats related to the fact that this type centers around + encoding/decoding values. For example, some technically distinct nil-like types + are all encoded as `nil` and compare equally under the `AnyCodable` type: + - `nil` + - `NSNull()` + - `Void()` */ public struct AnyCodable: @unchecked Sendable { // IMPORTANT: @@ -54,7 +61,11 @@ protocol _Optional { } extension Optional: _Optional { - var isNil: Bool { return self == nil } + var isNil: Bool { self == nil } +} + +extension NSNull: _Optional { + var isNil: Bool { true } } extension AnyCodable: Encodable { @@ -178,8 +189,28 @@ extension AnyCodable: Decodable { } } +func isNilEquivalent(value: AnyCodable) -> Bool { + let valueIsNil: Bool + + if let optionalValue = value.value as? _Optional, + optionalValue.isNil { + valueIsNil = true + } else if let _ = value.value as? Void { + valueIsNil = true + } else { + valueIsNil = false + } + + return valueIsNil +} + extension AnyCodable: Equatable { public static func == (lhs: AnyCodable, rhs: AnyCodable) -> Bool { + // special case for nil + if isNilEquivalent(value: lhs) && isNilEquivalent(value: rhs) { + return true + } + switch (lhs.value, rhs.value) { case is (Void, Void): return true diff --git a/Tests/AnyCodableTests/AnyCodableTests.swift b/Tests/AnyCodableTests/AnyCodableTests.swift index 6dfde77c5..6bb8f22ba 100644 --- a/Tests/AnyCodableTests/AnyCodableTests.swift +++ b/Tests/AnyCodableTests/AnyCodableTests.swift @@ -14,7 +14,12 @@ class AnyCodableTests: XCTestCase { } func testEquality() throws { - XCTAssertEqual(AnyCodable(()), AnyCodable(())) + // nil, NSNull(), and Void() all encode as "null" and + // compare equally. + XCTAssertEqual(AnyCodable(nil), AnyCodable(nil)) + XCTAssertEqual(AnyCodable(nil), AnyCodable(NSNull())) + XCTAssertEqual(AnyCodable(nil), AnyCodable(())) + XCTAssertEqual(AnyCodable(true), AnyCodable(true)) XCTAssertEqual(AnyCodable(2), AnyCodable(2)) XCTAssertEqual(AnyCodable(Int8(2)), AnyCodable(Int8(2))) @@ -53,7 +58,8 @@ class AnyCodableTests: XCTestCase { "a": "alpha", "b": "bravo", "c": "charlie" - } + }, + "null": null } """.data(using: .utf8)! let decoder = JSONDecoder() @@ -133,7 +139,8 @@ class AnyCodableTests: XCTestCase { "a": "alpha", "b": "bravo", "c": "charlie" - } + }, + "null": null } """.data(using: .utf8)! @@ -146,6 +153,7 @@ class AnyCodableTests: XCTestCase { XCTAssertEqual(dictionary["string"]?.value as! String, "string") XCTAssertEqual(dictionary["array"]?.value as! [Int], [1, 2, 3]) XCTAssertEqual(dictionary["nested"]?.value as! [String: String], ["a": "alpha", "b": "bravo", "c": "charlie"]) + XCTAssertEqual(dictionary["null"], AnyCodable(nil)) } func testJSONEncoding() throws { @@ -159,6 +167,9 @@ class AnyCodableTests: XCTestCase { "b": "bravo", "c": "charlie", ]), + "null": nil, + "void": .init(Void()), + "nsnull": .init(NSNull()) ] let result = try testStringFromEncoding(of: dictionary) @@ -179,7 +190,10 @@ class AnyCodableTests: XCTestCase { "b" : "bravo", "c" : "charlie" }, - "string" : "string" + "nsnull" : null, + "null" : null, + "string" : "string", + "void" : null } """ ) From 12434070c2bb1f2f6b7e2c31c2d26d4f5c39f685 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Wed, 12 Feb 2025 08:32:10 -0600 Subject: [PATCH 098/110] improve code coverage and add a proper fallback equality check based on encodability --- .../OpenAPIKitCore/AnyCodable/AnyCodable.swift | 17 +++++++++++++++-- Tests/AnyCodableTests/AnyCodableTests.swift | 15 ++++++++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/Sources/OpenAPIKitCore/AnyCodable/AnyCodable.swift b/Sources/OpenAPIKitCore/AnyCodable/AnyCodable.swift index da4e022e9..32c8c018f 100644 --- a/Sources/OpenAPIKitCore/AnyCodable/AnyCodable.swift +++ b/Sources/OpenAPIKitCore/AnyCodable/AnyCodable.swift @@ -212,8 +212,6 @@ extension AnyCodable: Equatable { } switch (lhs.value, rhs.value) { - case is (Void, Void): - return true case let (lhs as Bool, rhs as Bool): return lhs == rhs case let (lhs as Int, rhs as Int): @@ -268,6 +266,21 @@ extension AnyCodable: Equatable { return lhs == rhs case let (lhs as [Any], rhs as [Any]): return lhs.map(AnyCodable.init) == rhs.map(AnyCodable.init) + case let (lhs as Encodable, rhs as Encodable): + let encoder = JSONEncoder() + let lhsData: Data + do { + lhsData = try encoder.encode(lhs) + } catch { + return false + } + let rhsData: Data + do { + rhsData = try encoder.encode(rhs) + } catch { + return false + } + return lhsData == rhsData default: return false } diff --git a/Tests/AnyCodableTests/AnyCodableTests.swift b/Tests/AnyCodableTests/AnyCodableTests.swift index 6bb8f22ba..07a151573 100644 --- a/Tests/AnyCodableTests/AnyCodableTests.swift +++ b/Tests/AnyCodableTests/AnyCodableTests.swift @@ -20,6 +20,7 @@ class AnyCodableTests: XCTestCase { XCTAssertEqual(AnyCodable(nil), AnyCodable(NSNull())) XCTAssertEqual(AnyCodable(nil), AnyCodable(())) + XCTAssertEqual(AnyCodable(()), AnyCodable(())) XCTAssertEqual(AnyCodable(true), AnyCodable(true)) XCTAssertEqual(AnyCodable(2), AnyCodable(2)) XCTAssertEqual(AnyCodable(Int8(2)), AnyCodable(Int8(2))) @@ -39,11 +40,18 @@ class AnyCodableTests: XCTestCase { XCTAssertEqual(AnyCodable([AnyCodable("hi"), AnyCodable("there")]), AnyCodable([AnyCodable("hi"), AnyCodable("there")])) XCTAssertEqual(AnyCodable(["hi":1]), AnyCodable(["hi":1])) XCTAssertEqual(AnyCodable(["hi":1.2]), AnyCodable(["hi":1.2])) + XCTAssertEqual(AnyCodable(["hi":true]), AnyCodable(["hi":true])) XCTAssertEqual(AnyCodable(["hi"]), AnyCodable(["hi"])) XCTAssertEqual(AnyCodable([1]), AnyCodable([1])) XCTAssertEqual(AnyCodable([1.2]), AnyCodable([1.2])) XCTAssertEqual(AnyCodable([true]), AnyCodable([true])) + // force the array of Any branch: + XCTAssertEqual(AnyCodable([StringThing(value: "hi")]), AnyCodable([StringThing(value: "hi")])) + + // force the dictionary of Any branch: + XCTAssertEqual(AnyCodable(["hi": StringThing(value: "hi")]), AnyCodable(["hi": StringThing(value: "hi")])) + XCTAssertNotEqual(AnyCodable(()), AnyCodable(true)) } @@ -86,6 +94,7 @@ class AnyCodableTests: XCTestCase { func testVoidDescription() { XCTAssertEqual(String(describing: AnyCodable(Void())), "nil") + XCTAssertEqual(AnyCodable(Void()).debugDescription, "AnyCodable(nil)") } func test_encodedDecodedURL() throws { @@ -269,6 +278,10 @@ class AnyCodableTests: XCTestCase { } } -fileprivate struct Wrapper: Codable { +fileprivate struct Wrapper: Codable, Equatable { let value: AnyCodable } + +fileprivate struct StringThing: Codable, Equatable { + let value: String +} From bb4bf692feb748806e4db613b25254d1477b6b6c Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sat, 15 Feb 2025 16:34:06 -0600 Subject: [PATCH 099/110] add some more test coverage --- Tests/AnyCodableTests/AnyCodableTests.swift | 18 ++++++++ .../CallbackURLTests.swift | 43 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 Tests/OpenAPIKitCoreTests/CallbackURLTests.swift diff --git a/Tests/AnyCodableTests/AnyCodableTests.swift b/Tests/AnyCodableTests/AnyCodableTests.swift index 07a151573..3e2971c92 100644 --- a/Tests/AnyCodableTests/AnyCodableTests.swift +++ b/Tests/AnyCodableTests/AnyCodableTests.swift @@ -251,6 +251,12 @@ class AnyCodableTests: XCTestCase { let string = String(data: data, encoding: .utf8) XCTAssertEqual(string, #"{"value":false}"#) + + let data2 = try JSONEncoder().encode(AnyCodable(false)) + + let string2 = String(data: data2, encoding: .utf8) + + XCTAssertEqual(string2, "false") } func test_encodeInt() throws { @@ -259,6 +265,12 @@ class AnyCodableTests: XCTestCase { let string = String(data: data, encoding: .utf8) XCTAssertEqual(string, #"{"value":2}"#) + + let data2 = try JSONEncoder().encode(AnyCodable(2)) + + let string2 = String(data: data2, encoding: .utf8) + + XCTAssertEqual(string2, "2") } func test_encodeString() throws { @@ -267,6 +279,12 @@ class AnyCodableTests: XCTestCase { let string = String(data: data, encoding: .utf8) XCTAssertEqual(string, #"{"value":"hi"}"#) + + let data2 = try JSONEncoder().encode(AnyCodable("hi")) + + let string2 = String(data: data2, encoding: .utf8) + + XCTAssertEqual(string2, #""hi""#) } func test_encodeURL() throws { diff --git a/Tests/OpenAPIKitCoreTests/CallbackURLTests.swift b/Tests/OpenAPIKitCoreTests/CallbackURLTests.swift new file mode 100644 index 000000000..15fe508ae --- /dev/null +++ b/Tests/OpenAPIKitCoreTests/CallbackURLTests.swift @@ -0,0 +1,43 @@ +// +// CallbackURLTests.swift +// OpenAPIKit +// +// Created by Mathew Polzin on 2/15/25. +// + +import OpenAPIKitCore +import XCTest + +final class CallbackURLTests: XCTestCase { + func testInit() { + let plainUrl = Shared.CallbackURL(url: URL(string: "https://hello.com")!) + XCTAssertEqual(plainUrl.url, URL(string: "https://hello.com")!) + XCTAssertEqual(plainUrl.template.variables.count, 0) + XCTAssertEqual(plainUrl.rawValue, "https://hello.com") + + let templateUrl = Shared.CallbackURL(rawValue: "https://hello.com/item/{$request.path.id}") + XCTAssertEqual(templateUrl?.template.variables, ["$request.path.id"]) + } + + func testEncode() throws { + let url = Shared.CallbackURL(rawValue: "https://hello.com/item/{$request.path.id}") + + let result = try orderUnstableTestStringFromEncoding(of: url) + + assertJSONEquivalent( + result, + """ + "https:\\/\\/hello.com\\/item\\/{$request.path.id}" + """ + ) + } + + func testDecode() throws { + let json = #""https://hello.com/item/{$request.path.id}""# + let data = json.data(using: .utf8)! + + let url = try orderUnstableDecode(Shared.CallbackURL.self, from: data) + + XCTAssertEqual(url, Shared.CallbackURL(rawValue: "https://hello.com/item/{$request.path.id}")) + } +} From 4cf25fcb0ad0614baf909dce754f82624631d7ff Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sun, 16 Feb 2025 10:10:45 -0600 Subject: [PATCH 100/110] improve test coverage for some additional easy cases --- .../DocumentErrorTests.swift | 1 + .../Document/DereferencedDocumentTests.swift | 12 +++++- .../Validator/ValidatorTests.swift | 6 +++ .../CallbackURLTests.swift | 6 +-- .../ComponentKeyTests.swift | 40 +++++++++++++++++++ .../Document/DereferencedDocumentTests.swift | 12 +++++- 6 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 Tests/OpenAPIKitCoreTests/ComponentKeyTests.swift diff --git a/Tests/OpenAPIKit30ErrorReportingTests/DocumentErrorTests.swift b/Tests/OpenAPIKit30ErrorReportingTests/DocumentErrorTests.swift index 919c52132..30cedeff6 100644 --- a/Tests/OpenAPIKit30ErrorReportingTests/DocumentErrorTests.swift +++ b/Tests/OpenAPIKit30ErrorReportingTests/DocumentErrorTests.swift @@ -26,6 +26,7 @@ final class DocumentErrorTests: XCTestCase { let openAPIError = OpenAPI.Error(from: error) XCTAssertEqual(openAPIError.localizedDescription, "Expected to find `openapi` key in the root Document object but it is missing.") + XCTAssertEqual(openAPIError.localizedDescription, openAPIError.description) XCTAssertEqual(openAPIError.codingPath.map { $0.stringValue }, []) } } diff --git a/Tests/OpenAPIKit30Tests/Document/DereferencedDocumentTests.swift b/Tests/OpenAPIKit30Tests/Document/DereferencedDocumentTests.swift index d46449530..a1ea8115e 100644 --- a/Tests/OpenAPIKit30Tests/Document/DereferencedDocumentTests.swift +++ b/Tests/OpenAPIKit30Tests/Document/DereferencedDocumentTests.swift @@ -28,14 +28,17 @@ final class DereferencedDocumentTests: XCTestCase { servers: [.init(url: URL(string: "http://website.com")!)], paths: [ "/hello/world": .init( + servers: [.init(urlTemplate: URLTemplate(rawValue: "http://{domain}.com")!, variables: ["domain": .init(default: "other")])], get: .init( + operationId: "hi", responses: [ 200: .response(description: "success") ] ) ) ], - components: .noComponents + components: .noComponents, + tags: ["hi"] ).locallyDereferenced() XCTAssertEqual(t1.paths.count, 1) @@ -51,6 +54,13 @@ final class DereferencedDocumentTests: XCTestCase { t1.resolvedEndpointsByPath().keys, ["/hello/world"] ) + + XCTAssertEqual(t1.allOperationIds, ["hi"]) + XCTAssertEqual(t1.allServers, [ + .init(url: URL(string: "http://website.com")!), + .init(urlTemplate: URLTemplate(rawValue: "http://{domain}.com")!, variables: ["domain": .init(default: "other")]), + ]) + XCTAssertEqual(t1.allTags, ["hi"]) } func test_noSecurityReferencedResponseInPath() throws { diff --git a/Tests/OpenAPIKit30Tests/Validator/ValidatorTests.swift b/Tests/OpenAPIKit30Tests/Validator/ValidatorTests.swift index bc3d4555d..c96acc1cf 100644 --- a/Tests/OpenAPIKit30Tests/Validator/ValidatorTests.swift +++ b/Tests/OpenAPIKit30Tests/Validator/ValidatorTests.swift @@ -1444,6 +1444,12 @@ final class ValidatorTests: XCTestCase { "Inconsistency encountered when parsing ``: \'gzip\' could not be parsed as a Content Type. Content Types should have the format \'/\'." ) XCTAssertEqual(warnings.first?.codingPathString, ".paths[\'/test\'].get.responses.200.content") + XCTAssertNotNil(warnings.first?.underlyingError) + XCTAssertNotNil(warnings.first?.errorCategory) + XCTAssertEqual(warnings.first?.subjectName, "") + XCTAssertEqual(warnings.first?.contextString, "") + + XCTAssertEqual(warnings.first?.localizedDescription, warnings.first?.description) } func test_collectsContentTypeWarningStrict() throws { diff --git a/Tests/OpenAPIKitCoreTests/CallbackURLTests.swift b/Tests/OpenAPIKitCoreTests/CallbackURLTests.swift index 15fe508ae..01f975b70 100644 --- a/Tests/OpenAPIKitCoreTests/CallbackURLTests.swift +++ b/Tests/OpenAPIKitCoreTests/CallbackURLTests.swift @@ -9,7 +9,7 @@ import OpenAPIKitCore import XCTest final class CallbackURLTests: XCTestCase { - func testInit() { + func test_init() { let plainUrl = Shared.CallbackURL(url: URL(string: "https://hello.com")!) XCTAssertEqual(plainUrl.url, URL(string: "https://hello.com")!) XCTAssertEqual(plainUrl.template.variables.count, 0) @@ -19,7 +19,7 @@ final class CallbackURLTests: XCTestCase { XCTAssertEqual(templateUrl?.template.variables, ["$request.path.id"]) } - func testEncode() throws { + func test_encode() throws { let url = Shared.CallbackURL(rawValue: "https://hello.com/item/{$request.path.id}") let result = try orderUnstableTestStringFromEncoding(of: url) @@ -32,7 +32,7 @@ final class CallbackURLTests: XCTestCase { ) } - func testDecode() throws { + func test_decode() throws { let json = #""https://hello.com/item/{$request.path.id}""# let data = json.data(using: .utf8)! diff --git a/Tests/OpenAPIKitCoreTests/ComponentKeyTests.swift b/Tests/OpenAPIKitCoreTests/ComponentKeyTests.swift new file mode 100644 index 000000000..0236e9ee0 --- /dev/null +++ b/Tests/OpenAPIKitCoreTests/ComponentKeyTests.swift @@ -0,0 +1,40 @@ +// +// ComponentKeyTests.swift +// OpenAPIKit +// +// Created by Mathew Polzin on 2/16/25. +// + +import OpenAPIKitCore +import XCTest + +final class ComponentKeyTests: XCTestCase { + func test_init() throws { + let t1 : Shared.ComponentKey = "abcd" + XCTAssertEqual(t1.rawValue, "abcd") + + let t2 = Shared.ComponentKey(rawValue: "abcd") + XCTAssertEqual(t2?.rawValue, "abcd") + + let t3 = Shared.ComponentKey(rawValue: "") + XCTAssertNil(t3) + + let t4 = Shared.ComponentKey(rawValue: "(abcd)") + XCTAssertNil(t4) + + let t5 = try Shared.ComponentKey.forceInit(rawValue: "abcd") + XCTAssertEqual(t5.rawValue, "abcd") + + XCTAssertThrowsError(try Shared.ComponentKey.forceInit(rawValue: nil)) + XCTAssertThrowsError(try Shared.ComponentKey.forceInit(rawValue: "(abcd)")) + } + + func test_problemString() { + let message = Shared.ComponentKey.problem(with: "(abcd)") + + XCTAssertEqual(message, "Keys for components in the Components Object must conform to the regex `^[a-zA-Z0-9\\.\\-_]+$`. '(abcd)' does not..") + + let nonProblem = Shared.ComponentKey.problem(with: "abcd") + XCTAssertNil(nonProblem) + } +} diff --git a/Tests/OpenAPIKitTests/Document/DereferencedDocumentTests.swift b/Tests/OpenAPIKitTests/Document/DereferencedDocumentTests.swift index 941db1093..678223292 100644 --- a/Tests/OpenAPIKitTests/Document/DereferencedDocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/DereferencedDocumentTests.swift @@ -28,14 +28,17 @@ final class DereferencedDocumentTests: XCTestCase { servers: [.init(url: URL(string: "http://website.com")!)], paths: [ "/hello/world": .init( + servers: [.init(urlTemplate: URLTemplate(rawValue: "http://{domain}.com")!, variables: ["domain": .init(default: "other")])], get: .init( + operationId: "hi", responses: [ 200: .response(description: "success") ] ) ) ], - components: .noComponents + components: .noComponents, + tags: ["hi"] ).locallyDereferenced() XCTAssertEqual(t1.paths.count, 1) @@ -51,6 +54,13 @@ final class DereferencedDocumentTests: XCTestCase { t1.resolvedEndpointsByPath().keys, ["/hello/world"] ) + + XCTAssertEqual(t1.allOperationIds, ["hi"]) + XCTAssertEqual(t1.allServers, [ + .init(url: URL(string: "http://website.com")!), + .init(urlTemplate: URLTemplate(rawValue: "http://{domain}.com")!, variables: ["domain": .init(default: "other")]), + ]) + XCTAssertEqual(t1.allTags, ["hi"]) } func test_noSecurityReferencedResponseInPath() throws { From 122cddf851d781559b162610445e9d69c5506927 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Mon, 17 Feb 2025 13:36:05 -0600 Subject: [PATCH 101/110] update the README and draft a migration guide --- README.md | 8 +-- documentation/v4_migration_guide.md | 75 +++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 documentation/v4_migration_guide.md diff --git a/README.md b/README.md index 181a9c98f..bd30e7801 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![sswg:sandbox|94x20](https://img.shields.io/badge/sswg-sandbox-lightgrey.svg)](https://github.com/swift-server/sswg/blob/master/process/incubation.md#sandbox-level) [![Swift 5.1+](http://img.shields.io/badge/Swift-5.1+-blue.svg)](https://swift.org) +[![sswg:sandbox|94x20](https://img.shields.io/badge/sswg-sandbox-lightgrey.svg)](https://github.com/swift-server/sswg/blob/master/process/incubation.md#sandbox-level) [![Swift 5.8+](http://img.shields.io/badge/Swift-5.8+-blue.svg)](https://swift.org) [![MIT license](http://img.shields.io/badge/license-MIT-lightgrey.svg)](http://opensource.org/licenses/MIT) ![Tests](https://github.com/mattpolzin/OpenAPIKit/workflows/Tests/badge.svg) @@ -10,8 +10,8 @@ OpenAPIKit follows semantic versioning despite the fact that the OpenAPI specifi | OpenAPIKit | Swift | OpenAPI v3.0 | OpenAPI v3.1 | External Dereferencing | |------------|-------|--------------|--------------|------------------------| -| v2.x | 5.1+ | ✅ | ❌ | ❌ | -| v3.x | 5.1+ | ✅ | ✅ | ❌ | +| v2.x | 5.1+ | ✅ | | | +| v3.x | 5.1+ | ✅ | ✅ | | | v4.x | 5.8+ | ✅ | ✅ | ✅ | - [Usage](#usage) @@ -76,7 +76,7 @@ import OpenAPIKit 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. #### 3.x to 4.x -This section has not been written yet. Stay tuned! +If you are migrating from OpenAPIKit 3.x to OpenAPIKit 4.x, check out the [v4 migration guide](./documentation/v4_migration_guide.md). ### Decoding OpenAPI Documents diff --git a/documentation/v4_migration_guide.md b/documentation/v4_migration_guide.md new file mode 100644 index 000000000..0632ecb5f --- /dev/null +++ b/documentation/v4_migration_guide.md @@ -0,0 +1,75 @@ +## OpenAPIKit v4 Migration Guide +For general information on the v4 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 v3 to v4. You can also refer back to the release notes +for each of the v4 pre-releases for the most thorough look at what changed. + +This guide will not spend time on strictly additive features of version 4. See +the release notes, README, and documentation for information on new features. + +### Swift version support +OpenAPIKit v4.0 drops support for Swift versions prior to 5.8 (i.e. it supports +v5.8 and greater). + +### Yams version support +Yams is only a test dependency of OpenAPIKit, but since it is still a dependency +it will still impact dependency resolution of downstream projects. Yams 5.1.0+ +is now required. + +### MacOS version support +Only relevant when compiling OpenAPIKit on macOS: Now v10_15+ is required. + +### OpenAPI Specification Versions +The `OpenAPIKit.Document.Version` enum gained `v3_1_1` and the +`OpenAPIKit30.Document.Version` enum gained `v3_0_4`. If you have exhaustive +switches over values of those types then your switch statements will need to be +updated. + +### Typo corrections +The following typo corrections were made to OpenAPIKit code. These amount to +breaking changes only in so far as you need to correct the same names if they +appear in your codebase. + +- `public static Validation.serverVarialbeEnumIsValid` -> `.serverVariableEnumIsValid` +- `spublic static Validation.erverVarialbeDefaultExistsInEnum` -> `.serverVariableDefaultExistsInEnum` + +### `AnyCodable` +**NOTE** that the `AnyCodable` type is used extensively for OpenAPIKit examples +and vendor extensions so that is likely where this note will be relevant to you. + +1. The constructor for `AnyCodable` now requires knowledge at compile time that + the value it is initialized with is `Sendable`. +2. Array and Dictionary literal protocol conformances had to be dropped. + Anywhere you were relying on implicit conversion from e.g. `["hello": 1]` to + an `AnyCodable`, wrap the literal with an explicit call to init: + `.init(["hello": 1])`. + +### Vendor Extensions +1. The `vendorExtensions` property of any `VendorExtendable` type must now + implement a setter as well as a getter. This is not likely to impact + downstream projects, but technically possible. +2. If you are disabling `vendorExtensions` support via the + `VendorExtensionsConfiguration.isEnabled` property, you need to switch to + using encoder/decoder `userInfo` to disable vendor extensions. The + `isEnabled` property has been removed. See the example below. + +To set an encoder or decoder up to disable vendor extensions use code like the +following before using the encoder or decoder with an OpenAPIKit type: +```swift +let userInfo = [VendorExtensionsConfiguration.enabledKey: false] +let encoder = JSONEncoder() +encoder.userInfo = userInfo + +let decoder = JSONDecoder() +decoder.userInfo = userInfo +``` + +### `OpenAPI.Content.Encoding` +The `contentType` property has been removed in favor of the newer `contentTypes` +property (plural). + +### `JSONSchemaContext` +The default (fallback) implementations of `inferred`, `anchor`, and +`dynamicAnchor` have been removed. Almost no downstream code will break because +of this, but if you've implemented the `JSONSchemaContext` protocol yourself +then this note is for you. From dca99b7111e6c18a36ed6d0b4f773b03fa36fe36 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Mon, 17 Feb 2025 14:05:54 -0600 Subject: [PATCH 102/110] make it possible to support additional future versions of OAS without a breaking change to an enumeration --- Sources/OpenAPIKit/Document/Document.swift | 34 ++++++++++++-- Sources/OpenAPIKit30/Document/Document.swift | 46 ++++++++++++++++--- .../Document/DocumentTests.swift | 38 +++++++++++++++ .../Document/DocumentTests.swift | 20 ++++++++ 4 files changed, 129 insertions(+), 9 deletions(-) diff --git a/Sources/OpenAPIKit/Document/Document.swift b/Sources/OpenAPIKit/Document/Document.swift index 73513284e..378991237 100644 --- a/Sources/OpenAPIKit/Document/Document.swift +++ b/Sources/OpenAPIKit/Document/Document.swift @@ -424,9 +424,37 @@ extension OpenAPI.Document { /// OpenAPIKit only explicitly supports versions that can be found in /// this enum. Other versions may or may not be decodable by /// OpenAPIKit to a certain extent. - public enum Version: String, Codable { - case v3_1_0 = "3.1.0" - case v3_1_1 = "3.1.1" + public enum Version: RawRepresentable, Equatable, Codable { + case v3_1_0 + case v3_1_1 + case v3_1_x(x: Int) + + public init?(rawValue: String) { + switch rawValue { + case "3.1.0": self = .v3_1_0 + case "3.1.1": self = .v3_1_1 + default: + let components = rawValue.split(separator: ".") + guard components.count == 3 else { + return nil + } + guard components[0] == "3", components[1] == "1" else { + return nil + } + guard let patchVersion = Int(components[2], radix: 10) else { + return nil + } + self = .v3_1_x(x: patchVersion) + } + } + + public var rawValue: String { + switch self { + case .v3_1_0: return "3.1.0" + case .v3_1_1: return "3.1.1" + case .v3_1_x(x: let x): return "3.1.\(x)" + } + } } } diff --git a/Sources/OpenAPIKit30/Document/Document.swift b/Sources/OpenAPIKit30/Document/Document.swift index 672405f3a..c48e22399 100644 --- a/Sources/OpenAPIKit30/Document/Document.swift +++ b/Sources/OpenAPIKit30/Document/Document.swift @@ -408,12 +408,46 @@ extension OpenAPI.Document { /// OpenAPIKit only explicitly supports versions that can be found in /// this enum. Other versions may or may not be decodable by /// OpenAPIKit to a certain extent. - public enum Version: String, Codable { - case v3_0_0 = "3.0.0" - case v3_0_1 = "3.0.1" - case v3_0_2 = "3.0.2" - case v3_0_3 = "3.0.3" - case v3_0_4 = "3.0.4" + public enum Version: RawRepresentable, Equatable, Codable { + case v3_0_0 + case v3_0_1 + case v3_0_2 + case v3_0_3 + case v3_0_4 + case v3_0_x(x: Int) + + public init?(rawValue: String) { + switch rawValue { + case "3.0.0": self = .v3_0_0 + case "3.0.1": self = .v3_0_1 + case "3.0.2": self = .v3_0_2 + case "3.0.3": self = .v3_0_3 + case "3.0.4": self = .v3_0_4 + default: + let components = rawValue.split(separator: ".") + guard components.count == 3 else { + return nil + } + guard components[0] == "3", components[1] == "0" else { + return nil + } + guard let patchVersion = Int(components[2], radix: 10) else { + return nil + } + self = .v3_0_x(x: patchVersion) + } + } + + public var rawValue: String { + switch self { + case .v3_0_0: return "3.0.0" + case .v3_0_1: return "3.0.1" + case .v3_0_2: return "3.0.2" + case .v3_0_3: return "3.0.3" + case .v3_0_4: return "3.0.4" + case .v3_0_x(x: let x): return "3.0.\(x)" + } + } } } diff --git a/Tests/OpenAPIKit30Tests/Document/DocumentTests.swift b/Tests/OpenAPIKit30Tests/Document/DocumentTests.swift index d46d29b52..91bc21d3d 100644 --- a/Tests/OpenAPIKit30Tests/Document/DocumentTests.swift +++ b/Tests/OpenAPIKit30Tests/Document/DocumentTests.swift @@ -41,6 +41,44 @@ final class DocumentTests: XCTestCase { ) } + func test_initOASVersions() { + let t1 = OpenAPI.Document.Version.v3_0_0 + XCTAssertEqual(t1.rawValue, "3.0.0") + + let t2 = OpenAPI.Document.Version.v3_0_1 + XCTAssertEqual(t2.rawValue, "3.0.1") + + let t3 = OpenAPI.Document.Version.v3_0_2 + XCTAssertEqual(t3.rawValue, "3.0.2") + + let t4 = OpenAPI.Document.Version.v3_0_3 + XCTAssertEqual(t4.rawValue, "3.0.3") + + let t5 = OpenAPI.Document.Version.v3_0_4 + XCTAssertEqual(t5.rawValue, "3.0.4") + + let t6 = OpenAPI.Document.Version.v3_0_x(x: 8) + XCTAssertEqual(t6.rawValue, "3.0.8") + + let t7 = OpenAPI.Document.Version(rawValue: "3.0.0") + XCTAssertEqual(t7, .v3_0_0) + + let t8 = OpenAPI.Document.Version(rawValue: "3.0.1") + XCTAssertEqual(t8, .v3_0_1) + + let t9 = OpenAPI.Document.Version(rawValue: "3.0.2") + XCTAssertEqual(t9, .v3_0_2) + + let t10 = OpenAPI.Document.Version(rawValue: "3.0.3") + XCTAssertEqual(t10, .v3_0_3) + + let t11 = OpenAPI.Document.Version(rawValue: "3.0.4") + XCTAssertEqual(t11, .v3_0_4) + + let t12 = OpenAPI.Document.Version(rawValue: "3.0.8") + XCTAssertEqual(t12, .v3_0_x(x: 8)) + } + func test_getRoutes() { let pi1 = OpenAPI.PathItem( parameters: [], diff --git a/Tests/OpenAPIKitTests/Document/DocumentTests.swift b/Tests/OpenAPIKitTests/Document/DocumentTests.swift index cbeeb45ec..cb99e21d1 100644 --- a/Tests/OpenAPIKitTests/Document/DocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/DocumentTests.swift @@ -41,6 +41,26 @@ final class DocumentTests: XCTestCase { ) } + func test_initOASVersions() { + let t1 = OpenAPI.Document.Version.v3_1_0 + XCTAssertEqual(t1.rawValue, "3.1.0") + + let t2 = OpenAPI.Document.Version.v3_1_1 + XCTAssertEqual(t2.rawValue, "3.1.1") + + let t3 = OpenAPI.Document.Version.v3_1_x(x: 8) + XCTAssertEqual(t3.rawValue, "3.1.8") + + let t4 = OpenAPI.Document.Version(rawValue: "3.1.0") + XCTAssertEqual(t4, .v3_1_0) + + let t5 = OpenAPI.Document.Version(rawValue: "3.1.1") + XCTAssertEqual(t5, .v3_1_1) + + let t6 = OpenAPI.Document.Version(rawValue: "3.1.8") + XCTAssertEqual(t6, .v3_1_x(x: 8)) + } + func test_getRoutes() { let pi1 = OpenAPI.PathItem( parameters: [], From ace1ef300d317547773fb4624a8c8e2fb6a1ca16 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Mon, 17 Feb 2025 14:19:25 -0600 Subject: [PATCH 103/110] change implementation to not support decoding unknown new OAS versions --- Sources/OpenAPIKit/Document/Document.swift | 12 ++++++++++++ Sources/OpenAPIKit30/Document/Document.swift | 14 +++++++++++++- .../OpenAPIKit30Tests/Document/DocumentTests.swift | 3 ++- Tests/OpenAPIKitTests/Document/DocumentTests.swift | 3 ++- 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/Sources/OpenAPIKit/Document/Document.swift b/Sources/OpenAPIKit/Document/Document.swift index 378991237..8e5ee58df 100644 --- a/Sources/OpenAPIKit/Document/Document.swift +++ b/Sources/OpenAPIKit/Document/Document.swift @@ -424,6 +424,12 @@ extension OpenAPI.Document { /// OpenAPIKit only explicitly supports versions that can be found in /// this enum. Other versions may or may not be decodable by /// OpenAPIKit to a certain extent. + /// + ///**IMPORTANT**: Although the `v3_1_x` case supports arbitrary + /// patch versions, only _known_ patch versions are decodable. That is, if the OpenAPI + /// specification releases a new patch version, OpenAPIKit will see a patch version release + /// explicitly supports decoding documents of that new patch version before said version will + /// succesfully decode as the `v3_1_x` case. public enum Version: RawRepresentable, Equatable, Codable { case v3_1_0 case v3_1_1 @@ -444,6 +450,12 @@ extension OpenAPI.Document { guard let patchVersion = Int(components[2], radix: 10) else { return nil } + // to support newer versions released in the future without a breaking + // change to the enumeration, bump the upper limit here to e.g. 2 or 3 + // or 6: + guard patchVersion > 1 && patchVersion <= 1 else { + return nil + } self = .v3_1_x(x: patchVersion) } } diff --git a/Sources/OpenAPIKit30/Document/Document.swift b/Sources/OpenAPIKit30/Document/Document.swift index c48e22399..c4bc3a127 100644 --- a/Sources/OpenAPIKit30/Document/Document.swift +++ b/Sources/OpenAPIKit30/Document/Document.swift @@ -408,7 +408,13 @@ extension OpenAPI.Document { /// OpenAPIKit only explicitly supports versions that can be found in /// this enum. Other versions may or may not be decodable by /// OpenAPIKit to a certain extent. - public enum Version: RawRepresentable, Equatable, Codable { + /// + ///**IMPORTANT**: Although the `v3_0_x` case supports arbitrary + /// patch versions, only _known_ patch versions are decodable. That is, if the OpenAPI + /// specification releases a new patch version, OpenAPIKit will see a patch version release + /// explicitly supports decoding documents of that new patch version before said version will + /// succesfully decode as the `v3_0_x` case. + public enum Version: RawRepresentable, Equatable, Codable { case v3_0_0 case v3_0_1 case v3_0_2 @@ -434,6 +440,12 @@ extension OpenAPI.Document { guard let patchVersion = Int(components[2], radix: 10) else { return nil } + // to support newer versions released in the future without a breaking + // change to the enumeration, bump the upper limit here to e.g. 5 or 6 + // or 9: + guard patchVersion > 4 && patchVersion <= 4 else { + return nil + } self = .v3_0_x(x: patchVersion) } } diff --git a/Tests/OpenAPIKit30Tests/Document/DocumentTests.swift b/Tests/OpenAPIKit30Tests/Document/DocumentTests.swift index 91bc21d3d..b4b53607a 100644 --- a/Tests/OpenAPIKit30Tests/Document/DocumentTests.swift +++ b/Tests/OpenAPIKit30Tests/Document/DocumentTests.swift @@ -75,8 +75,9 @@ final class DocumentTests: XCTestCase { let t11 = OpenAPI.Document.Version(rawValue: "3.0.4") XCTAssertEqual(t11, .v3_0_4) + // not a known version: let t12 = OpenAPI.Document.Version(rawValue: "3.0.8") - XCTAssertEqual(t12, .v3_0_x(x: 8)) + XCTAssertNil(t12) } func test_getRoutes() { diff --git a/Tests/OpenAPIKitTests/Document/DocumentTests.swift b/Tests/OpenAPIKitTests/Document/DocumentTests.swift index cb99e21d1..3c396377d 100644 --- a/Tests/OpenAPIKitTests/Document/DocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/DocumentTests.swift @@ -57,8 +57,9 @@ final class DocumentTests: XCTestCase { let t5 = OpenAPI.Document.Version(rawValue: "3.1.1") XCTAssertEqual(t5, .v3_1_1) + // not a known version: let t6 = OpenAPI.Document.Version(rawValue: "3.1.8") - XCTAssertEqual(t6, .v3_1_x(x: 8)) + XCTAssertNil(t6) } func test_getRoutes() { From c7c7b8b7b5f35096077a984207551a9046f6564b Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Mon, 17 Feb 2025 14:29:27 -0600 Subject: [PATCH 104/110] test cases --- .../Document/DocumentTests.swift | 44 +++++++++++++++++++ .../Document/DocumentTests.swift | 41 +++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/Tests/OpenAPIKit30Tests/Document/DocumentTests.swift b/Tests/OpenAPIKit30Tests/Document/DocumentTests.swift index b4b53607a..53a890bd7 100644 --- a/Tests/OpenAPIKit30Tests/Document/DocumentTests.swift +++ b/Tests/OpenAPIKit30Tests/Document/DocumentTests.swift @@ -511,6 +511,33 @@ extension DocumentTests { ) } + func test_specifyUknownOpenAPIVersion_encode() throws { + let document = OpenAPI.Document( + openAPIVersion: .v3_0_x(x: 9), + info: .init(title: "API", version: "1.0"), + servers: [], + paths: [:], + components: .noComponents + ) + let encodedDocument = try orderUnstableTestStringFromEncoding(of: document) + + assertJSONEquivalent( + encodedDocument, + """ + { + "info" : { + "title" : "API", + "version" : "1.0" + }, + "openapi" : "3.0.9", + "paths" : { + + } + } + """ + ) + } + func test_specifyOpenAPIVersion_decode() throws { let documentData = """ @@ -539,6 +566,23 @@ extension DocumentTests { ) } + func test_specifyUnknownOpenAPIVersion_decode() throws { + let documentData = + """ + { + "info" : { + "title" : "API", + "version" : "1.0" + }, + "openapi" : "3.0.9", + "paths" : { + + } + } + """.data(using: .utf8)! + XCTAssertThrowsError(try orderUnstableDecode(OpenAPI.Document.self, from: documentData)) { error in XCTAssertEqual(OpenAPI.Error(from: error).localizedDescription, "Inconsistency encountered when parsing `openapi` in the root Document object: Cannot initialize Version from invalid String value 3.0.9.") } + } + func test_specifyServers_encode() throws { let document = OpenAPI.Document( info: .init(title: "API", version: "1.0"), diff --git a/Tests/OpenAPIKitTests/Document/DocumentTests.swift b/Tests/OpenAPIKitTests/Document/DocumentTests.swift index 3c396377d..1de075b87 100644 --- a/Tests/OpenAPIKitTests/Document/DocumentTests.swift +++ b/Tests/OpenAPIKitTests/Document/DocumentTests.swift @@ -513,6 +513,30 @@ extension DocumentTests { ) } + func test_specifyUknownOpenAPIVersion_encode() throws { + let document = OpenAPI.Document( + openAPIVersion: .v3_1_x(x: 9), + info: .init(title: "API", version: "1.0"), + servers: [], + paths: [:], + components: .noComponents + ) + let encodedDocument = try orderUnstableTestStringFromEncoding(of: document) + + assertJSONEquivalent( + encodedDocument, + """ + { + "info" : { + "title" : "API", + "version" : "1.0" + }, + "openapi" : "3.1.9" + } + """ + ) + } + func test_specifyOpenAPIVersion_decode() throws { let documentData = """ @@ -541,6 +565,23 @@ extension DocumentTests { ) } + func test_specifyUnknownOpenAPIVersion_decode() throws { + let documentData = + """ + { + "info" : { + "title" : "API", + "version" : "1.0" + }, + "openapi" : "3.1.9", + "paths" : { + + } + } + """.data(using: .utf8)! + XCTAssertThrowsError(try orderUnstableDecode(OpenAPI.Document.self, from: documentData)) { error in XCTAssertEqual(OpenAPI.Error(from: error).localizedDescription, "Inconsistency encountered when parsing `openapi` in the root Document object: Cannot initialize Version from invalid String value 3.1.9.") } + } + func test_specifyServers_encode() throws { let document = OpenAPI.Document( info: .init(title: "API", version: "1.0"), From 70fcb701628080752b6f7a13f83ff56ab101b2a8 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Mon, 17 Feb 2025 14:35:17 -0600 Subject: [PATCH 105/110] update migration guide --- documentation/v4_migration_guide.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/documentation/v4_migration_guide.md b/documentation/v4_migration_guide.md index 0632ecb5f..d4b4a61a5 100644 --- a/documentation/v4_migration_guide.md +++ b/documentation/v4_migration_guide.md @@ -20,10 +20,16 @@ is now required. Only relevant when compiling OpenAPIKit on macOS: Now v10_15+ is required. ### OpenAPI Specification Versions -The `OpenAPIKit.Document.Version` enum gained `v3_1_1` and the -`OpenAPIKit30.Document.Version` enum gained `v3_0_4`. If you have exhaustive -switches over values of those types then your switch statements will need to be -updated. +The OpenAPIKit module's `OpenAPI.Document.Version` enum gained `v3_1_1` and the +OpenAPIKit30 module's `OpenAPI.Document.Version` enum gained `v3_0_4`. + +The `OpenAPI.Document.Version` enum in both modules gained a new case +(`v3_0_x(x: Int)` and `v3_1_x(x: Int)` respectively) that represents future OAS +versions not released at the time of the given OpenAPIKit release. This allows +non-breaking addition of support for those new versions. + +If you have exhaustive switches over values of those types then your switch +statements will need to be updated. ### Typo corrections The following typo corrections were made to OpenAPIKit code. These amount to From ccbdbba5e65c0e87386d5c5d9cdef2f9c47f4a4f Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Mon, 17 Feb 2025 15:06:01 -0600 Subject: [PATCH 106/110] update external dereferencing note --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bd30e7801..65c0b3823 100644 --- a/README.md +++ b/README.md @@ -339,7 +339,7 @@ try encodeEqual(URL(string: "https://website.com"), AnyCodable(URL(string: "http ### Dereferencing & Resolving #### External References -This is currently only available for OAS 3.1 documents (supported by the `OpenAPIKit` module (as opposed to the `OpenAPIKit30` moudle). External dereferencing does not resolve any local (internal) references, it just loads external references into the Document. It does this by storing any loaded externally referenced objects in the Components Object and transforming the reference being resolved from an external reference to an internal one. That way, you can always run internal dereferencing as a second step if you want a fully dereferenced document, but if you simply wanted to load additional referenced files then you can stop after external dereferencing. +External dereferencing does not resolve any local (internal) references, it just loads external references into the Document. It does this by storing any loaded externally referenced objects in the Components Object and transforming the reference being resolved from an external reference to an internal one. That way, you can always run internal dereferencing as a second step if you want a fully dereferenced document, but if you simply wanted to load additional referenced files then you can stop after external dereferencing. OpenAPIKit leaves it to you to decide how to load external files and where to store the results in the Components Object. It does this by requiring that you provide an implementation of the `ExternalLoader` protocol. You provide a `load` function and a `componentKey` function, both of which accept as input the `URL` to load. A simple mock example implementation from the OpenAPIKit tests will go a long way to showing how the `ExternalLoader` can be set up: From 893529e39d12c018c6b4b2bebec0597e2b996131 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Mon, 17 Feb 2025 15:31:39 -0600 Subject: [PATCH 107/110] sprinkle some references to the generated docs from the README --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 65c0b3823..cb47a8f01 100644 --- a/README.md +++ b/README.md @@ -177,7 +177,7 @@ Each route is an entry in the document's `OpenAPI.PathItem.Map`. The keys of thi Each endpoint on a route is defined by an `OpenAPI.Operation`. Among other things, this operation can specify the parameters (path, query, header, etc.), request body, and response bodies/codes supported by the given endpoint. #### Request/Response Bodies -Request and response bodies can be defined in great detail using OpenAPI's derivative of the JSON Schema specification. This library uses the `JSONSchema` type for such schema definitions. +Request and response bodies can be defined in great detail using OpenAPI's derivative of the JSON Schema specification. This library uses the [`JSONSchema`](https://mattpolzin.github.io/OpenAPIKit/documentation/openapikit/jsonschema) type for such schema definitions. #### Schemas **Fundamental types** are specified as `JSONSchema.integer`, `JSONSchema.string`, `JSONSchema.boolean`, etc. @@ -214,12 +214,12 @@ JSONSchema.object( Take a look at the [OpenAPIKit Schema Object](./documentation/schema_object.md) documentation for more information. #### OpenAPI References -The `OpenAPI.Reference` type represents the OpenAPI specification's reference support that is essentially just JSON Reference specification compliant but with the ability to override summaries and descriptions at the reference site where appropriate. +The [`OpenAPI.Reference`](https://mattpolzin.github.io/OpenAPIKit/documentation/openapikit/openapi/reference) type represents the OpenAPI specification's reference support that is essentially just JSON Reference specification compliant but with the ability to override summaries and descriptions at the reference site where appropriate. For details on the underlying reference support, see the next section on the `JSONReference` type. #### JSON References -The `JSONReference` type allows you to work with OpenAPIDocuments that store some of their information in the shared Components Object dictionary or even external files. Only documents where all references point to the Components Object can be dereferenced currently, but you can encode and decode all references. +The [`JSONReference`](https://mattpolzin.github.io/OpenAPIKit/documentation/openapikit/jsonreference) type allows you to work with OpenAPIDocuments that store some of their information in the shared Components Object dictionary or even external files. Only documents where all references point to the Components Object can be dereferenced currently, but you can encode and decode all references. You can create an external reference with `JSONReference.external(URL)`. Internal references usually refer to an object in the Components Object dictionary and are constructed with `JSONReference.component(named:)`. If you need to refer to something in the current file but not in the Components Object, you can use `JSONReference.internal(path:)`. @@ -242,7 +242,7 @@ Note that this looks a component up in the Components Object but it does not tra #### Security Requirements In the OpenAPI specifcation, a security requirement (like can be found on the root Document or on Operations) is a dictionary where each key is the name of a security scheme found in the Components Object and each value is an array of applicable scopes (which is of course only a non-empty array when the security scheme type is one for which "scopes" are relevant). -OpenAPIKit defines the `SecurityRequirement` typealias as a dictionary with `JSONReference` keys; These references point to the Components Object and provide a slightly stronger contract than the String values required by the OpenAPI specification. Naturally, these are encoded to JSON/YAML as String values rather than JSON References to maintain compliance with the OpenAPI Specification. +OpenAPIKit defines the [`SecurityRequirement`](https://mattpolzin.github.io/OpenAPIKit/documentation/openapikit/openapi/securityrequirement) typealias as a dictionary with `JSONReference` keys; These references point to the Components Object and provide a slightly stronger contract than the String values required by the OpenAPI specification. Naturally, these are encoded to JSON/YAML as String values rather than JSON References to maintain compliance with the OpenAPI Specification. To give an example, let's say you want to describe OAuth 2.0 authentication via the implicit flow. First, define a Security Scheme: ```swift @@ -291,7 +291,7 @@ let document = OpenAPI.Document( #### Specification Extensions 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. -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. +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. If you wish to disable decoding/encoding of vendor extensions for performance reasons, you can configure the Encoder and Decoder using their `userInfo`: ```swift @@ -341,7 +341,7 @@ try encodeEqual(URL(string: "https://website.com"), AnyCodable(URL(string: "http #### External References External dereferencing does not resolve any local (internal) references, it just loads external references into the Document. It does this by storing any loaded externally referenced objects in the Components Object and transforming the reference being resolved from an external reference to an internal one. That way, you can always run internal dereferencing as a second step if you want a fully dereferenced document, but if you simply wanted to load additional referenced files then you can stop after external dereferencing. -OpenAPIKit leaves it to you to decide how to load external files and where to store the results in the Components Object. It does this by requiring that you provide an implementation of the `ExternalLoader` protocol. You provide a `load` function and a `componentKey` function, both of which accept as input the `URL` to load. A simple mock example implementation from the OpenAPIKit tests will go a long way to showing how the `ExternalLoader` can be set up: +OpenAPIKit leaves it to you to decide how to load external files and where to store the results in the Components Object. It does this by requiring that you provide an implementation of the [`ExternalLoader`](https://mattpolzin.github.io/OpenAPIKit/documentation/openapikit/externalloader) protocol. You provide a `load` function and a `componentKey` function, both of which accept as input the `URL` to load. A simple mock example implementation from the OpenAPIKit tests will go a long way to showing how the `ExternalLoader` can be set up: ```swift struct ExampleLoader: ExternalLoader { @@ -382,9 +382,9 @@ Once you have an `ExternalLoader`, you can call an `OpenAPI.Document`'s `externa If you have some information that you want to pass back to yourself from the `load()` function, you can specify any type you want as the `Message` type and return any number of messages from each `load()` function execution. These messages could be warnings, additional information about the files that each newly loaded Component came from, etc. If you want to tie some information about file loading to new Components in your messages, you can use the `componentKey()` function to get the key the new Component will be found under once external dereferencing is complete. #### Internal References -In addition to looking something up in the `Components` object, you can entirely derefererence many OpenAPIKit types. A dereferenced type has had all of its references looked up (and all of its properties' references, all the way down). +In addition to looking something up in the [`Components`](https://mattpolzin.github.io/OpenAPIKit/documentation/openapikit/openapi/components) object, you can entirely derefererence many OpenAPIKit types. A dereferenced type has had all of its references looked up (and all of its properties' references, all the way down). -You use a value's `dereferenced(in:)` method to fully dereference it. +You use a value's [`dereferenced(in:)`](https://mattpolzin.github.io/OpenAPIKit/documentation/openapikit/locallydereferenceable) method to fully dereference it. You can even dereference the whole document with the `OpenAPI.Document` `locallyDereferenced()` method. As the name implies, you can only derefence whole documents that are contained within one file (which is another way of saying that all references are "local"). Specifically, all references must be located within the document's Components Object. External dereferencing is done as a separeate step, but you can first dereference externally and then dereference internally if you'd like to perform both. From 7312c6b16a1aac00a6e8523c4029ea0ef473e14a Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Mon, 17 Feb 2025 16:48:13 -0600 Subject: [PATCH 108/110] Address some compiler warnings about retroactive protocol conformance --- .../OpenAPIKit/AnyCodable+Validatable.swift | 2 +- .../Either/Either+Validatable.swift | 2 +- .../OrderedDictionary+Validatable.swift | 2 +- .../Parameter/ParameterSchemaContext.swift | 2 +- Sources/OpenAPIKit/Path Item/PathItem.swift | 18 ------------- Sources/OpenAPIKit/Response/Response.swift | 26 ------------------- .../OpenAPIKit/URLTemplate+Validatable.swift | 2 +- .../Parameter/ParameterSchemaContext.swift | 2 +- Sources/OpenAPIKit30/Path Item/PathItem.swift | 18 ------------- Sources/OpenAPIKit30/Response/Response.swift | 26 ------------------- Sources/OpenAPIKitCore/Shared/Path.swift | 18 +++++++++++++ .../Shared/ResponseStatusCode.swift | 26 +++++++++++++++++++ 12 files changed, 50 insertions(+), 94 deletions(-) diff --git a/Sources/OpenAPIKit/AnyCodable+Validatable.swift b/Sources/OpenAPIKit/AnyCodable+Validatable.swift index 289a18ebe..bd6b0f8a1 100644 --- a/Sources/OpenAPIKit/AnyCodable+Validatable.swift +++ b/Sources/OpenAPIKit/AnyCodable+Validatable.swift @@ -7,4 +7,4 @@ import OpenAPIKitCore -extension AnyCodable: Validatable {} +extension AnyCodable: @retroactive Validatable {} diff --git a/Sources/OpenAPIKit/Either/Either+Validatable.swift b/Sources/OpenAPIKit/Either/Either+Validatable.swift index 69f4f24e6..f180022e3 100644 --- a/Sources/OpenAPIKit/Either/Either+Validatable.swift +++ b/Sources/OpenAPIKit/Either/Either+Validatable.swift @@ -7,4 +7,4 @@ import OpenAPIKitCore -extension Either: Validatable where A: Validatable, B: Validatable {} +extension Either: @retroactive Validatable where A: Validatable, B: Validatable {} diff --git a/Sources/OpenAPIKit/OrderedDictionary+Validatable.swift b/Sources/OpenAPIKit/OrderedDictionary+Validatable.swift index d6a7a0f65..185ff4f7e 100644 --- a/Sources/OpenAPIKit/OrderedDictionary+Validatable.swift +++ b/Sources/OpenAPIKit/OrderedDictionary+Validatable.swift @@ -7,4 +7,4 @@ import OpenAPIKitCore -extension OrderedDictionary: Validatable where Value: Validatable {} +extension OrderedDictionary: @retroactive Validatable where Value: Validatable {} diff --git a/Sources/OpenAPIKit/Parameter/ParameterSchemaContext.swift b/Sources/OpenAPIKit/Parameter/ParameterSchemaContext.swift index f8828da2e..01b18a598 100644 --- a/Sources/OpenAPIKit/Parameter/ParameterSchemaContext.swift +++ b/Sources/OpenAPIKit/Parameter/ParameterSchemaContext.swift @@ -220,4 +220,4 @@ extension OpenAPI.Parameter.SchemaContext { } } -extension OpenAPI.Parameter.SchemaContext.Style: Validatable {} +extension OpenAPI.Parameter.SchemaContext.Style: @retroactive Validatable {} diff --git a/Sources/OpenAPIKit/Path Item/PathItem.swift b/Sources/OpenAPIKit/Path Item/PathItem.swift index 09d2bf650..6db744f78 100644 --- a/Sources/OpenAPIKit/Path Item/PathItem.swift +++ b/Sources/OpenAPIKit/Path Item/PathItem.swift @@ -236,24 +236,6 @@ extension OpenAPI.PathItem : OpenAPISummarizable { // MARK: - Codable -extension OpenAPI.Path: Encodable { - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - - try container.encode(rawValue) - } -} - -extension OpenAPI.Path: Decodable { - public init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - - let rawValue = try container.decode(String.self) - - self.init(rawValue: rawValue) - } -} - extension OpenAPI.PathItem: Encodable { public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) diff --git a/Sources/OpenAPIKit/Response/Response.swift b/Sources/OpenAPIKit/Response/Response.swift index 2124012c3..d8b3f3000 100644 --- a/Sources/OpenAPIKit/Response/Response.swift +++ b/Sources/OpenAPIKit/Response/Response.swift @@ -199,30 +199,4 @@ extension OpenAPI.Response: Decodable { } } -extension OpenAPI.Response.StatusCode: Encodable { - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - - try container.encode(self.rawValue) - } -} - -extension OpenAPI.Response.StatusCode: Decodable { - public init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let strVal = try container.decode(String.self) - let val = OpenAPI.Response.StatusCode(rawValue: strVal) - - guard let value = val else { - throw InconsistencyError( - subjectName: "status code", - details: "Expected the status code to be either an Int, a range like '1XX', or 'default' but found \(strVal) instead", - codingPath: decoder.codingPath - ) - } - - self = value - } -} - extension OpenAPI.Response: Validatable {} diff --git a/Sources/OpenAPIKit/URLTemplate+Validatable.swift b/Sources/OpenAPIKit/URLTemplate+Validatable.swift index a699d094c..6ebb978ed 100644 --- a/Sources/OpenAPIKit/URLTemplate+Validatable.swift +++ b/Sources/OpenAPIKit/URLTemplate+Validatable.swift @@ -7,4 +7,4 @@ import OpenAPIKitCore -extension URLTemplate: Validatable {} +extension URLTemplate: @retroactive Validatable {} diff --git a/Sources/OpenAPIKit30/Parameter/ParameterSchemaContext.swift b/Sources/OpenAPIKit30/Parameter/ParameterSchemaContext.swift index 7dddf716d..a948b51b0 100644 --- a/Sources/OpenAPIKit30/Parameter/ParameterSchemaContext.swift +++ b/Sources/OpenAPIKit30/Parameter/ParameterSchemaContext.swift @@ -221,4 +221,4 @@ extension OpenAPI.Parameter.SchemaContext { } } -extension OpenAPI.Parameter.SchemaContext.Style: Validatable {} +extension OpenAPI.Parameter.SchemaContext.Style: @retroactive Validatable {} diff --git a/Sources/OpenAPIKit30/Path Item/PathItem.swift b/Sources/OpenAPIKit30/Path Item/PathItem.swift index 463f5610b..518818192 100644 --- a/Sources/OpenAPIKit30/Path Item/PathItem.swift +++ b/Sources/OpenAPIKit30/Path Item/PathItem.swift @@ -218,24 +218,6 @@ extension OpenAPI.PathItem { // MARK: - Codable -extension OpenAPI.Path: Encodable { - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - - try container.encode(rawValue) - } -} - -extension OpenAPI.Path: Decodable { - public init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - - let rawValue = try container.decode(String.self) - - self.init(rawValue: rawValue) - } -} - extension OpenAPI.PathItem: Encodable { public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) diff --git a/Sources/OpenAPIKit30/Response/Response.swift b/Sources/OpenAPIKit30/Response/Response.swift index 31c990437..3af8733d5 100644 --- a/Sources/OpenAPIKit30/Response/Response.swift +++ b/Sources/OpenAPIKit30/Response/Response.swift @@ -189,30 +189,4 @@ extension OpenAPI.Response: Decodable { } } -extension OpenAPI.Response.StatusCode: Encodable { - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - - try container.encode(self.rawValue) - } -} - -extension OpenAPI.Response.StatusCode: Decodable { - public init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let strVal = try container.decode(String.self) - let val = OpenAPI.Response.StatusCode(rawValue: strVal) - - guard let value = val else { - throw InconsistencyError( - subjectName: "status code", - details: "Expected the status code to be either an Int, a range like '1XX', or 'default' but found \(strVal) instead", - codingPath: decoder.codingPath - ) - } - - self = value - } -} - extension OpenAPI.Response: Validatable {} diff --git a/Sources/OpenAPIKitCore/Shared/Path.swift b/Sources/OpenAPIKitCore/Shared/Path.swift index 8e73ad831..d3d84916f 100644 --- a/Sources/OpenAPIKitCore/Shared/Path.swift +++ b/Sources/OpenAPIKitCore/Shared/Path.swift @@ -43,3 +43,21 @@ extension Shared.Path: ExpressibleByStringLiteral { self.init(rawValue: value) } } + +extension Shared.Path: Encodable { + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + + try container.encode(rawValue) + } +} + +extension Shared.Path: Decodable { + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + + let rawValue = try container.decode(String.self) + + self.init(rawValue: rawValue) + } +} diff --git a/Sources/OpenAPIKitCore/Shared/ResponseStatusCode.swift b/Sources/OpenAPIKitCore/Shared/ResponseStatusCode.swift index 6bdcd7476..82df5365e 100644 --- a/Sources/OpenAPIKitCore/Shared/ResponseStatusCode.swift +++ b/Sources/OpenAPIKitCore/Shared/ResponseStatusCode.swift @@ -106,3 +106,29 @@ extension Shared.ResponseStatusCode: ExpressibleByIntegerLiteral { warnings = [] } } + +extension Shared.ResponseStatusCode: Encodable { + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + + try container.encode(self.rawValue) + } +} + +extension Shared.ResponseStatusCode: Decodable { + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let strVal = try container.decode(String.self) + let val = Shared.ResponseStatusCode(rawValue: strVal) + + guard let value = val else { + throw InconsistencyError( + subjectName: "status code", + details: "Expected the status code to be either an Int, a range like '1XX', or 'default' but found \(strVal) instead", + codingPath: decoder.codingPath + ) + } + + self = value + } +} From a4a34416e6b4586dbbe9e5d652f52485d7963005 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 18 Feb 2025 08:49:27 -0600 Subject: [PATCH 109/110] undo retroactive annotations because older compiler versions don't know about it --- Sources/OpenAPIKit/AnyCodable+Validatable.swift | 2 +- Sources/OpenAPIKit/Either/Either+Validatable.swift | 2 +- Sources/OpenAPIKit/OrderedDictionary+Validatable.swift | 2 +- Sources/OpenAPIKit/Parameter/ParameterSchemaContext.swift | 2 +- Sources/OpenAPIKit/URLTemplate+Validatable.swift | 2 +- Sources/OpenAPIKit30/Parameter/ParameterSchemaContext.swift | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/OpenAPIKit/AnyCodable+Validatable.swift b/Sources/OpenAPIKit/AnyCodable+Validatable.swift index bd6b0f8a1..289a18ebe 100644 --- a/Sources/OpenAPIKit/AnyCodable+Validatable.swift +++ b/Sources/OpenAPIKit/AnyCodable+Validatable.swift @@ -7,4 +7,4 @@ import OpenAPIKitCore -extension AnyCodable: @retroactive Validatable {} +extension AnyCodable: Validatable {} diff --git a/Sources/OpenAPIKit/Either/Either+Validatable.swift b/Sources/OpenAPIKit/Either/Either+Validatable.swift index f180022e3..69f4f24e6 100644 --- a/Sources/OpenAPIKit/Either/Either+Validatable.swift +++ b/Sources/OpenAPIKit/Either/Either+Validatable.swift @@ -7,4 +7,4 @@ import OpenAPIKitCore -extension Either: @retroactive Validatable where A: Validatable, B: Validatable {} +extension Either: Validatable where A: Validatable, B: Validatable {} diff --git a/Sources/OpenAPIKit/OrderedDictionary+Validatable.swift b/Sources/OpenAPIKit/OrderedDictionary+Validatable.swift index 185ff4f7e..d6a7a0f65 100644 --- a/Sources/OpenAPIKit/OrderedDictionary+Validatable.swift +++ b/Sources/OpenAPIKit/OrderedDictionary+Validatable.swift @@ -7,4 +7,4 @@ import OpenAPIKitCore -extension OrderedDictionary: @retroactive Validatable where Value: Validatable {} +extension OrderedDictionary: Validatable where Value: Validatable {} diff --git a/Sources/OpenAPIKit/Parameter/ParameterSchemaContext.swift b/Sources/OpenAPIKit/Parameter/ParameterSchemaContext.swift index 01b18a598..f8828da2e 100644 --- a/Sources/OpenAPIKit/Parameter/ParameterSchemaContext.swift +++ b/Sources/OpenAPIKit/Parameter/ParameterSchemaContext.swift @@ -220,4 +220,4 @@ extension OpenAPI.Parameter.SchemaContext { } } -extension OpenAPI.Parameter.SchemaContext.Style: @retroactive Validatable {} +extension OpenAPI.Parameter.SchemaContext.Style: Validatable {} diff --git a/Sources/OpenAPIKit/URLTemplate+Validatable.swift b/Sources/OpenAPIKit/URLTemplate+Validatable.swift index 6ebb978ed..a699d094c 100644 --- a/Sources/OpenAPIKit/URLTemplate+Validatable.swift +++ b/Sources/OpenAPIKit/URLTemplate+Validatable.swift @@ -7,4 +7,4 @@ import OpenAPIKitCore -extension URLTemplate: @retroactive Validatable {} +extension URLTemplate: Validatable {} diff --git a/Sources/OpenAPIKit30/Parameter/ParameterSchemaContext.swift b/Sources/OpenAPIKit30/Parameter/ParameterSchemaContext.swift index a948b51b0..7dddf716d 100644 --- a/Sources/OpenAPIKit30/Parameter/ParameterSchemaContext.swift +++ b/Sources/OpenAPIKit30/Parameter/ParameterSchemaContext.swift @@ -221,4 +221,4 @@ extension OpenAPI.Parameter.SchemaContext { } } -extension OpenAPI.Parameter.SchemaContext.Style: @retroactive Validatable {} +extension OpenAPI.Parameter.SchemaContext.Style: Validatable {} From 1e5db25c9f908f5738e3ac4d9630ea893af39470 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 18 Feb 2025 10:44:43 -0600 Subject: [PATCH 110/110] update dependencies --- Package.resolved | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Package.resolved b/Package.resolved index 8ea187bda..95608ba82 100644 --- a/Package.resolved +++ b/Package.resolved @@ -5,14 +5,14 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-docc-plugin", "state" : { - "revision" : "26ac5758409154cc448d7ab82389c520fa8a8247", - "version" : "1.3.0" + "revision" : "85e4bb4e1cd62cec64a4b8e769dcefdf0c5b9d64", + "version" : "1.4.3" } }, { "identity" : "swift-docc-symbolkit", "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-docc-symbolkit", + "location" : "https://github.com/swiftlang/swift-docc-symbolkit", "state" : { "revision" : "b45d1f2ed151d057b54504d653e0da5552844e34", "version" : "1.0.0" @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/jpsim/Yams.git", "state" : { - "revision" : "8a835d918245ca22f36663dd3862138805d7f707", - "version" : "5.1.0" + "revision" : "2688707e563b44d7d87c29ba6c5ca04ce86ae58b", + "version" : "5.3.0" } } ],