Skip to content

Commit 0df6960

Browse files
committed
update OAS 3.0 components merge function. fix tests after refactor.
1 parent e4a2b52 commit 0df6960

File tree

4 files changed

+69
-48
lines changed

4 files changed

+69
-48
lines changed

Sources/OpenAPIKit30/Components Object/Components.swift

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -78,18 +78,47 @@ extension OpenAPI.Components {
7878
public let newComponent: String
7979
}
8080

81+
private func detectCollision<T: Equatable>(type: String) throws -> (_ old: T, _ new: T) throws -> T {
82+
return { old, new in
83+
// theoretically we can detect collisions here, but we would need to compare
84+
// for equality up-to but not including the difference between an external and
85+
// internal reference which is not supported yet.
86+
// if(old == new) { return old }
87+
// throw ComponentCollision(componentType: type, existingComponent: String(describing:old), newComponent: String(describing:new))
88+
89+
// Given we aren't ensuring there are no collisions, the old version is going to be
90+
// the one more likely to have been _further_ dereferenced than the new record, so
91+
// we keep that version.
92+
return old
93+
}
94+
}
95+
8196
public mutating func merge(_ other: OpenAPI.Components) throws {
82-
try schemas.merge(other.schemas, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "schema", existingComponent: String(describing: a), newComponent: String(describing: b)) })
83-
try responses.merge(other.responses, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "responses", existingComponent: String(describing: a), newComponent: String(describing: b)) })
84-
try parameters.merge(other.parameters, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "parameters", existingComponent: String(describing: a), newComponent: String(describing: b)) })
85-
try examples.merge(other.examples, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "examples", existingComponent: String(describing: a), newComponent: String(describing: b)) })
86-
try requestBodies.merge(other.requestBodies, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "requestBodies", existingComponent: String(describing: a), newComponent: String(describing: b)) })
87-
try headers.merge(other.headers, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "headers", existingComponent: String(describing: a), newComponent: String(describing: b)) })
88-
try securitySchemes.merge(other.securitySchemes, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "securitySchemes", existingComponent: String(describing: a), newComponent: String(describing: b)) })
89-
try links.merge(other.links, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "links", existingComponent: String(describing: a), newComponent: String(describing: b)) })
90-
try callbacks.merge(other.callbacks, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "callbacks", existingComponent: String(describing: a), newComponent: String(describing: b)) })
91-
try pathItems.merge(other.pathItems, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "pathItems", existingComponent: String(describing: a), newComponent: String(describing: b)) })
92-
try vendorExtensions.merge(other.vendorExtensions, uniquingKeysWith: { a, b in throw ComponentCollision(componentType: "vendorExtensions", existingComponent: String(describing: a), newComponent: String(describing: b)) })
97+
try schemas.merge(other.schemas, uniquingKeysWith: detectCollision(type: "schema"))
98+
try responses.merge(other.responses, uniquingKeysWith: detectCollision(type: "responses"))
99+
try parameters.merge(other.parameters, uniquingKeysWith: detectCollision(type: "parameters"))
100+
try examples.merge(other.examples, uniquingKeysWith: detectCollision(type: "examples"))
101+
try requestBodies.merge(other.requestBodies, uniquingKeysWith: detectCollision(type: "requestBodies"))
102+
try headers.merge(other.headers, uniquingKeysWith: detectCollision(type: "headers"))
103+
try securitySchemes.merge(other.securitySchemes, uniquingKeysWith: detectCollision(type: "securitySchemes"))
104+
try links.merge(other.links, uniquingKeysWith: detectCollision(type: "links"))
105+
try callbacks.merge(other.callbacks, uniquingKeysWith: detectCollision(type: "callbacks"))
106+
try pathItems.merge(other.pathItems, uniquingKeysWith: detectCollision(type: "pathItems"))
107+
try vendorExtensions.merge(other.vendorExtensions, uniquingKeysWith: detectCollision(type: "vendorExtensions"))
108+
}
109+
110+
/// Sort the components within each type by the component key.
111+
public mutating func sort() {
112+
schemas.sortKeys()
113+
responses.sortKeys()
114+
parameters.sortKeys()
115+
examples.sortKeys()
116+
requestBodies.sortKeys()
117+
headers.sortKeys()
118+
securitySchemes.sortKeys()
119+
links.sortKeys()
120+
callbacks.sortKeys()
121+
pathItems.sortKeys()
93122
}
94123
}
95124

Tests/OpenAPIKit30Tests/Document/ExternalDereferencingDocumentTests.swift

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import Foundation
66
import Yams
7-
import OpenAPIKit
7+
import OpenAPIKit30
88
import XCTest
99

