Skip to content

Commit e9c50c9

Browse files
authored
Merge pull request #295 from mattpolzin/store-dereferenced-component-name
store component name as x-component-name when locally dereferencing an object.
2 parents 7e3969e + cc58af0 commit e9c50c9

22 files changed

+209
-60
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,8 @@ Unlike what happens when you lookup an individual component using the `lookup()`
228228

229229
Anywhere that a type would have had either a reference or a component, the dereferenced variety will simply have the component. For example, `PathItem` has an array of parameters, each of which is `Either<JSONReference<Parameter>, Parameter>` whereas a `DereferencedPathItem` has an array of `DereferencedParameter`s. The dereferenced variant of each type exposes all the same properties and you can get at the underlying `OpenAPI` type via an `underlying{TypeName}` property. This can make for a much more convenient way to traverse a document because you don't need to check for or look up references anywhere the OpenAPI Specification allows them.
230230

231+
For all dereferenced types except for `JSONSchema`, dereferencing will store a new vendor extension on the dereferenced value to keep track of the Component Object name the value used to be referenced at. This vendor extension is a string value with the `x-component-name` key.
232+
231233
You can take things a step further and resolve the document. Calling `resolved()` on a `DereferencedDocument` will produce a canonical form of an `OpenAPI.Document`. The `ResolvedRoute`s and `ResolvedEndpoint`s that the `ResolvedDocument` exposes collect all relevant information from the whole document into themselves. For example, a `ResolvedEndpoint` knows what servers it can be used on, what path it is located at, and which parameters it supports (even if some of those parameters were defined in an `OpenAPI.Operation` and others were defined in the containing `OpenAPI.PathItem`).
232234

233235
If your end goal is to analyze the OpenAPI Document or generate something entirely new (like code) from it, the `ResolvedDocument` is by far more convenient to traverse and query than the original `OpenAPI.Document`. The downside is, there is not currently support for mutating the `ResolvedDocument` and then turning it back into an `OpenAPI.Document` to encode it.

Sources/OpenAPIKit/Components Object/Components+Locatable.swift

+7-2
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,18 @@ public protocol LocallyDereferenceable {
8585
/// For all external-use, see `dereferenced(in:)` (provided by the `LocallyDereferenceable` protocol).
8686
/// All types that provide a `_dereferenced(in:following:)` implementation have a `dereferenced(in:)`
8787
/// implementation for free.
88-
func _dereferenced(in components: OpenAPI.Components, following references: Set<AnyHashable>) throws -> DereferencedSelf
88+
func _dereferenced(
89+
in components: OpenAPI.Components,
90+
following references: Set<AnyHashable>,
91+
dereferencedFromComponentNamed name: String?
92+
) throws -> DereferencedSelf
8993
}
9094

9195
extension LocallyDereferenceable {
9296
// default implementation of public `dereferenced(in:)` based on internal
9397
// method that tracks reference cycles.
9498
public func dereferenced(in components: OpenAPI.Components) throws -> DereferencedSelf {
95-
try _dereferenced(in: components, following: [])
99+
try _dereferenced(in: components, following: [], dereferencedFromComponentNamed: nil)
96100
}
97101
}
102+

Sources/OpenAPIKit/Components Object/Components.swift

+8
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,14 @@ extension OpenAPI {
6666
}
6767
}
6868

