Skip to content

Commit

Permalink
[Generator] Generate server variables (#348)
Browse files Browse the repository at this point in the history
[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
  • Loading branch information
czechboy0 committed Oct 26, 2023
1 parent 4355b3a commit dfdbd72
Show file tree
Hide file tree
Showing 11 changed files with 234 additions and 82 deletions.
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ let package = Package(
// Tests-only: Runtime library linked by generated code, and also
// helps keep the runtime library new enough to work with the generated
// code.
.package(url: "https://github.com/apple/swift-openapi-runtime", .upToNextMinor(from: "0.3.3")),
.package(url: "https://github.com/apple/swift-openapi-runtime", .upToNextMinor(from: "0.3.5")),

// Build and preview docs
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
Expand Down
16 changes: 10 additions & 6 deletions Sources/_OpenAPIGeneratorCore/Renderer/TextBasedRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -456,13 +456,17 @@ struct TextBasedRenderer: RendererProtocol {
case .nil: write("nil")
case .array(let items):
writer.writeLine("[")
writer.nextLineAppendsToLastLine()
for (item, isLast) in items.enumeratedWithLastMarker() {
renderExpression(item)
if !isLast {
writer.nextLineAppendsToLastLine()
writer.writeLine(", ")
if !items.isEmpty {
writer.withNestedLevel {
for (item, isLast) in items.enumeratedWithLastMarker() {
renderExpression(item)
if !isLast {
writer.nextLineAppendsToLastLine()
writer.writeLine(",")
}
}
}
} else {
writer.nextLineAppendsToLastLine()
}
writer.writeLine("]")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ extension FileTranslator {
/// - Returns: A `Declaration` representing the translated struct.
func translateStructBlueprintInitializer(typeName: TypeName, properties: [PropertyBlueprint]) -> Declaration {

let comment: Comment = .doc(properties.initializerComment(typeName: typeName.shortSwiftName))
let comment: Comment = properties.initializerComment(typeName: typeName.shortSwiftName)

let decls: [(ParameterDescription, String)] = properties.map { property in
(
Expand Down Expand Up @@ -145,19 +145,11 @@ fileprivate extension Array where Element == PropertyBlueprint {
/// the properties contained in the current array.
/// - Parameter typeName: The name of the structure type.
/// - Returns: A comment string describing the initializer.
func initializerComment(typeName: String) -> String {
var components: [String] = ["Creates a new `\(typeName)`."]
if !isEmpty {
var parameterComponents: [String] = []
parameterComponents.append("- Parameters:")
for parameter in self {
parameterComponents.append(
" - \(parameter.swiftSafeName):\(parameter.comment?.firstLineOfContent.map { " \($0)" } ?? "")"
)
}
components.append("")
components.append(parameterComponents.joined(separator: "\n"))
}
return components.joined(separator: "\n")
func initializerComment(typeName: String) -> Comment {
Comment.functionComment(
abstract: "Creates a new `\(typeName)`.",
parameters: map { ($0.swiftSafeName, $0.comment?.firstLineOfContent) }
)! // This force-unwrap is safe as the method never returns nil when
// a non-nil abstract is provided.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,30 @@ extension Comment {
}
)
}

/// Returns a documentation comment for a function with the provided
/// parameters.
/// - Parameters:
/// - abstract: The documentation of the function.
/// - parameters: The parameters.
/// - Returns: A documentation comment for the function.
static func functionComment(abstract: String?, parameters: [(name: String, comment: String?)]) -> Comment? {
guard !parameters.isEmpty else { return abstract.map { .doc($0) } }
var components: [String] = abstract.map { [$0] } ?? []
var parameterComponents: [String] = []
parameterComponents.append("- Parameters:")
for (name, comment) in parameters {
let parameterComment: String
if let comment {
parameterComment = Comment.doc(comment).firstLineOfContent.map { " \($0)" } ?? ""
} else {
parameterComment = ""
}
parameterComponents.append(" - \(name):\(parameterComment)")
}
components.append("")
components.append(parameterComponents.joined(separator: "\n"))
return .doc(components.joined(separator: "\n"))
}
}

extension ComponentDictionaryLocatable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,33 @@ extension TypesFileTranslator {
/// declare the method under.
func translateServer(index: Int, server: OpenAPI.Server) -> Declaration {
let methodName = "\(Constants.ServerURL.propertyPrefix)\(index+1)"
let parameters: [ParameterDescription] = server.variables.map { (key, value) in
.init(label: key, type: .init(TypeName.string), defaultValue: .literal(value.default))
}
let variableInitializers: [Expression] = server.variables.map { (key, value) in
let allowedValuesArg: FunctionArgumentDescription?
if let allowedValues = value.enum {
allowedValuesArg = .init(
label: "allowedValues",
expression: .literal(.array(allowedValues.map { .literal($0) }))
)
} else {
allowedValuesArg = nil
}
return .dot("init")
.call(
[
.init(label: "name", expression: .literal(key)),
.init(label: "value", expression: .identifierPattern(key)),
] + (allowedValuesArg.flatMap { [$0] } ?? [])
)
}
let methodDecl = Declaration.commentable(
server.description.flatMap { .doc($0) },
.functionComment(abstract: server.description, parameters: server.variables.map { ($0, $1.description) }),
.function(
accessModifier: config.access,
kind: .function(name: methodName, isStatic: true),
parameters: [],
parameters: parameters,
keywords: [.throws],
returnType: .identifierType(TypeName.url),
body: [
Expand All @@ -41,7 +62,7 @@ extension TypesFileTranslator {
.init(
label: "validatingOpenAPIServerURL",
expression: .literal(.string(server.urlTemplate.absoluteString))
)
), .init(label: "variables", expression: .literal(.array(variableInitializers))),
])
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,14 +180,19 @@ final class Test_TextBasedRenderer: XCTestCase {
.array([.literal(.nil)]),
renderedBy: TextBasedRenderer.renderLiteral,
rendersAs: #"""
[nil]
[
nil
]
"""#
)
try _test(
.array([.literal(.nil), .literal(.nil)]),
renderedBy: TextBasedRenderer.renderLiteral,
rendersAs: #"""
[nil, nil]
[
nil,
nil
]
"""#
)
}
Expand Down
14 changes: 14 additions & 0 deletions Tests/OpenAPIGeneratorReferenceTests/Resources/Docs/petstore.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,20 @@ servers:
- url: https://example.com/api
description: Example Petstore implementation service
- url: /api
- url: https://{subdomain}.example.com:{port}/{basePath}
description: A custom domain.
variables:
subdomain:
default: test
description: A subdomain name.
port:
enum:
- '443'
- 8443
default: '443'
basePath:
default: v1
description: The base API path.
paths:
/pets:
summary: Work with pets
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,9 @@ public struct Client: APIProtocol {
serializer: { input in
let path = try converter.renderedPath(
template: "/pets/{}",
parameters: [input.path.petId]
parameters: [
input.path.petId
]
)
var request: HTTPTypes.HTTPRequest = .init(
soar_path: path,
Expand Down Expand Up @@ -539,7 +541,9 @@ public struct Client: APIProtocol {
serializer: { input in
let path = try converter.renderedPath(
template: "/pets/{}/avatar",
parameters: [input.path.petId]
parameters: [
input.path.petId
]
)
var request: HTTPTypes.HTTPRequest = .init(
soar_path: path,
Expand Down

0 comments on commit dfdbd72

Please sign in to comment.