From e599b4531c08986fe1f80e446f8e83e5d7cc1182 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Wed, 24 Apr 2024 10:52:18 -0500 Subject: [PATCH 01/14] 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 02/14] 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 03/14] 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 04/14] 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 05/14] 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 06/14] 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 07/14] 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 08/14] 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 09/14] 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 10/14] 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 11/14] 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 12/14] 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 13/14] 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 14/14] 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