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

Lines changed: 2 additions & 0 deletions
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

Lines changed: 7 additions & 2 deletions
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

Lines changed: 8 additions & 0 deletions
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

Lines changed: 15 additions & 4 deletions
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

Lines changed: 6 additions & 2 deletions
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

Lines changed: 2 additions & 1 deletion
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

Lines changed: 7 additions & 3 deletions
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

Lines changed: 16 additions & 2 deletions
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

Lines changed: 13 additions & 3 deletions
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

Lines changed: 6 additions & 3 deletions
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

0 commit comments

Comments
 (0)