1010
final class ExternalDereferencingDocumentTests: XCTestCase {
@@ -25,15 +25,17 @@ final class ExternalDereferencingDocumentTests: XCTestCase {
2525
// to keep track of where a reference was loaded from. This test makes sure
2626
// the following strategy of using vendor extensions works.
2727
if var extendable = decoded as? VendorExtendable {
28-
extendable.vendorExtensions["x-source-url"] = AnyCodable(url)
28+
//TODO: revisit vendor extensions mutability
29+
#warning("revisit vendor extensions mutability")
30+
// extendable.vendorExtensions["x-source-url"] = AnyCodable(url)
2931
finished = extendable as! T
3032
} else {
3133
finished = decoded
3234
}
3335
return finished
3436
}
3537

36-
static func componentKey<T>(type: T.Type, at url: URL) throws -> OpenAPIKit.OpenAPI.ComponentKey {
38+
static func componentKey<T>(type: T.Type, at url: URL) throws -> OpenAPIKit30.OpenAPI.ComponentKey {
3739
// do anything you want here to determine what key the new component should be stored at.
3840
// for the example, we will just transform the URL into a valid components key:
3941
let urlString = url.pathComponents.dropFirst()
@@ -43,7 +45,7 @@ final class ExternalDereferencingDocumentTests: XCTestCase {
4345
}
4446

4547
/// Mock up some data, just for the example.
46-
static func mockData(_ key: OpenAPIKit.OpenAPI.ComponentKey) async throws -> Data {
48+
static func mockData(_ key: OpenAPIKit30.OpenAPI.ComponentKey) async throws -> Data {
4749
return try XCTUnwrap(files[key.rawValue])
4850
}
4951

@@ -217,9 +219,6 @@ final class ExternalDereferencingDocumentTests: XCTestCase {
217219
),
218220
"/webhook": .reference(.external(URL(string: "file://./paths/webhook.json")!))
219221
],
220-
webhooks: [
221-
"webhook": .reference(.external(URL(string: "file://./paths/webhook.json")!))
222-
],
223222
components: .init(
224223
schemas: [
225224
"name_param": .reference(.external(URL(string: "file://./schemas/string_param.json")!))
@@ -233,17 +232,17 @@ final class ExternalDereferencingDocumentTests: XCTestCase {
233232
encoder.outputFormatting = .prettyPrinted
234233

235234
var docCopy1 = document
236-
try await docCopy1.externallyDereference(in: ExampleLoader.self)
237-
try await docCopy1.externallyDereference(in: ExampleLoader.self)
238-
try await docCopy1.externallyDereference(in: ExampleLoader.self)
235+
try await docCopy1.externallyDereference(with: ExampleLoader.self)
236+
try await docCopy1.externallyDereference(with: ExampleLoader.self)
237+
try await docCopy1.externallyDereference(with: ExampleLoader.self)
239238
docCopy1.components.sort()
240239

241240
var docCopy2 = document
242-
try await docCopy2.externallyDereference(in: ExampleLoader.self, depth: 3)
241+
try await docCopy2.externallyDereference(with: ExampleLoader.self, depth: 3)
243242
docCopy2.components.sort()
244243

245244
var docCopy3 = document
246-
try await docCopy3.externallyDereference(in: ExampleLoader.self, depth: .full)
245+
try await docCopy3.externallyDereference(with: ExampleLoader.self, depth: .full)
247246
docCopy3.components.sort()
248247

249248
XCTAssertEqual(docCopy1, docCopy2)

Tests/OpenAPIKit30Tests/JSONReferenceTests.swift

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,19 @@ extension JSONReferenceTests {
339339
struct ReferenceWrapper: Codable, Equatable {
340340
let reference: JSONReference<JSONSchema>
341341
}
342+
343+
struct SchemaLoader: ExternalLoader {
344+
static func load<T>(_ url: URL) -> T where T: Decodable {
345+
return JSONSchema.string as! T
346+
}
347+
348+
static func componentKey<T>(type: T.Type, at url: URL) throws -> OpenAPI.ComponentKey {
349+
return try .forceInit(rawValue: url.absoluteString
350+
.replacingOccurrences(of: "/", with: "_")
351+
.replacingOccurrences(of: "#", with: "_")
352+
.replacingOccurrences(of: ".", with: "_"))
353+
}
354+
}
342355
}
343356

344357
// MARK: - External Dereferencing
@@ -370,23 +383,3 @@ extension JSONReferenceTests {
370383
XCTAssertEqual(components, .init(schemas: ["__schema_json__components_schemas_test": .string]))
371384
}
372385
}
373-
374-
// MARK: - Test Types
375-
extension JSONReferenceTests {
376-
struct ReferenceWrapper: Codable, Equatable {
377-
let reference: JSONReference<JSONSchema>
378-
}
379-
380-
struct SchemaLoader: ExternalLoader {
381-
static func load<T>(_ url: URL) -> T where T: Decodable {
382-
return JSONSchema.string as! T
383-
}
384-
385-
static func componentKey<T>(type: T.Type, at url: URL) throws -> OpenAPI.ComponentKey {
386-
return try .forceInit(rawValue: url.absoluteString
387-
.replacingOccurrences(of: "/", with: "_")
388-
.replacingOccurrences(of: "#", with: "_")
389-
.replacingOccurrences(of: ".", with: "_"))
390-
}
391-
}
392-
}

Tests/OpenAPIKitTests/Document/ExternalDereferencingDocumentTests.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -233,17 +233,17 @@ final class ExternalDereferencingDocumentTests: XCTestCase {
233233
encoder.outputFormatting = .prettyPrinted
234234

235235
var docCopy1 = document
236-
try await docCopy1.externallyDereference(in: ExampleLoader.self)
237-
try await docCopy1.externallyDereference(in: ExampleLoader.self)
238-
try await docCopy1.externallyDereference(in: ExampleLoader.self)
236+
try await docCopy1.externallyDereference(with: ExampleLoader.self)
237+
try await docCopy1.externallyDereference(with: ExampleLoader.self)
238+
try await docCopy1.externallyDereference(with: ExampleLoader.self)
239239
docCopy1.components.sort()
240240

241241
var docCopy2 = document
242-
try await docCopy2.externallyDereference(in: ExampleLoader.self, depth: 3)
242+
try await docCopy2.externallyDereference(with: ExampleLoader.self, depth: 3)
243243
docCopy2.components.sort()
244244

245245
var docCopy3 = document
246-
try await docCopy3.externallyDereference(in: ExampleLoader.self, depth: .full)
246+
try await docCopy3.externallyDereference(with: ExampleLoader.self, depth: .full)
247247
docCopy3.components.sort()
248248

249249
XCTAssertEqual(docCopy1, docCopy2)

0 commit comments

Comments
 (0)