Skip to content

Commit 4c8ed5c

Browse files
Add support for filtering OpenAPI document (#319)
### Motivation When generating client code, Swift OpenAPI Generator generates code for the entire OpenAPI document, even if the user only makes use of a subset of its types and operations. Generating code that is unused constitutes overhead for the adopter: - The overhead of generating code for unused types and operations - The overhead of compiling the generated code - The overhead of unused code in the users codebase (AOT generation) This is particularly noticeable when working with a small subset of a large API, which can result in O(100k) lines of unused code and long generation and compile times. For a more detailed motivation and design, see the proposal in #303. ### Modifications - Add document filter to the generator config. - Run filter as a post-transition hook in the generator pipeline after parsing the document. - Provide a CLI command that outputs the filtered document to stdout. ### Result Users can filter a document before code-generation. For large APIs, this can result in >90% speedup (see proposal). ### Test Plan - Unit tests. --------- Signed-off-by: Si Beaumont <[email protected]> Co-authored-by: Honza Dvorsky <[email protected]>
1 parent 3f8542b commit 4c8ed5c

File tree

12 files changed

+935
-6
lines changed

12 files changed

+935
-6
lines changed

Sources/_OpenAPIGeneratorCore/Config.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,27 @@ public struct Config: Sendable {
2626
/// Additional imports to add to each generated file.
2727
public var additionalImports: [String]
2828

29+
/// Filter to apply to the OpenAPI document before generation.
30+
public var filter: DocumentFilter?
31+
2932
/// Additional pre-release features to enable.
3033
public var featureFlags: FeatureFlags
3134

3235
/// Creates a configuration with the specified generator mode and imports.
3336
/// - Parameters:
3437
/// - mode: The mode to use for generation.
3538
/// - additionalImports: Additional imports to add to each generated file.
39+
/// - filter: Filter to apply to the OpenAPI document before generation.
3640
/// - featureFlags: Additional pre-release features to enable.
3741
public init(
3842
mode: GeneratorMode,
3943
additionalImports: [String] = [],
44+
filter: DocumentFilter? = nil,
4045
featureFlags: FeatureFlags = []
4146
) {
4247
self.mode = mode
4348
self.additionalImports = additionalImports
49+
self.filter = filter
4450
self.featureFlags = featureFlags
4551
}
4652
}

Sources/_OpenAPIGeneratorCore/GeneratorPipeline.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,13 +126,19 @@ func makeGeneratorPipeline(
126126
)
127127
},
128128
postTransitionHooks: [
129+
{ document in
130+
guard let documentFilter = config.filter else {
131+
return document
132+
}
133+
return try documentFilter.filter(document)
134+
},
129135
{ doc in
130136
let validationDiagnostics = try validator(doc, config)
131137
for diagnostic in validationDiagnostics {
132138
diagnostics.emit(diagnostic)
133139
}
134140
return doc
135-
}
141+
},
136142
]
137143
),
138144
translateOpenAPIToStructuredSwiftStage: .init(
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftOpenAPIGenerator open source project
4+
//
5+
// Copyright (c) 2023 Apple Inc. and the SwiftOpenAPIGenerator project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftOpenAPIGenerator project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
@preconcurrency import OpenAPIKit
16+
17+
/// Rules used to filter an OpenAPI document.
18+
public struct DocumentFilter: Codable, Sendable {
19+
20+
/// Operations with these operation IDs will be included in the filter.
21+
public var operations: [String]?
22+
23+
/// Operations tagged with these tags will be included in the filter.
24+
public var tags: [String]?
25+
26+
/// These paths will be included in the filter.
27+
public var paths: [OpenAPI.Path]?
28+
29+
/// These (additional) schemas will be included in the filter.
30+
///
31+
/// These schemas are included in addition to the transitive closure of schema dependencies of
32+
/// the paths included in the filter.
33+
public var schemas: [String]?
34+
35+
/// Create a new DocumentFilter.
36+
///
37+
/// - Parameters:
38+
/// - operations: Operations with these IDs will be included in the filter.
39+
/// - tags: Operations tagged with these tags will be included in the filter.
40+
/// - paths: These paths will be included in the filter.
41+
/// - schemas: These (additional) schemas will be included in the filter.
42+
public init(
43+
operations: [String] = [],
44+
tags: [String] = [],
45+
paths: [OpenAPI.Path] = [],
46+
schemas: [String] = []
47+
) {
48+
self.operations = operations
49+
self.tags = tags
50+
self.paths = paths
51+
self.schemas = schemas
52+
}
53+
54+
/// Filter an OpenAPI document.
55+
///
56+
/// - Parameter document: The OpenAPI document to filter.
57+
/// - Returns: The filtered document.
58+
/// - Throws: If any requested document components do not exist in the original document.
59+
/// - Throws: If any dependencies of the requested document components cannot be resolved.
60+
public func filter(_ document: OpenAPI.Document) throws -> OpenAPI.Document {
61+
var builder = FilteredDocumentBuilder(document: document)
62+
for tag in tags ?? [] {
63+
try builder.includeOperations(tagged: tag)
64+
}
65+
for operationID in operations ?? [] {
66+
try builder.includeOperation(operationID: operationID)
67+
}
68+
for path in paths ?? [] {
69+
try builder.includePath(path)
70+
}
71+
for schema in schemas ?? [] {
72+
try builder.includeSchema(schema)
73+
}
74+
return try builder.filter()
75+
}
76+
}

0 commit comments

Comments
 (0)