Skip to content

Commit

Permalink
pipe messages through all external dereferencing
Browse files Browse the repository at this point in the history
  • Loading branch information
mattpolzin committed Apr 24, 2024
1 parent a49d111 commit e599b45
Show file tree
Hide file tree
Showing 24 changed files with 190 additions and 129 deletions.
30 changes: 16 additions & 14 deletions Sources/OpenAPIKit/Components Object/Components.swift
Original file line number Diff line number Diff line change
Expand Up @@ -320,10 +320,10 @@ extension OpenAPI.Components {
}

extension OpenAPI.Components {
internal mutating func externallyDereference<Loader: ExternalLoader>(with loader: Loader.Type, depth: ExternalDereferenceDepth = .iterations(1)) async throws {
internal mutating func externallyDereference<Loader: ExternalLoader>(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
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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)
}
}
}
Expand Down
13 changes: 8 additions & 5 deletions Sources/OpenAPIKit/Content/DereferencedContent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,28 +77,31 @@ extension OpenAPI.Content: LocallyDereferenceable {
}

extension OpenAPI.Content: ExternallyDereferenceable {
public func externallyDereferenced<Loader: ExternalLoader>(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) {
public func externallyDereferenced<Loader: ExternalLoader>(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)
}
}
8 changes: 5 additions & 3 deletions Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,17 @@ extension OpenAPI.Content.Encoding: LocallyDereferenceable {
}

extension OpenAPI.Content.Encoding: ExternallyDereferenceable {
public func externallyDereferenced<Loader: ExternalLoader>(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) {
public func externallyDereferenced<Loader: ExternalLoader>(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(
Expand All @@ -77,6 +79,6 @@ extension OpenAPI.Content.Encoding: ExternallyDereferenceable {
allowReserved: allowReserved
)

return (newEncoding, newComponents)
return (newEncoding, newComponents, newMessages)
}
}
12 changes: 7 additions & 5 deletions Sources/OpenAPIKit/Document/Document.swift
Original file line number Diff line number Diff line change
Expand Up @@ -362,24 +362,26 @@ extension OpenAPI.Document {
return try DereferencedDocument(self)
}

public mutating func externallyDereference<Loader: ExternalLoader>(with loader: Loader.Type, depth: ExternalDereferenceDepth = .iterations(1)) async throws {
public mutating func externallyDereference<Loader: ExternalLoader>(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
}
}

Expand Down
10 changes: 5 additions & 5 deletions Sources/OpenAPIKit/Either/Either+ExternallyDereferenceable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ import OpenAPIKitCore
// MARK: - ExternallyDereferenceable
extension Either: ExternallyDereferenceable where A: ExternallyDereferenceable, B: ExternallyDereferenceable {

public func externallyDereferenced<Loader: ExternalLoader>(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) {
public func externallyDereferenced<Loader: ExternalLoader>(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)
}
}
}
4 changes: 2 additions & 2 deletions Sources/OpenAPIKit/Example.swift
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,8 @@ extension OpenAPI.Example: LocallyDereferenceable {
}

extension OpenAPI.Example: ExternallyDereferenceable {
public func externallyDereferenced<Loader: ExternalLoader>(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) {
return (self, .init())
public func externallyDereferenced<Loader: ExternalLoader>(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) {
return (self, .init(), [])
}
}

Expand Down
10 changes: 8 additions & 2 deletions Sources/OpenAPIKit/ExternalLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>(_: URL) async throws -> T where T: Decodable
static func load<T>(_: 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
Expand All @@ -30,5 +36,5 @@ public protocol ExternalLoader {
}

public protocol ExternallyDereferenceable {
func externallyDereferenced<Loader: ExternalLoader>(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components)
func externallyDereferenced<Loader: ExternalLoader>(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message])
}
11 changes: 7 additions & 4 deletions Sources/OpenAPIKit/Header/DereferencedHeader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,24 +84,27 @@ extension OpenAPI.Header: LocallyDereferenceable {
}

extension OpenAPI.Header: ExternallyDereferenceable {
public func externallyDereferenced<Loader: ExternalLoader>(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) {
public func externallyDereferenced<Loader: ExternalLoader>(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:
// let (newSchemaOrContent, components) = try await schemaOrContent.externallyDereferenced(with: loader)

let newSchemaOrContent: Either<SchemaContext, OpenAPI.Content.Map>
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(
Expand All @@ -112,6 +115,6 @@ extension OpenAPI.Header: ExternallyDereferenceable {
vendorExtensions: vendorExtensions
)

return (newHeader, newComponents)
return (newHeader, newComponents, newMessages)
}
}
14 changes: 7 additions & 7 deletions Sources/OpenAPIKit/JSONReference.swift
Original file line number Diff line number Diff line change
Expand Up @@ -538,16 +538,16 @@ extension JSONReference: LocallyDereferenceable where ReferenceType: LocallyDere
}

extension JSONReference: ExternallyDereferenceable where ReferenceType: ExternallyDereferenceable & Decodable & Equatable {
public func externallyDereferenced<Loader: ExternalLoader>(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) {
public func externallyDereferenced<Loader: ExternalLoader>(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)
}
}
}
Expand Down Expand Up @@ -580,9 +580,9 @@ extension OpenAPI.Reference: LocallyDereferenceable where ReferenceType: Locally
}

extension OpenAPI.Reference: ExternallyDereferenceable where ReferenceType: ExternallyDereferenceable & Decodable & Equatable {
public func externallyDereferenced<Loader: ExternalLoader>(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<Loader: ExternalLoader>(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)
}
}

Expand Down
6 changes: 3 additions & 3 deletions Sources/OpenAPIKit/Link.swift
Original file line number Diff line number Diff line change
Expand Up @@ -290,13 +290,13 @@ extension OpenAPI.Link: LocallyDereferenceable {
}

extension OpenAPI.Link: ExternallyDereferenceable {
public func externallyDereferenced<Loader: ExternalLoader>(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) {
let (newServer, newComponents) = try await server.externallyDereferenced(with: loader)
public func externallyDereferenced<Loader: ExternalLoader>(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)
}
}

Expand Down
29 changes: 14 additions & 15 deletions Sources/OpenAPIKit/Operation/DereferencedOperation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,41 +126,40 @@ extension OpenAPI.Operation: LocallyDereferenceable {
}

extension OpenAPI.Operation: ExternallyDereferenceable {
public func externallyDereferenced<Loader: ExternalLoader>(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components) {
public func externallyDereferenced<Loader: ExternalLoader>(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)
}
}
Loading

0 comments on commit e599b45

Please sign in to comment.