From e599b4531c08986fe1f80e446f8e83e5d7cc1182 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Wed, 24 Apr 2024 10:52:18 -0500 Subject: [PATCH] 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 {