Skip to content

Commit dfdbd72

Browse files
authored
[Generator] Generate server variables (#348)
[Generator] Generate server variables ### Motivation The generator side of #24. Depends on apple/swift-openapi-runtime#64 landing and getting released. ### Modifications See the file-based reference tests first to get a sense of how server variables work in templates. Most of this PR is test changes, plus slightly changed formatting of arrays, to put each item on new line for non-empty ones. ### Result Server variables are supported, allowing e.g. services that have enterprise versions to use the feature instead of having to hardcode the remote URL. ### Test Plan Adapted tests. 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. #348
1 parent 4355b3a commit dfdbd72

File tree

11 files changed

+234
-82
lines changed

11 files changed

+234
-82
lines changed

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ let package = Package(
6464
// Tests-only: Runtime library linked by generated code, and also
6565
// helps keep the runtime library new enough to work with the generated
6666
// code.
67-
.package(url: "https://github.com/apple/swift-openapi-runtime", .upToNextMinor(from: "0.3.3")),
67+
.package(url: "https://github.com/apple/swift-openapi-runtime", .upToNextMinor(from: "0.3.5")),
6868

6969
// Build and preview docs
7070
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),

Sources/_OpenAPIGeneratorCore/Renderer/TextBasedRenderer.swift

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -456,13 +456,17 @@ struct TextBasedRenderer: RendererProtocol {
456456
case .nil: write("nil")
457457
case .array(let items):
458458
writer.writeLine("[")
459-
writer.nextLineAppendsToLastLine()
460-
for (item, isLast) in items.enumeratedWithLastMarker() {
461-
renderExpression(item)
462-
if !isLast {
463-
writer.nextLineAppendsToLastLine()
464-
writer.writeLine(", ")
459+
if !items.isEmpty {
460+
writer.withNestedLevel {
461+
for (item, isLast) in items.enumeratedWithLastMarker() {
462+
renderExpression(item)
463+
if !isLast {
464+
writer.nextLineAppendsToLastLine()
465+
writer.writeLine(",")
466+
}
467+
}
465468
}
469+
} else {
466470
writer.nextLineAppendsToLastLine()
467471
}
468472
writer.writeLine("]")

Sources/_OpenAPIGeneratorCore/Translator/CommonTranslations/translateStructBlueprint.swift

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ extension FileTranslator {
6767
/// - Returns: A `Declaration` representing the translated struct.
6868
func translateStructBlueprintInitializer(typeName: TypeName, properties: [PropertyBlueprint]) -> Declaration {
6969

70-
let comment: Comment = .doc(properties.initializerComment(typeName: typeName.shortSwiftName))
70+
let comment: Comment = properties.initializerComment(typeName: typeName.shortSwiftName)
7171

7272
let decls: [(ParameterDescription, String)] = properties.map { property in
7373
(
@@ -145,19 +145,11 @@ fileprivate extension Array where Element == PropertyBlueprint {
145145
/// the properties contained in the current array.
146146
/// - Parameter typeName: The name of the structure type.
147147
/// - Returns: A comment string describing the initializer.
148-
func initializerComment(typeName: String) -> String {
149-
var components: [String] = ["Creates a new `\(typeName)`."]
150-
if !isEmpty {
151-
var parameterComponents: [String] = []
152-
parameterComponents.append("- Parameters:")
153-
for parameter in self {
154-
parameterComponents.append(
155-
" - \(parameter.swiftSafeName):\(parameter.comment?.firstLineOfContent.map { " \($0)" } ?? "")"
156-
)
157-
}
158-
components.append("")
159-
components.append(parameterComponents.joined(separator: "\n"))
160-
}
161-
return components.joined(separator: "\n")
148+
func initializerComment(typeName: String) -> Comment {
149+
Comment.functionComment(
150+
abstract: "Creates a new `\(typeName)`.",
151+
parameters: map { ($0.swiftSafeName, $0.comment?.firstLineOfContent) }
152+
)! // This force-unwrap is safe as the method never returns nil when
153+
// a non-nil abstract is provided.
162154
}
163155
}

Sources/_OpenAPIGeneratorCore/Translator/CommonTypes/CommentExtensions.swift

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,30 @@ extension Comment {
238238
}
239239
)
240240
}
241-
241+
/// Returns a documentation comment for a function with the provided
242+
/// parameters.
243+
/// - Parameters:
244+
/// - abstract: The documentation of the function.
245+
/// - parameters: The parameters.
246+
/// - Returns: A documentation comment for the function.
247+
static func functionComment(abstract: String?, parameters: [(name: String, comment: String?)]) -> Comment? {
248+
guard !parameters.isEmpty else { return abstract.map { .doc($0) } }
249+
var components: [String] = abstract.map { [$0] } ?? []
250+
var parameterComponents: [String] = []
251+
parameterComponents.append("- Parameters:")
252+
for (name, comment) in parameters {
253+
let parameterComment: String
254+
if let comment {
255+
parameterComment = Comment.doc(comment).firstLineOfContent.map { " \($0)" } ?? ""
256+
} else {
257+
parameterComment = ""
258+
}
259+
parameterComponents.append(" - \(name):\(parameterComment)")
260+
}
261+
components.append("")
262+
components.append(parameterComponents.joined(separator: "\n"))
263+
return .doc(components.joined(separator: "\n"))
264+
}
242265
}
243266

244267
extension ComponentDictionaryLocatable {

Sources/_OpenAPIGeneratorCore/Translator/TypesTranslator/translateServers.swift

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,33 @@ extension TypesFileTranslator {
2525
/// declare the method under.
2626
func translateServer(index: Int, server: OpenAPI.Server) -> Declaration {
2727
let methodName = "\(Constants.ServerURL.propertyPrefix)\(index+1)"
28+
let parameters: [ParameterDescription] = server.variables.map { (key, value) in
29+
.init(label: key, type: .init(TypeName.string), defaultValue: .literal(value.default))
30+
}
31+
let variableInitializers: [Expression] = server.variables.map { (key, value) in
32+
let allowedValuesArg: FunctionArgumentDescription?
33+
if let allowedValues = value.enum {
34+
allowedValuesArg = .init(
35+
label: "allowedValues",
36+
expression: .literal(.array(allowedValues.map { .literal($0) }))
37+
)
38+
} else {
39+
allowedValuesArg = nil
40+
}
41+
return .dot("init")
42+
.call(
43+
[
44+
.init(label: "name", expression: .literal(key)),
45+
.init(label: "value", expression: .identifierPattern(key)),
46+
] + (allowedValuesArg.flatMap { [$0] } ?? [])
47+
)
48+
}
2849
let methodDecl = Declaration.commentable(
29-
server.description.flatMap { .doc($0) },
50+
.functionComment(abstract: server.description, parameters: server.variables.map { ($0, $1.description) }),
3051
.function(
3152
accessModifier: config.access,
3253
kind: .function(name: methodName, isStatic: true),
33-
parameters: [],
54+
parameters: parameters,
3455
keywords: [.throws],
3556
returnType: .identifierType(TypeName.url),
3657
body: [
@@ -41,7 +62,7 @@ extension TypesFileTranslator {
4162
.init(
4263
label: "validatingOpenAPIServerURL",
4364
expression: .literal(.string(server.urlTemplate.absoluteString))
44-
)
65+
), .init(label: "variables", expression: .literal(.array(variableInitializers))),
4566
])
4667
)
4768
)

Tests/OpenAPIGeneratorCoreTests/Renderer/Test_TextBasedRenderer.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,14 +180,19 @@ final class Test_TextBasedRenderer: XCTestCase {
180180
.array([.literal(.nil)]),
181181
renderedBy: TextBasedRenderer.renderLiteral,
182182
rendersAs: #"""
183-
[nil]
183+
[
184+
nil
185+
]
184186
"""#
185187
)
186188
try _test(
187189
.array([.literal(.nil), .literal(.nil)]),
188190
renderedBy: TextBasedRenderer.renderLiteral,
189191
rendersAs: #"""
190-
[nil, nil]
192+
[
193+
nil,
194+
nil
195+
]
191196
"""#
192197
)
193198
}

Tests/OpenAPIGeneratorReferenceTests/Resources/Docs/petstore.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,20 @@ servers:
1010
- url: https://example.com/api
1111
description: Example Petstore implementation service
1212
- url: /api
13+
- url: https://{subdomain}.example.com:{port}/{basePath}
14+
description: A custom domain.
15+
variables:
16+
subdomain:
17+
default: test
18+
description: A subdomain name.
19+
port:
20+
enum:
21+
- '443'
22+
- 8443
23+
default: '443'
24+
basePath:
25+
default: v1
26+
description: The base API path.
1327
paths:
1428
/pets:
1529
summary: Work with pets

Tests/OpenAPIGeneratorReferenceTests/Resources/ReferenceSources/Petstore/Client.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,9 @@ public struct Client: APIProtocol {
473473
serializer: { input in
474474
let path = try converter.renderedPath(
475475
template: "/pets/{}",
476-
parameters: [input.path.petId]
476+
parameters: [
477+
input.path.petId
478+
]
477479
)
478480
var request: HTTPTypes.HTTPRequest = .init(
479481
soar_path: path,
@@ -539,7 +541,9 @@ public struct Client: APIProtocol {
539541
serializer: { input in
540542
let path = try converter.renderedPath(
541543
template: "/pets/{}/avatar",
542-
parameters: [input.path.petId]
544+
parameters: [
545+
input.path.petId
546+
]
543547
)
544548
var request: HTTPTypes.HTTPRequest = .init(
545549
soar_path: path,

0 commit comments

Comments
 (0)