69+
extension OpenAPI.Components {
70+
/// The extension name used to store a Components Object name (the key something is stored under
71+
/// within the Components Object). This is used by OpenAPIKit to store the previous Component name
72+
/// of an OpenAPI Object that has been dereferenced (pulled out of the Components and stored inline
73+
/// in the OpenAPI Document).
74+
public static let componentNameExtension: String = "x-component-name"
75+
}
76+
6977
extension OpenAPI {
7078
/// A key for one of the component dictionaries.
7179
///

Sources/OpenAPIKit/Content/DereferencedContent.swift

+15-4
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,23 @@ public struct DereferencedContent: Equatable {
3232
resolvingIn components: OpenAPI.Components,
3333
following references: Set<AnyHashable>
3434
) throws {
35-
self.schema = try content.schema?._dereferenced(in: components, following: references)
36-
let examples = try content.examples?.mapValues { try components.lookup($0) }
35+
self.schema = try content.schema?._dereferenced(in: components, following: references, dereferencedFromComponentNamed: nil)
36+
let examples = try content.examples?
37+
.mapValues {
38+
try $0._dereferenced(
39+
in: components,
40+
following: references,
41+
dereferencedFromComponentNamed: nil
42+
)
43+
}
3744
self.examples = examples
3845

3946
self.example = examples.flatMap(OpenAPI.Content.firstExample(from:))
4047
?? content.example
4148

4249
self.encoding = try content.encoding.map { encodingMap in
4350
try encodingMap.mapValues { encoding in
44-
try encoding._dereferenced(in: components, following: references)
51+
try encoding._dereferenced(in: components, following: references, dereferencedFromComponentNamed: nil)
4552
}
4653
}
4754

@@ -58,7 +65,11 @@ extension OpenAPI.Content: LocallyDereferenceable {
5865
/// For all external-use, see `dereferenced(in:)` (provided by the `LocallyDereferenceable` protocol).
5966
/// All types that provide a `_dereferenced(in:following:)` implementation have a `dereferenced(in:)`
6067
/// implementation for free.
61-
public func _dereferenced(in components: OpenAPI.Components, following references: Set<AnyHashable>) throws -> DereferencedContent {
68+
public func _dereferenced(
69+
in components: OpenAPI.Components,
70+
following references: Set<AnyHashable>,
71+
dereferencedFromComponentNamed name: String?
72+
) throws -> DereferencedContent {
6273
return try DereferencedContent(self, resolvingIn: components, following: references)
6374
}
6475
}

Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift

+6-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public struct DereferencedContentEncoding: Equatable {
3131
) throws {
3232
self.headers = try contentEncoding.headers.map { headersMap in
3333
try headersMap.mapValues { header in
34-
try header._dereferenced(in: components, following: references)
34+
try header._dereferenced(in: components, following: references, dereferencedFromComponentNamed: nil)
3535
}
3636
}
3737

@@ -46,7 +46,11 @@ extension OpenAPI.Content.Encoding: LocallyDereferenceable {
4646
/// For all external-use, see `dereferenced(in:)` (provided by the `LocallyDereferenceable` protocol).
4747
/// All types that provide a `_dereferenced(in:following:)` implementation have a `dereferenced(in:)`
4848
/// implementation for free.
49-
public func _dereferenced(in components: OpenAPI.Components, following references: Set<AnyHashable>) throws -> DereferencedContentEncoding {
49+
public func _dereferenced(
50+
in components: OpenAPI.Components,
51+
following references: Set<AnyHashable>,
52+
dereferencedFromComponentNamed name: String?
53+
) throws -> DereferencedContentEncoding {
5054
return try DereferencedContentEncoding(self, resolvingIn: components, following: references)
5155
}
5256
}

Sources/OpenAPIKit/Document/DereferencedDocument.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ public struct DereferencedDocument: Equatable {
5050
try DereferencedPathItem(
5151
$0,
5252
resolvingIn: document.components,
53-
following: []
53+
following: [],
54+
dereferencedFromComponentNamed: nil
5455
)
5556
}
5657
self.security = try document.security.map {

Sources/OpenAPIKit/Either/Either.swift

+7-3
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,16 @@ extension Either: Equatable where A: Equatable, B: Equatable {}
5858

5959
// MARK: - LocallyDereferenceable
6060
extension Either: LocallyDereferenceable where A: LocallyDereferenceable, B: LocallyDereferenceable, A.DereferencedSelf == B.DereferencedSelf {
61-
public func _dereferenced(in components: OpenAPI.Components, following references: Set<AnyHashable>) throws -> A.DereferencedSelf {
61+
public func _dereferenced(
62+
in components: OpenAPI.Components,
63+
following references: Set<AnyHashable>,
64+
dereferencedFromComponentNamed name: String?
65+
) throws -> A.DereferencedSelf {
6266
switch self {
6367
case .a(let value):
64-
return try value._dereferenced(in: components, following: references)
68+
return try value._dereferenced(in: components, following: references, dereferencedFromComponentNamed: nil)
6569
case .b(let value):
66-
return try value._dereferenced(in: components, following: references)
70+
return try value._dereferenced(in: components, following: references, dereferencedFromComponentNamed: nil)
6771
}
6872
}
6973
}

Sources/OpenAPIKit/Example.swift

+16-2
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,22 @@ extension OpenAPI.Example {
158158
extension OpenAPI.Example: LocallyDereferenceable {
159159
/// Examples do not contain any references but for convenience
160160
/// they can be "dereferenced" to themselves.
161-
public func _dereferenced(in components: OpenAPI.Components, following references: Set<AnyHashable>) throws -> OpenAPI.Example {
162-
return self
161+
public func _dereferenced(
162+
in components: OpenAPI.Components,
163+
following references: Set<AnyHashable>,
164+
dereferencedFromComponentNamed name: String?
165+
) throws -> OpenAPI.Example{
166+
var vendorExtensions = self.vendorExtensions
167+
if let name = name {
168+
vendorExtensions[OpenAPI.Components.componentNameExtension] = .init(name)
169+
}
170+
171+
return .init(
172+
summary: self.summary,
173+
description: self.description,
174+
value: self.value,
175+
vendorExtensions: vendorExtensions
176+
)
163177
}
164178
}
165179

Sources/OpenAPIKit/Header/DereferencedHeader.swift

+13-3
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ public struct DereferencedHeader: Equatable {
3030
internal init(
3131
_ header: OpenAPI.Header,
3232
resolvingIn components: OpenAPI.Components,
33-
following references: Set<AnyHashable>
33+
following references: Set<AnyHashable>,
34+
dereferencedFromComponentNamed name: String?
3435
) throws {
3536
switch header.schemaOrContent {
3637
case .a(let schemaContext):
@@ -53,6 +54,11 @@ public struct DereferencedHeader: Equatable {
5354
)
5455
}
5556

57+
var header = header
58+
if let name = name {
59+
header.vendorExtensions[OpenAPI.Components.componentNameExtension] = .init(name)
60+
}
61+
5662
self.underlyingHeader = header
5763
}
5864

@@ -66,7 +72,11 @@ extension OpenAPI.Header: LocallyDereferenceable {
6672
/// For all external-use, see `dereferenced(in:)` (provided by the `LocallyDereferenceable` protocol).
6773
/// All types that provide a `_dereferenced(in:following:)` implementation have a `dereferenced(in:)`
6874
/// implementation for free.
69-
public func _dereferenced(in components: OpenAPI.Components, following references: Set<AnyHashable>) throws -> DereferencedHeader {
70-
return try DereferencedHeader(self, resolvingIn: components, following: references)
75+
public func _dereferenced(
76+
in components: OpenAPI.Components,
77+
following references: Set<AnyHashable>,
78+
dereferencedFromComponentNamed name: String?
79+
) throws -> DereferencedHeader {
80+
return try DereferencedHeader(self, resolvingIn: components, following: references, dereferencedFromComponentNamed: name)
7181
}
7282
}

Sources/OpenAPIKit/JSONReference.swift

+6-3
Original file line numberDiff line numberDiff line change
@@ -358,8 +358,11 @@ extension JSONReference: LocallyDereferenceable where ReferenceType: LocallyDere
358358
///
359359
/// If you just want to look the reference up, use the `subscript` or the
360360
/// `lookup()` method on `Components`.
361-
public func _dereferenced(in components: OpenAPI.Components, following references: Set<AnyHashable>) throws -> ReferenceType.DereferencedSelf {
362-
361+
public func _dereferenced(
362+
in components: OpenAPI.Components,
363+
following references: Set<AnyHashable>,
364+
dereferencedFromComponentNamed name: String?
365+
) throws -> ReferenceType.DereferencedSelf {
363366
var newReferences = references
364367
let (inserted, _) = newReferences.insert(self)
365368
guard inserted else {
@@ -368,7 +371,7 @@ extension JSONReference: LocallyDereferenceable where ReferenceType: LocallyDere
368371

369372
return try components
370373
.lookup(self)
371-
._dereferenced(in: components, following: newReferences)
374+
._dereferenced(in: components, following: newReferences, dereferencedFromComponentNamed: self.name)
372375
}
373376
}
374377

Sources/OpenAPIKit/Operation/DereferencedOperation.swift

+8-4
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,15 @@ public struct DereferencedOperation: Equatable {
5050
following references: Set<AnyHashable>
5151
) throws {
5252
self.parameters = try operation.parameters.map { parameter in
53-
try parameter._dereferenced(in: components, following: references)
53+
try parameter._dereferenced(in: components, following: references, dereferencedFromComponentNamed: nil)
5454
}
5555

5656
self.requestBody = try operation.requestBody.map { request in
57-
try request._dereferenced(in: components, following: references)
57+
try request._dereferenced(in: components, following: references, dereferencedFromComponentNamed: nil)
5858
}
5959

6060
self.responses = try operation.responses.mapValues { response in
61-
try response._dereferenced(in: components, following: references)
61+
try response._dereferenced(in: components, following: references, dereferencedFromComponentNamed: nil)
6262
}
6363

6464
self.security = try operation.security?.map {
@@ -105,7 +105,11 @@ extension OpenAPI.Operation: LocallyDereferenceable {
105105
/// For all external-use, see `dereferenced(in:)` (provided by the `LocallyDereferenceable` protocol).
106106
/// All types that provide a `_dereferenced(in:following:)` implementation have a `dereferenced(in:)`
107107
/// implementation for free.
108-
public func _dereferenced(in components: OpenAPI.Components, following references: Set<AnyHashable>) throws -> DereferencedOperation {
108+
public func _dereferenced(
109+
in components: OpenAPI.Components,
110+
following references: Set<AnyHashable>,
111+
dereferencedFromComponentNamed name: String?
112+
) throws -> DereferencedOperation {
109113
return try DereferencedOperation(self, resolvingIn: components, following: references)
110114
}
111115
}

Sources/OpenAPIKit/Parameter/DereferencedParameter.swift

+13-3
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ public struct DereferencedParameter: Equatable {
3232
internal init(
3333
_ parameter: OpenAPI.Parameter,
3434
resolvingIn components: OpenAPI.Components,
35-
following references: Set<AnyHashable>
35+
following references: Set<AnyHashable>,
36+
dereferencedFromComponentNamed name: String?
3637
) throws {
3738
switch parameter.schemaOrContent {
3839
case .a(let schemaContext):
@@ -55,6 +56,11 @@ public struct DereferencedParameter: Equatable {
5556
)
5657
}
5758

59+
var parameter = parameter
60+
if let name = name {
61+
parameter.vendorExtensions[OpenAPI.Components.componentNameExtension] = .init(name)
62+
}
63+
5864
self.underlyingParameter = parameter
5965
}
6066
}
@@ -66,7 +72,11 @@ extension OpenAPI.Parameter: LocallyDereferenceable {
6672
/// For all external-use, see `dereferenced(in:)` (provided by the `LocallyDereferenceable` protocol).
6773
/// All types that provide a `_dereferenced(in:following:)` implementation have a `dereferenced(in:)`
6874
/// implementation for free.
69-
public func _dereferenced(in components: OpenAPI.Components, following references: Set<AnyHashable>) throws -> DereferencedParameter {
70-
return try DereferencedParameter(self, resolvingIn: components, following: references)
75+
public func _dereferenced(
76+
in components: OpenAPI.Components,
77+
following references: Set<AnyHashable>,
78+
dereferencedFromComponentNamed name: String?
79+
) throws -> DereferencedParameter {
80+
return try DereferencedParameter(self, resolvingIn: components, following: references, dereferencedFromComponentNamed: name)
7181
}
7282
}

Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift

+6-2
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public struct DereferencedSchemaContext: Equatable {
4040
resolvingIn components: OpenAPI.Components,
4141
following references: Set<AnyHashable>
4242
) throws {
43-
self.schema = try schemaContext.schema._dereferenced(in: components, following: references)
43+
self.schema = try schemaContext.schema._dereferenced(in: components, following: references, dereferencedFromComponentNamed: nil)
4444
let examples = try schemaContext.examples?.mapValues { try components.lookup($0) }
4545
self.examples = examples
4646

@@ -58,7 +58,11 @@ extension OpenAPI.Parameter.SchemaContext: LocallyDereferenceable {
5858
/// For all external-use, see `dereferenced(in:)` (provided by the `LocallyDereferenceable` protocol).
5959
/// All types that provide a `_dereferenced(in:following:)` implementation have a `dereferenced(in:)`
6060
/// implementation for free.
61-
public func _dereferenced(in components: OpenAPI.Components, following references: Set<AnyHashable>) throws -> DereferencedSchemaContext {
61+
public func _dereferenced(
62+
in components: OpenAPI.Components,
63+
following references: Set<AnyHashable>,
64+
dereferencedFromComponentNamed name: String?
65+
) throws -> DereferencedSchemaContext {
6266
return try DereferencedSchemaContext(self, resolvingIn: components, following: references)
6367
}
6468
}

Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift

+14-4
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,11 @@ public struct DereferencedPathItem: Equatable {
4646
internal init(
4747
_ pathItem: OpenAPI.PathItem,
4848
resolvingIn components: OpenAPI.Components,
49-
following references: Set<AnyHashable>
49+
following references: Set<AnyHashable>,
50+
dereferencedFromComponentNamed name: String?
5051
) throws {
5152
self.parameters = try pathItem.parameters.map { parameter in
52-
try parameter._dereferenced(in: components, following: references)
53+
try parameter._dereferenced(in: components, following: references, dereferencedFromComponentNamed: nil)
5354
}
5455

5556
self.get = try pathItem.get.map { try DereferencedOperation($0, resolvingIn: components, following: references) }
@@ -61,6 +62,11 @@ public struct DereferencedPathItem: Equatable {
6162
self.patch = try pathItem.patch.map { try DereferencedOperation($0, resolvingIn: components, following: references) }
6263
self.trace = try pathItem.trace.map { try DereferencedOperation($0, resolvingIn: components, following: references) }
6364

65+
var pathItem = pathItem
66+
if let name = name {
67+
pathItem.vendorExtensions[OpenAPI.Components.componentNameExtension] = .init(name)
68+
}
69+
6470
self.underlyingPathItem = pathItem
6571
}
6672

@@ -121,7 +127,11 @@ extension OpenAPI.PathItem: LocallyDereferenceable {
121127
/// For all external-use, see `dereferenced(in:)` (provided by the `LocallyDereferenceable` protocol).
122128
/// All types that provide a `_dereferenced(in:following:)` implementation have a `dereferenced(in:)`
123129
/// implementation for free.
124-
public func _dereferenced(in components: OpenAPI.Components, following references: Set<AnyHashable>) throws -> DereferencedPathItem {
125-
return try DereferencedPathItem(self, resolvingIn: components, following: references)
130+
public func _dereferenced(
131+
in components: OpenAPI.Components,
132+
following references: Set<AnyHashable>,
133+
dereferencedFromComponentNamed name: String?
134+
) throws -> DereferencedPathItem {
135+
return try DereferencedPathItem(self, resolvingIn: components, following: references, dereferencedFromComponentNamed: name)
126136
}
127137
}

0 commit comments

Comments
 (0)