Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ import aws.sdk.kotlin.codegen.AwsKotlinDependency
import aws.sdk.kotlin.codegen.protocols.middleware.ModeledExceptionsMiddleware
import software.amazon.smithy.kotlin.codegen.core.KotlinWriter
import software.amazon.smithy.kotlin.codegen.core.addImport
import software.amazon.smithy.kotlin.codegen.model.expectTrait
import software.amazon.smithy.kotlin.codegen.model.getTrait
import software.amazon.smithy.kotlin.codegen.rendering.protocol.HttpBindingResolver
import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator
import software.amazon.smithy.model.traits.ErrorTrait
import software.amazon.smithy.model.traits.HttpErrorTrait

class RestJsonErrorMiddleware(
Expand All @@ -31,10 +34,11 @@ class RestJsonErrorMiddleware(
val code = errShape.id.name
val symbol = ctx.symbolProvider.toSymbol(errShape)
val deserializerName = "${symbol.name}Deserializer"
val httpStatusCode: Int? = errShape.getTrait(HttpErrorTrait::class.java).map { it.code }.orElse(null)
if (httpStatusCode != null) {
writer.write("register(code = #S, deserializer = $deserializerName(), httpStatusCode = $httpStatusCode)", code)
}
// If model specifies error code use it otherwise default to 400 (client) or 500 (server)
// See https://awslabs.github.io/smithy/1.0/spec/core/http-traits.html#httperror-trait
val defaultCode = if (errShape.expectTrait<ErrorTrait>().isClientError) 400 else 500
val httpStatusCode = errShape.getTrait<HttpErrorTrait>()?.code ?: defaultCode
writer.write("register(code = #S, deserializer = $deserializerName(), httpStatusCode = $httpStatusCode)", code)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import aws.sdk.kotlin.codegen.AwsKotlinDependency
import aws.sdk.kotlin.codegen.protocols.middleware.ModeledExceptionsMiddleware
import software.amazon.smithy.kotlin.codegen.core.KotlinWriter
import software.amazon.smithy.kotlin.codegen.core.addImport
import software.amazon.smithy.kotlin.codegen.model.expectTrait
import software.amazon.smithy.kotlin.codegen.model.getTrait
import software.amazon.smithy.kotlin.codegen.rendering.protocol.HttpBindingResolver
import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator
import software.amazon.smithy.model.traits.ErrorTrait
import software.amazon.smithy.model.traits.HttpErrorTrait

class RestXmlErrorMiddleware(
Expand All @@ -32,9 +34,11 @@ class RestXmlErrorMiddleware(
val code = errShape.id.name
val symbol = ctx.symbolProvider.toSymbol(errShape)
val deserializerName = "${symbol.name}Deserializer"
errShape.getTrait<HttpErrorTrait>()?.code?.let { httpStatusCode ->
writer.write("register(code = #S, deserializer = $deserializerName(), httpStatusCode = $httpStatusCode)", code)
}
// If model specifies error code use it otherwise default to 400 (client) or 500 (server)
// See https://awslabs.github.io/smithy/1.0/spec/core/http-traits.html#httperror-trait
val defaultCode = if (errShape.expectTrait<ErrorTrait>().isClientError) 400 else 500
val httpStatusCode = errShape.getTrait<HttpErrorTrait>()?.code ?: defaultCode
writer.write("register(code = #S, deserializer = $deserializerName(), httpStatusCode = $httpStatusCode)", code)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package aws.sdk.kotlin.codegen.protocols.json

import org.junit.jupiter.api.Test
import software.amazon.smithy.kotlin.codegen.rendering.protocol.HttpTraitResolver
import software.amazon.smithy.kotlin.codegen.test.*

class AwsJsonErrorMiddlewareTest {

@Test
fun `it registers error deserializers for client error shapes`() {
val model = """
@http(method: "PUT", uri: "/test", code: 200)
operation TestOperation {
output: TestResponse,
errors: [TestError]
}

structure TestResponse {
someVal: Integer
}

@error("client")
structure TestError {
someCode: Integer,
someMessage: String
}
""".prependNamespaceAndService(operations = listOf("TestOperation"))
.toSmithyModel()

val (ctx, _, _) = model.newTestContext()

val unit = RestJsonErrorMiddleware(ctx, AwsJsonHttpBindingResolver(ctx, "application/x-amz-json-1.0"))

val actual = generateCode { writer ->
unit.renderRegisterErrors(writer)
}

actual.shouldContainOnlyOnceWithDiff("register(code = \"TestError\", deserializer = TestErrorDeserializer(), httpStatusCode = 400)")
}

@Test
fun `it registers error deserializers for server error shapes`() {
val model = """
@http(method: "PUT", uri: "/test", code: 200)
operation TestOperation {
output: TestResponse,
errors: [TestError]
}

structure TestResponse {
someVal: Integer
}

@error("server")
structure TestError {
someCode: Integer,
someMessage: String
}
""".prependNamespaceAndService(operations = listOf("TestOperation"))
.toSmithyModel()

val (ctx, _, _) = model.newTestContext()

val unit = RestJsonErrorMiddleware(ctx, AwsJsonHttpBindingResolver(ctx, "application/x-amz-json-1.0"))

val actual = generateCode { writer ->
unit.renderRegisterErrors(writer)
}

actual.shouldContainOnlyOnceWithDiff("register(code = \"TestError\", deserializer = TestErrorDeserializer(), httpStatusCode = 500)")
}

@Test
fun `it registers error deserializer with custom code for error shapes`() {
val model = """
@http(method: "PUT", uri: "/test", code: 200)
operation TestOperation {
output: TestResponse,
errors: [TestError]
}

structure TestResponse {
someVal: Integer
}

@error("client")
@httpError(404)
structure TestError {
someCode: Integer,
someMessage: String
}
""".prependNamespaceAndService(operations = listOf("TestOperation"))
.toSmithyModel()

val (ctx, _, _) = model.newTestContext()

val unit = RestJsonErrorMiddleware(ctx, HttpTraitResolver(ctx, "application/xml"))

val actual = generateCode { writer ->
unit.renderRegisterErrors(writer)
}

actual.shouldContainOnlyOnceWithDiff("register(code = \"TestError\", deserializer = TestErrorDeserializer(), httpStatusCode = 404)")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package aws.sdk.kotlin.codegen.protocols.xml

import org.junit.jupiter.api.Test
import software.amazon.smithy.kotlin.codegen.rendering.protocol.HttpTraitResolver
import software.amazon.smithy.kotlin.codegen.test.*

class RestXmlErrorMiddlewareTest {

@Test
fun `it registers error deserializers for client error shapes`() {
val model = """
@http(method: "PUT", uri: "/test", code: 200)
operation TestOperation {
output: TestResponse,
errors: [TestError]
}

structure TestResponse {
someVal: Integer
}

@error("client")
structure TestError {
someCode: Integer,
someMessage: String
}
""".prependNamespaceAndService(operations = listOf("TestOperation"))
.toSmithyModel()

val (ctx, _, _) = model.newTestContext()

val unit = RestXmlErrorMiddleware(ctx, HttpTraitResolver(ctx, "application/xml"))

val actual = generateCode { writer ->
unit.renderRegisterErrors(writer)
}

actual.shouldContainOnlyOnceWithDiff("register(code = \"TestError\", deserializer = TestErrorDeserializer(), httpStatusCode = 400)")
}

@Test
fun `it registers error deserializers for server error shapes`() {
val model = """
@http(method: "PUT", uri: "/test", code: 200)
operation TestOperation {
output: TestResponse,
errors: [TestError]
}

structure TestResponse {
someVal: Integer
}

@error("server")
structure TestError {
someCode: Integer,
someMessage: String
}
""".prependNamespaceAndService(operations = listOf("TestOperation"))
.toSmithyModel()

val (ctx, _, _) = model.newTestContext()

val unit = RestXmlErrorMiddleware(ctx, HttpTraitResolver(ctx, "application/xml"))

val actual = generateCode { writer ->
unit.renderRegisterErrors(writer)
}

actual.shouldContainOnlyOnceWithDiff("register(code = \"TestError\", deserializer = TestErrorDeserializer(), httpStatusCode = 500)")
}

@Test
fun `it registers error deserializer with custom code for error shapes`() {
val model = """
@http(method: "PUT", uri: "/test", code: 200)
operation TestOperation {
output: TestResponse,
errors: [TestError]
}

structure TestResponse {
someVal: Integer
}

@error("client")
@httpError(404)
structure TestError {
someCode: Integer,
someMessage: String
}
""".prependNamespaceAndService(operations = listOf("TestOperation"))
.toSmithyModel()

val (ctx, _, _) = model.newTestContext()

val unit = RestXmlErrorMiddleware(ctx, HttpTraitResolver(ctx, "application/xml"))

val actual = generateCode { writer ->
unit.renderRegisterErrors(writer)
}

actual.shouldContainOnlyOnceWithDiff("register(code = \"TestError\", deserializer = TestErrorDeserializer(), httpStatusCode = 404)")
}
}