Skip to content
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

Update codegen to generate base exception rather than UnknownServiceErrorException #484

Merged
merged 5 commits into from
Jan 5, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -92,7 +92,7 @@ class S3Generator : RestXml() {
}
.dedent()
.withBlock("} catch (ex: Exception) {", "}") {
withBlock("""throw #T("failed to parse response as ${ctx.protocol.name} error", ex).also {""", "}", AwsRuntimeTypes.Core.UnknownServiceErrorException) {
withBlock("""throw #T("Failed to parse response as '${ctx.protocol.name}' error", ex).also {""", "}", exceptionBaseSymbol) {
write("#T(it, wrappedResponse, null)", setS3ErrorMetadata)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ abstract class AwsHttpBindingProtocolGenerator : HttpBindingProtocolGenerator()
}
.dedent()
.withBlock("} catch (ex: Exception) {", "}") {
withBlock("""throw #T("failed to parse response as ${ctx.protocol.name} error", ex).also {""", "}", AwsRuntimeTypes.Core.UnknownServiceErrorException) {
withBlock("""throw #T("Failed to parse response as '${ctx.protocol.name}' error", ex).also {""", "}", exceptionBaseSymbol) {
write("#T(it, wrappedResponse, null)", AwsRuntimeTypes.Http.setAseErrorMetadata)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
package aws.sdk.kotlin.codegen.protocols.core

import software.amazon.smithy.kotlin.codegen.core.KotlinWriter
import software.amazon.smithy.kotlin.codegen.model.expectShape
import software.amazon.smithy.kotlin.codegen.rendering.protocol.HttpBindingResolver
import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator
import software.amazon.smithy.kotlin.codegen.test.defaultSettings
import software.amazon.smithy.kotlin.codegen.test.newTestContext
import software.amazon.smithy.kotlin.codegen.test.shouldContainOnlyOnceWithDiff
import software.amazon.smithy.kotlin.codegen.test.toSmithyModel
import software.amazon.smithy.model.Model
import software.amazon.smithy.model.shapes.OperationShape
import software.amazon.smithy.model.shapes.ServiceShape
import software.amazon.smithy.model.shapes.Shape
import software.amazon.smithy.model.shapes.ShapeId
import software.amazon.smithy.model.traits.TimestampFormatTrait
import kotlin.test.Test

class AwsHttpBindingProtocolGeneratorTest {

@Test
fun `it throws base service exception on error parse failure`() {
val model = """
namespace com.test
use aws.protocols#restJson1

@restJson1
service Example {
version: "1.0.0",
operations: [GetFoo]
}

operation GetFoo {
errors: [FooError]
}

@error("server")
structure FooError {
payload: String
}
""".toSmithyModel()

// This is the value that produces the name of the service base exception type
val serviceSdkName = "SdkName"

val testCtx = model.newTestContext(
serviceName = "Example",
settings = model.defaultSettings(sdkId = serviceSdkName)
)
val writer = KotlinWriter("com.test")
val unit = TestableAwsHttpBindingProtocolGenerator()
val op = model.expectShape<OperationShape>("com.test#GetFoo")

unit.renderThrowOperationError(testCtx.generationCtx, op, writer)

val actual = writer.toString()
val expected = """
throw ${serviceSdkName}Exception("Failed to parse response as 'restJson1' error", ex).also {
""".trimIndent()

actual.shouldContainOnlyOnceWithDiff(expected)
}

// A concrete implementation of AwsHttpBindingProtocolGenerator to exercise renderThrowOperationError()
class TestableAwsHttpBindingProtocolGenerator : AwsHttpBindingProtocolGenerator() {
override fun renderDeserializeErrorDetails(
ctx: ProtocolGenerator.GenerationContext,
op: OperationShape,
writer: KotlinWriter
) {
// NOP
}

override val defaultTimestampFormat: TimestampFormatTrait.Format
get() = TODO("Unneeded for test")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment: I think we should reserve TODO for things that we honestly believe need to be done in the future. If we don't intend to implement unused interface members, I suggest using something with clearer semantics like fail or throw IllegalStateException.


override fun getProtocolHttpBindingResolver(model: Model, serviceShape: ServiceShape): HttpBindingResolver {
TODO("Unneeded for test")
}

override fun renderSerializeOperationBody(
ctx: ProtocolGenerator.GenerationContext,
op: OperationShape,
writer: KotlinWriter
) {
TODO("Unneeded for test")
}

override fun renderDeserializeOperationBody(
ctx: ProtocolGenerator.GenerationContext,
op: OperationShape,
writer: KotlinWriter
) {
TODO("Unneeded for test")
}

override fun renderSerializeDocumentBody(
ctx: ProtocolGenerator.GenerationContext,
shape: Shape,
writer: KotlinWriter
) {
TODO("Unneeded for test")
}

override fun renderDeserializeDocumentBody(
ctx: ProtocolGenerator.GenerationContext,
shape: Shape,
writer: KotlinWriter
) {
TODO("Unneeded for test")
}

override fun renderDeserializeException(
ctx: ProtocolGenerator.GenerationContext,
shape: Shape,
writer: KotlinWriter
) {
TODO("Unneeded for test")
}

override val protocol: ShapeId
get() = TODO("Unneeded for test")
}
}