Skip to content

Commit 4ebe476

Browse files
authored
Respect the input order when filtering (#337)
Respect the input order when filtering ### Motivation The original implementation of document filtering iterated over a Set and a Dictionary, which do not guarantee a stable order, meaning that every time the filter command was invoked with identical inptus, it could produce different outputs, potentially leading to needless rebuilds when used as a plugin, and needless commits when using ahead-of-time generation. It also made debugging of the generator itself more difficult. ### Modifications Respect the input order of operations and components. ### Result Invoking the generator multiple times with the same input produces the same output every time, improving cachability. ### Test Plan Renamed the filter test to be consistent with the rest and manually tested on a larger document. Reviewed by: glbrntt Builds: ✔︎ pull request validation (5.10) - Build finished. ✔︎ pull request validation (5.8) - Build finished. ✔︎ pull request validation (5.9) - Build finished. ✔︎ pull request validation (compatibility test) - Build finished. ✔︎ pull request validation (docc test) - Build finished. ✔︎ pull request validation (integration test) - Build finished. ✔︎ pull request validation (nightly) - Build finished. ✔︎ pull request validation (soundness) - Build finished. #337
1 parent 978e498 commit 4ebe476

File tree

2 files changed

+69
-25
lines changed

2 files changed

+69
-25
lines changed

Sources/_OpenAPIGeneratorCore/Hooks/FilteredDocument.swift

Lines changed: 68 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -63,43 +63,87 @@ struct FilteredDocumentBuilder {
6363
/// - Returns: The filtered OpenAPI document.
6464
/// - Throws: If any dependencies of the requested document components cannot be resolved.
6565
func filter() throws -> OpenAPI.Document {
66+
let originalDocument = document
67+
let originalComponents = originalDocument.components
6668
var components = OpenAPI.Components.noComponents
67-
for reference in requiredSchemaReferences {
68-
components.schemas[try reference.internalComponentKey] = try document.components.lookup(reference)
69-
}
70-
for reference in requiredPathItemReferences {
71-
components.pathItems[try reference.internalComponentKey] = try document.components.lookup(reference)
69+
for (key, value) in originalComponents.schemas {
70+
let reference = OpenAPI.Reference<JSONSchema>.component(named: key.rawValue)
71+
guard requiredSchemaReferences.contains(reference) else {
72+
continue
73+
}
74+
components.schemas[key] = value
7275
}
73-
for reference in requiredParameterReferences {
74-
components.parameters[try reference.internalComponentKey] = try document.components.lookup(reference)
76+
for (key, value) in originalComponents.pathItems {
77+
let reference = OpenAPI.Reference<OpenAPI.PathItem>.component(named: key.rawValue)
78+
guard requiredPathItemReferences.contains(reference) else {
79+
continue
80+
}
81+
components.pathItems[key] = value
7582
}
76-
for reference in requiredHeaderReferences {
77-
components.headers[try reference.internalComponentKey] = try document.components.lookup(reference)
83+
for (key, value) in originalComponents.parameters {
84+
let reference = OpenAPI.Reference<OpenAPI.Parameter>.component(named: key.rawValue)
85+
guard requiredParameterReferences.contains(reference) else {
86+
continue
87+
}
88+
components.parameters[key] = value
7889
}
79-
for reference in requiredResponseReferences {
80-
components.responses[try reference.internalComponentKey] = try document.components.lookup(reference)
90+
for (key, value) in originalComponents.headers {
91+
let reference = OpenAPI.Reference<OpenAPI.Header>.component(named: key.rawValue)
92+
guard requiredHeaderReferences.contains(reference) else {
93+
continue
94+
}
95+
components.headers[key] = value
8196
}
82-
for reference in requiredCallbacksReferences {
83-
components.callbacks[try reference.internalComponentKey] = try document.components.lookup(reference)
97+
for (key, value) in originalComponents.responses {
98+
let reference = OpenAPI.Reference<OpenAPI.Response>.component(named: key.rawValue)
99+
guard requiredResponseReferences.contains(reference) else {
100+
continue
101+
}
102+
components.responses[key] = value
84103
}
85-
for reference in requiredExampleReferences {
86-
components.examples[try reference.internalComponentKey] = try document.components.lookup(reference)
104+
for (key, value) in originalComponents.callbacks {
105+
let reference = OpenAPI.Reference<OpenAPI.Callbacks>.component(named: key.rawValue)
106+
guard requiredCallbacksReferences.contains(reference) else {
107+
continue
108+
}
109+
components.callbacks[key] = value
87110
}
88-
for reference in requiredLinkReferences {
89-
components.links[try reference.internalComponentKey] = try document.components.lookup(reference)
111+
for (key, value) in originalComponents.examples {
112+
let reference = OpenAPI.Reference<OpenAPI.Example>.component(named: key.rawValue)
113+
guard requiredExampleReferences.contains(reference) else {
114+
continue
115+
}
116+
components.examples[key] = value
90117
}
91-
for reference in requiredRequestReferences {
92-
components.requestBodies[try reference.internalComponentKey] = try document.components.lookup(reference)
118+
for (key, value) in originalComponents.links {
119+
let reference = OpenAPI.Reference<OpenAPI.Link>.component(named: key.rawValue)
120+
guard requiredLinkReferences.contains(reference) else {
121+
continue
122+
}
123+
components.links[key] = value
93124
}
94-
var filteredDocument = document.filteringPaths(with: requiredPaths.contains(_:))
95-
for (path, methods) in requiredEndpoints {
96-
if filteredDocument.paths.contains(key: path) {
125+
for (key, value) in originalComponents.requestBodies {
126+
let reference = OpenAPI.Reference<OpenAPI.Request>.component(named: key.rawValue)
127+
guard requiredRequestReferences.contains(reference) else {
97128
continue
98129
}
99-
guard let maybeReference = document.paths[path] else {
130+
components.requestBodies[key] = value
131+
}
132+
var filteredDocument = document.filteringPaths { path in
133+
if requiredPaths.contains(path) {
134+
return true
135+
}
136+
if let methods = requiredEndpoints[path], !methods.isEmpty {
137+
return true
138+
}
139+
return false
140+
}
141+
let filteredPaths = filteredDocument.paths
142+
for (path, pathItem) in filteredPaths {
143+
guard let methods = requiredEndpoints[path] else {
100144
continue
101145
}
102-
switch maybeReference {
146+
switch pathItem {
103147
case .a(let reference):
104148
components.pathItems[try reference.internalComponentKey] = try document.components.lookup(reference)
105149
.filteringEndpoints { methods.contains($0.method) }

Tests/OpenAPIGeneratorCoreTests/Hooks/FilteredDocumentTests.swift renamed to Tests/OpenAPIGeneratorCoreTests/Hooks/Test_FilteredDocument.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import XCTest
1616
import Yams
1717
@testable import _OpenAPIGeneratorCore
1818

19-
final class FilteredDocumentTests: XCTestCase {
19+
final class Test_FilteredDocument: XCTestCase {
2020

2121
func testDocumentFilter() throws {
2222
let documentYAML = """

0 commit comments

Comments
 (0)