Skip to content

Calculate required response header parameters for initialize #150

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Original file line number Diff line number Diff line change
Expand Up @@ -102,20 +102,23 @@ extension TypesFileTranslator {
asSwiftSafeName: swiftSafeName
)

let responseStructDecl = translateStructBlueprint(
.init(
comment: nil,
access: config.access,
typeName: typeName,
conformances: Constants.Operation.Output.Payload.conformances,
properties: [
headersProperty,
contentProperty,
]
)
let structBlueprint: StructBlueprint = .init(
comment: nil,
access: config.access,
typeName: typeName,
conformances: Constants.Operation.Output.Payload.conformances,
properties: [
headersProperty,
contentProperty,
]
)

return responseStructDecl
let responseStructDecl = _calculateRequiredHeadersForInitialize(
with: headers,
from: translateStructBlueprint(structBlueprint)
)

return responseStructDecl.deprecate(if: structBlueprint.isDeprecated)
}

/// Returns a list of declarations for the specified reusable response
Expand All @@ -138,4 +141,82 @@ extension TypesFileTranslator {
response: response
)
}

/// Calculate the necessary parameters for initializing the response headers
/// and return the list of specified reusable response declarations.
/// - Parameters:
/// - headers: the typed response headers
/// - responseStructDecl: A structure declaration before calculate.
/// - Returns: A structure declaration.
private func _calculateRequiredHeadersForInitialize(
with headers: [TypedResponseHeader],
from responseStructDecl: Declaration
) -> Declaration {
guard case .commentable(let comment, let structDec) = responseStructDecl,
case .struct(let structDescription) = structDec
else {
return responseStructDecl
}

let newMembers: [Declaration] = structDescription
.members
.reduce(into: [Declaration]()) { partialResult, member in
let labelHeaders = "headers"

if case .commentable(let memberComment, let memberDecl) = member,
case .function(let memberFuncDescription) = memberDecl,
memberFuncDescription.signature.kind == .initializer,
memberFuncDescription.signature.parameters.first(where: { $0.label == labelHeaders }) != nil
{

let initParameters: [ParameterDescription] = memberFuncDescription
.signature
.parameters
.map { parameterDesc in
guard parameterDesc.label == labelHeaders else {
return parameterDesc
}
let defaultValue: Expression? = {
if headers.isEmpty {
return PropertyBlueprint.DefaultValue.emptyInit.asExpression
}
if headers.first(where: { !$0.isOptional }) != nil {
return nil
}
return PropertyBlueprint.DefaultValue.emptyInit.asExpression
}()
return .init(
label: parameterDesc.label,
name: parameterDesc.name,
type: parameterDesc.type,
defaultValue: defaultValue
)
}

let initDescription: FunctionDescription = .init(
accessModifier: memberFuncDescription.signature.accessModifier,
kind: memberFuncDescription.signature.kind,
parameters: initParameters,
keywords: memberFuncDescription.signature.keywords,
returnType: memberFuncDescription.signature.returnType,
body: memberFuncDescription.body
)

partialResult.append(
.commentable(memberComment, .function(initDescription))
)
} else {
partialResult.append(member)
}
}

let structDescriptionWithCalc = StructDescription(
accessModifier: structDescription.accessModifier,
name: structDescription.name,
conformances: structDescription.conformances,
members: newMembers
)

return .commentable(comment, .struct(structDescriptionWithCalc))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/CodeError'
X-Extra-Arguments2:
required: true
description: "A description here."
content:
application/json:
schema:
$ref: '#/components/schemas/CodeError'
content:
application/json:
schema:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,11 @@ public struct Client: APIProtocol {
in: response.headerFields,
name: "X-Extra-Arguments",
as: Components.Schemas.CodeError.self
),
X_Extra_Arguments2: try converter.getRequiredHeaderFieldAsJSON(
in: response.headerFields,
name: "X-Extra-Arguments2",
as: Components.Schemas.CodeError.self
)
)
try converter.validateContentTypeIfPresent(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,11 @@ fileprivate extension UniversalServer where APIHandler: APIProtocol {
name: "X-Extra-Arguments",
value: value.headers.X_Extra_Arguments
)
try converter.setHeaderFieldAsJSON(
in: &response.headerFields,
name: "X-Extra-Arguments2",
value: value.headers.X_Extra_Arguments2
)
try converter.validateAcceptIfPresent(
"application/json",
in: request.headerFields
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,7 @@ public enum Components {
/// - headers: Received HTTP response headers
/// - body: Received HTTP response body
public init(
headers: Components.Responses.ErrorBadRequest.Headers,
headers: Components.Responses.ErrorBadRequest.Headers = .init(),
body: Components.Responses.ErrorBadRequest.Body
) {
self.headers = headers
Expand Down Expand Up @@ -985,12 +985,18 @@ public enum Operations {
public struct Created: Sendable, Equatable, Hashable {
public struct Headers: Sendable, Equatable, Hashable {
public var X_Extra_Arguments: Components.Schemas.CodeError?
public var X_Extra_Arguments2: Components.Schemas.CodeError
/// Creates a new `Headers`.
///
/// - Parameters:
/// - X_Extra_Arguments:
public init(X_Extra_Arguments: Components.Schemas.CodeError? = nil) {
/// - X_Extra_Arguments2:
public init(
X_Extra_Arguments: Components.Schemas.CodeError? = nil,
X_Extra_Arguments2: Components.Schemas.CodeError
) {
self.X_Extra_Arguments = X_Extra_Arguments
self.X_Extra_Arguments2 = X_Extra_Arguments2
}
}
/// Received HTTP response headers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,7 @@ final class SnippetBasedReferenceTests: XCTestCase {
)
}

func testComponentsResponsesResponseWithHeader() throws {
func testComponentsResponsesResponseWithOptionalHeader() throws {
try self.assertResponsesTranslation(
"""
responses:
Expand All @@ -541,6 +541,42 @@ final class SnippetBasedReferenceTests: XCTestCase {
public var headers: Components.Responses.BadRequest.Headers
@frozen public enum Body: Sendable, Equatable, Hashable {}
public var body: Components.Responses.BadRequest.Body?
public init(
headers: Components.Responses.BadRequest.Headers = .init(),
body: Components.Responses.BadRequest.Body? = nil
) {
self.headers = headers
self.body = body
}
}
}
"""
)
}

func testComponentsResponsesResponseWithRequiredHeader() throws {
try self.assertResponsesTranslation(
"""
responses:
BadRequest:
description: Bad request
headers:
X-Reason:
schema:
type: string
required: true
""",
"""
public enum Responses {
public struct BadRequest: Sendable, Equatable, Hashable {
public struct Headers: Sendable, Equatable, Hashable {
public var X_Reason: Swift.String
public init(X_Reason: Swift.String) {
self.X_Reason = X_Reason }
}
public var headers: Components.Responses.BadRequest.Headers
@frozen public enum Body: Sendable, Equatable, Hashable {}
public var body: Components.Responses.BadRequest.Body?
public init(
headers: Components.Responses.BadRequest.Headers,
body: Components.Responses.BadRequest.Body? = nil
Expand Down
2 changes: 2 additions & 0 deletions Tests/PetstoreConsumerTests/Test_Client.swift
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ final class Test_Client: XCTestCase {
headers: [
.init(name: "content-type", value: "application/json; charset=utf-8"),
.init(name: "x-extra-arguments", value: #"{"code":1}"#),
.init(name: "x-extra-arguments2", value: #"{"code":9999}"#),
],
encodedBody: #"""
{
Expand All @@ -192,6 +193,7 @@ final class Test_Client: XCTestCase {
return
}
XCTAssertEqual(value.headers.X_Extra_Arguments, .init(code: 1))
XCTAssertEqual(value.headers.X_Extra_Arguments2, .init(code: 9999))
switch value.body {
case .json(let pets):
XCTAssertEqual(pets, .init(id: 1, name: "Fluffz"))
Expand Down
4 changes: 2 additions & 2 deletions Tests/PetstoreConsumerTests/Test_Server.swift
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ final class Test_Server: XCTestCase {
return .created(
.init(
headers: .init(
X_Extra_Arguments: .init(code: 1)
X_Extra_Arguments2: .init(code: 1)
),
body: .json(
.init(id: 1, name: "Fluffz")
Expand Down Expand Up @@ -166,7 +166,7 @@ final class Test_Server: XCTestCase {
XCTAssertEqual(
response.headerFields,
[
.init(name: "X-Extra-Arguments", value: #"{"code":1}"#),
.init(name: "X-Extra-Arguments2", value: #"{"code":1}"#),
.init(name: "content-type", value: "application/json; charset=utf-8"),
]
)
Expand Down