Skip to content

Commit 0508b76

Browse files
Generate enums for server variables
1 parent 9985908 commit 0508b76

File tree

4 files changed

+220
-25
lines changed

4 files changed

+220
-25
lines changed

Sources/_OpenAPIGeneratorCore/Translator/CommonTypes/Constants.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ enum Constants {
4545

4646
/// The prefix of each generated method name.
4747
static let propertyPrefix: String = "server"
48+
49+
/// The name of the server variables namespace.
50+
static let variablesNamespace: String = "Variables"
51+
52+
/// The prefix of the namespace that contains server specific variables.
53+
static let serverVariablesNamespacePrefix: String = "Server"
54+
55+
/// The name of the generated default computed property.
56+
static let defaultPropertyName: String = "default"
4857
}
4958

5059
/// Constants related to the configuration type, which is used by both

Sources/_OpenAPIGeneratorCore/Translator/TypesTranslator/translateServers.swift

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,30 @@ extension TypesFileTranslator {
2929
(originalKey: key, swiftSafeKey: swiftSafeName(for: key), value: value)
3030
}
3131
let parameters: [ParameterDescription] = safeVariables.map { (originalKey, swiftSafeKey, value) in
32-
.init(label: swiftSafeKey, type: .init(TypeName.string), defaultValue: .literal(value.default))
32+
let memberPath: [String] = [
33+
Constants.ServerURL.variablesNamespace,
34+
translateServerVariablesEnumName(for: index),
35+
translateVariableToEnumName((originalKey, value))
36+
]
37+
return .init(
38+
label: swiftSafeName(for: originalKey),
39+
type: .member(memberPath),
40+
defaultValue: .identifierType(.member(memberPath + CollectionOfOne(Constants.ServerURL.defaultPropertyName)))
41+
)
3342
}
3443
let variableInitializers: [Expression] = safeVariables.map { (originalKey, swiftSafeKey, value) in
35-
let allowedValuesArg: FunctionArgumentDescription?
36-
if let allowedValues = value.enum {
37-
allowedValuesArg = .init(
38-
label: "allowedValues",
39-
expression: .literal(.array(allowedValues.map { .literal($0) }))
40-
)
41-
} else {
42-
allowedValuesArg = nil
43-
}
4444
return .dot("init")
4545
.call(
4646
[
4747
.init(label: "name", expression: .literal(originalKey)),
48-
.init(label: "value", expression: .identifierPattern(swiftSafeKey)),
49-
] + (allowedValuesArg.flatMap { [$0] } ?? [])
48+
.init(
49+
label: "value",
50+
expression: .memberAccess(.init(
51+
left: .identifierPattern(swiftSafeKey),
52+
right: "rawValue"
53+
))
54+
),
55+
]
5056
)
5157
}
5258
let methodDecl = Declaration.commentable(
@@ -81,7 +87,12 @@ extension TypesFileTranslator {
8187
/// - Parameter servers: The servers to include in the extension.
8288
/// - Returns: A declaration of an enum namespace of the server URLs type.
8389
func translateServers(_ servers: [OpenAPI.Server]) -> Declaration {
84-
let serverDecls = servers.enumerated().map(translateServer)
90+
var serverDecls = servers.enumerated().map(translateServer)
91+
92+
if let variablesDecl = translateServersVariables(servers) {
93+
serverDecls.insert(variablesDecl, at: 0)
94+
}
95+
8596
return .commentable(
8697
.doc("Server URLs defined in the OpenAPI document."),
8798
.enum(accessModifier: config.access, name: Constants.ServerURL.namespace, members: serverDecls)
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
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+
import OpenAPIKit
15+
16+
extension TypesFileTranslator {
17+
18+
/// Returns the name used for the enum which represents a server variable defined in the OpenAPI
19+
/// document.
20+
/// - Parameter variable: The variable information.
21+
/// - Returns: A name that can be safely used for the enum.
22+
func translateVariableToEnumName(_ variable: (key: String, value: OpenAPI.Server.Variable)) -> String {
23+
return swiftSafeName(for: variable.key.localizedCapitalized)
24+
}
25+
26+
/// Returns the name used for the namespace (enum) which contains a specific server's variables.
27+
/// - Parameter index: The array index of the server.
28+
/// - Returns: A name that can be safely used for the namespace.
29+
func translateServerVariablesEnumName(for index: Int) -> String {
30+
return "\(Constants.ServerURL.serverVariablesNamespacePrefix)\(index + 1)"
31+
}
32+
33+
/// Returns a declaration of a variable enum case for the provided value. If the value can be
34+
/// safely represented as an identifier then the enum case is name only, otherwise the case
35+
/// will have a raw value set to the provided value to satisfy the OpenAPI document
36+
/// requirements.
37+
/// - Parameter value: The variable's enum value.
38+
/// - Returns: A enum case declaration named by the supplied value.
39+
func translateVariableCase(_ value: String) -> Declaration {
40+
let caseName = swiftSafeName(for: value)
41+
if caseName == value {
42+
return .enumCase(name: caseName, kind: .nameOnly)
43+
} else {
44+
return .enumCase(name: caseName, kind: .nameWithRawValue(.string(value)))
45+
}
46+
}
47+
48+
/// Returns a declaration of a variable enum defined in the OpenAPI document. Including
49+
/// a static computed property named default which returns the default defined in the
50+
/// document.
51+
/// - Parameter variable: The variable information.
52+
/// - Returns: An enum declaration.
53+
func translateServerVariable(_ variable: (key: String, value: OpenAPI.Server.Variable)) -> Declaration {
54+
let enumName = translateVariableToEnumName(variable)
55+
var casesDecls: [Declaration]
56+
57+
if let enums = variable.value.enum {
58+
casesDecls = enums.map(translateVariableCase)
59+
} else {
60+
casesDecls = [translateVariableCase(variable.value.default)]
61+
}
62+
casesDecls.append(.commentable(
63+
.doc("The default variable."),
64+
.variable(
65+
accessModifier: config.access,
66+
isStatic: true,
67+
kind: .var,
68+
left: .identifierPattern("`\(Constants.ServerURL.defaultPropertyName)`"),
69+
type: .member(enumName),
70+
getter: [
71+
.expression(
72+
.return(
73+
.memberAccess(.init(
74+
left: .identifierPattern(enumName),
75+
right: swiftSafeName(for: variable.value.default)
76+
))
77+
)
78+
),
79+
]
80+
)
81+
))
82+
83+
return .commentable(
84+
.doc("""
85+
The "\(variable.key)" variable defined in the OpenAPI document.
86+
87+
The default value is "\(variable.value.default)".
88+
"""),
89+
.enum(isFrozen: true, accessModifier: config.access, name: enumName, conformances: [TypeName.string.fullyQualifiedSwiftName], members: casesDecls)
90+
)
91+
}
92+
93+
/// Returns a declaration of a namespace (enum) for a specific server and will define
94+
/// one enum member for each of the server's variables in the OpenAPI Document.
95+
/// If the server does not define variables, no declaration will be generated.
96+
/// - Parameters:
97+
/// - index: The index of the server in the list of servers defined
98+
/// in the OpenAPI document.
99+
/// - server: The server variables information.
100+
/// - Returns: A declaration of the server variables namespace, or `nil` if no
101+
/// variables are declared.
102+
func translateServerVariables(index: Int, server: OpenAPI.Server) -> Declaration? {
103+
if server.variables.isEmpty {
104+
return nil
105+
}
106+
107+
let typeName = translateServerVariablesEnumName(for: index)
108+
let variableDecls = server.variables.map(translateServerVariable)
109+
return .commentable(
110+
.doc("The variables for Server\(index + 1) defined in the OpenAPI document."),
111+
.enum(accessModifier: config.access, name: typeName, members: variableDecls)
112+
)
113+
}
114+
115+
/// Returns a declaration of a namespace (enum) called "Variables" that
116+
/// defines one namespace (enum) per server URL that defines variables
117+
/// in the OpenAPI document. If no server URL defines variables then no
118+
/// declaration is generated.
119+
/// - Parameter servers: The servers to include in the extension.
120+
/// - Returns: A declaration of an enum namespace of the server URLs type.
121+
func translateServersVariables(_ servers: [OpenAPI.Server]) -> Declaration? {
122+
let variableDecls = servers.enumerated().compactMap(translateServerVariables)
123+
if variableDecls.isEmpty {
124+
return nil
125+
}
126+
127+
return .commentable(
128+
.doc("Server URL variables defined in the OpenAPI document."),
129+
.enum(accessModifier: config.access, name: Constants.ServerURL.variablesNamespace, members: variableDecls)
130+
)
131+
}
132+
}

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

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,53 @@ extension APIProtocol {
153153

154154
/// Server URLs defined in the OpenAPI document.
155155
public enum Servers {
156+
/// Server URL variables defined in the OpenAPI document.
157+
public enum Variables {
158+
/// The variables for Server3 defined in the OpenAPI document.
159+
public enum Server3 {
160+
/// The "protocol" variable defined in the OpenAPI document.
161+
///
162+
/// The default value is "https".
163+
@frozen public enum _Protocol: Swift.String {
164+
case https
165+
/// The default variable.
166+
public static var `default`: _Protocol {
167+
return _Protocol.https
168+
}
169+
}
170+
/// The "subdomain" variable defined in the OpenAPI document.
171+
///
172+
/// The default value is "test".
173+
@frozen public enum Subdomain: Swift.String {
174+
case test
175+
/// The default variable.
176+
public static var `default`: Subdomain {
177+
return Subdomain.test
178+
}
179+
}
180+
/// The "port" variable defined in the OpenAPI document.
181+
///
182+
/// The default value is "443".
183+
@frozen public enum Port: Swift.String {
184+
case _443 = "443"
185+
case _8443 = "8443"
186+
/// The default variable.
187+
public static var `default`: Port {
188+
return Port._443
189+
}
190+
}
191+
/// The "basePath" variable defined in the OpenAPI document.
192+
///
193+
/// The default value is "v1".
194+
@frozen public enum Basepath: Swift.String {
195+
case v1
196+
/// The default variable.
197+
public static var `default`: Basepath {
198+
return Basepath.v1
199+
}
200+
}
201+
}
202+
}
156203
/// Example Petstore implementation service
157204
public static func server1() throws -> Foundation.URL {
158205
try Foundation.URL(
@@ -174,33 +221,29 @@ public enum Servers {
174221
/// - port:
175222
/// - basePath: The base API path.
176223
public static func server3(
177-
_protocol: Swift.String = "https",
178-
subdomain: Swift.String = "test",
179-
port: Swift.String = "443",
180-
basePath: Swift.String = "v1"
224+
_protocol: Variables.Server3._Protocol = Variables.Server3._Protocol.default,
225+
subdomain: Variables.Server3.Subdomain = Variables.Server3.Subdomain.default,
226+
port: Variables.Server3.Port = Variables.Server3.Port.default,
227+
basePath: Variables.Server3.Basepath = Variables.Server3.Basepath.default
181228
) throws -> Foundation.URL {
182229
try Foundation.URL(
183230
validatingOpenAPIServerURL: "{protocol}://{subdomain}.example.com:{port}/{basePath}",
184231
variables: [
185232
.init(
186233
name: "protocol",
187-
value: _protocol
234+
value: _protocol.rawValue
188235
),
189236
.init(
190237
name: "subdomain",
191-
value: subdomain
238+
value: subdomain.rawValue
192239
),
193240
.init(
194241
name: "port",
195-
value: port,
196-
allowedValues: [
197-
"443",
198-
"8443"
199-
]
242+
value: port.rawValue
200243
),
201244
.init(
202245
name: "basePath",
203-
value: basePath
246+
value: basePath.rawValue
204247
)
205248
]
206249
)

0 commit comments

Comments
 (0)