Skip to content

Enable the generated server-side code to validate the content-type. #140

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

Merged
merged 8 commits into from
Jul 26, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,29 @@ extension ServerFileTranslator {
func translateServerDeserializer(
_ operation: OperationDescription
) throws -> Expression {
var closureBody: [CodeBlock] = []

let typedRequestBody = try typedRequestBody(in: operation)

if let headerValueForValidation = typedRequestBody?
.content
.content
.contentType
.headerValueForValidation
{
let validateContentTypeExpr: Expression = .try(
.identifier("converter").dot("validateContentTypeIfPresent")
.call([
.init(label: "in", expression: .identifier("request").dot("headerFields")),
.init(
label: "substring",
expression: .literal(headerValueForValidation)
),
])
)
closureBody.append(.expression(validateContentTypeExpr))
}

let inputTypeName = operation.inputTypeName

func locationSpecificInputDecl(
Expand Down Expand Up @@ -67,7 +90,7 @@ extension ServerFileTranslator {
.map(locationSpecificInputDecl(locatedIn:fromParameters:))

let bodyDecl = try translateRequestBodyInServer(
typedRequestBody(in: operation),
typedRequestBody,
requestVariableName: "request",
bodyVariableName: "body",
inputTypeName: inputTypeName
Expand All @@ -94,11 +117,13 @@ extension ServerFileTranslator {
])
)

closureBody.append(
contentsOf: inputMemberDecls.map(CodeBlock.declaration) + [.expression(returnExpr)]
)

return .closureInvocation(
argumentNames: ["request", "metadata"],
body: inputMemberDecls.map(CodeBlock.declaration) + [
.expression(returnExpr)
]
body: closureBody
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,12 @@ fileprivate extension UniversalServer where APIHandler: APIProtocol {
with: metadata,
forOperation: Operations.createPet.id,
using: APIHandler.createPet,
deserializer: { request, metadata in let path: Operations.createPet.Input.Path = .init()
deserializer: { request, metadata in
try converter.validateContentTypeIfPresent(
in: request.headerFields,
substring: "application/json"
)
let path: Operations.createPet.Input.Path = .init()
let query: Operations.createPet.Input.Query = .init()
let headers: Operations.createPet.Input.Headers = .init(
X_Extra_Arguments: try converter.getOptionalHeaderFieldAsJSON(
Expand Down Expand Up @@ -309,6 +314,10 @@ fileprivate extension UniversalServer where APIHandler: APIProtocol {
forOperation: Operations.updatePet.id,
using: APIHandler.updatePet,
deserializer: { request, metadata in
try converter.validateContentTypeIfPresent(
in: request.headerFields,
substring: "application/json"
)
let path: Operations.updatePet.Input.Path = .init(
petId: try converter.getPathParameterAsText(
in: metadata.pathParameters,
Expand Down Expand Up @@ -380,6 +389,10 @@ fileprivate extension UniversalServer where APIHandler: APIProtocol {
forOperation: Operations.uploadAvatarForPet.id,
using: APIHandler.uploadAvatarForPet,
deserializer: { request, metadata in
try converter.validateContentTypeIfPresent(
in: request.headerFields,
substring: "application/octet-stream"
)
let path: Operations.uploadAvatarForPet.Input.Path = .init(
petId: try converter.getPathParameterAsText(
in: metadata.pathParameters,
Expand Down
29 changes: 29 additions & 0 deletions Tests/PetstoreConsumerTests/Test_Server.swift
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,35 @@ final class Test_Server: XCTestCase {
)
}

func testCreatePet_withIncorrectContentType() async throws {
client = .init(
createPetBlock: { input in
XCTFail("The handler should not have been called")
fatalError("Unreachable")
}
)

do {
_ = try await server.createPet(
.init(
path: "/api/pets",
method: .post,
headerFields: [
.init(name: "x-extra-arguments", value: #"{"code":1}"#),
.init(name: "content-type", value: "text/plain; charset=utf-8"),
],
encodedBody: #"""
{
"name" : "Fluffz"
}
"""#
),
.init()
)
XCTFail("The method should have thrown an error.")
} catch {}
}

func testUpdatePet_204_withBody() async throws {
client = .init(
updatePetBlock: { input in
Expand Down