Skip to content

Commit f8a73cc

Browse files
authored
fix: ignore __type when deserializing union for AWS Json protocols (#1054)
1 parent 551cd81 commit f8a73cc

File tree

7 files changed

+194
-1
lines changed

7 files changed

+194
-1
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"id": "ac672b00-f8bb-4235-8db4-aad0ae2f157d",
3+
"type": "bugfix",
4+
"description": "ignore `__type` when deserializing union for AWS JSON 1.0, AWS JSON 1.1, and AWS restJson 1",
5+
"issues": [
6+
"awslabs/aws-sdk-kotlin#1044"
7+
]
8+
}

codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/protocols/AwsJson1_0.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@ package aws.sdk.kotlin.codegen.protocols
77
import aws.sdk.kotlin.codegen.protocols.core.AwsHttpBindingProtocolGenerator
88
import aws.sdk.kotlin.codegen.protocols.json.AwsJsonHttpBindingResolver
99
import aws.sdk.kotlin.codegen.protocols.json.AwsJsonProtocolMiddleware
10+
import aws.sdk.kotlin.codegen.protocols.json.AwsJsonProtocolParserGenerator
1011
import aws.sdk.kotlin.codegen.protocols.json.JsonHttpBindingProtocolGenerator
1112
import software.amazon.smithy.aws.traits.protocols.AwsJson1_0Trait
1213
import software.amazon.smithy.kotlin.codegen.rendering.protocol.HttpBindingResolver
1314
import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator
1415
import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolMiddleware
16+
import software.amazon.smithy.kotlin.codegen.rendering.serde.StructuredDataParserGenerator
1517
import software.amazon.smithy.model.Model
16-
import software.amazon.smithy.model.shapes.*
18+
import software.amazon.smithy.model.shapes.ServiceShape
19+
import software.amazon.smithy.model.shapes.ShapeId
1720

1821
/**
1922
* Handles generating the aws.protocols#awsJson1_0 protocol for services.
@@ -36,4 +39,7 @@ class AwsJson1_0 : JsonHttpBindingProtocolGenerator() {
3639

3740
override fun getProtocolHttpBindingResolver(model: Model, serviceShape: ServiceShape): HttpBindingResolver =
3841
AwsJsonHttpBindingResolver(model, serviceShape, "application/x-amz-json-1.0")
42+
43+
override fun structuredDataParser(ctx: ProtocolGenerator.GenerationContext): StructuredDataParserGenerator =
44+
AwsJsonProtocolParserGenerator(this, supportsJsonNameTrait)
3945
}

codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/protocols/AwsJson1_1.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ package aws.sdk.kotlin.codegen.protocols
88
import aws.sdk.kotlin.codegen.protocols.core.AwsHttpBindingProtocolGenerator
99
import aws.sdk.kotlin.codegen.protocols.json.AwsJsonHttpBindingResolver
1010
import aws.sdk.kotlin.codegen.protocols.json.AwsJsonProtocolMiddleware
11+
import aws.sdk.kotlin.codegen.protocols.json.AwsJsonProtocolParserGenerator
1112
import aws.sdk.kotlin.codegen.protocols.json.JsonHttpBindingProtocolGenerator
1213
import software.amazon.smithy.aws.traits.protocols.AwsJson1_1Trait
1314
import software.amazon.smithy.kotlin.codegen.rendering.protocol.HttpBindingResolver
1415
import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator
1516
import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolMiddleware
17+
import software.amazon.smithy.kotlin.codegen.rendering.serde.StructuredDataParserGenerator
1618
import software.amazon.smithy.model.Model
1719
import software.amazon.smithy.model.shapes.ServiceShape
1820
import software.amazon.smithy.model.shapes.ShapeId
@@ -38,4 +40,7 @@ class AwsJson1_1 : JsonHttpBindingProtocolGenerator() {
3840

3941
override fun getProtocolHttpBindingResolver(model: Model, serviceShape: ServiceShape): HttpBindingResolver =
4042
AwsJsonHttpBindingResolver(model, serviceShape, "application/x-amz-json-1.1")
43+
44+
override fun structuredDataParser(ctx: ProtocolGenerator.GenerationContext): StructuredDataParserGenerator =
45+
AwsJsonProtocolParserGenerator(this, supportsJsonNameTrait)
4146
}

codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/protocols/RestJson1.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
package aws.sdk.kotlin.codegen.protocols
66

77
import aws.sdk.kotlin.codegen.protocols.core.AwsHttpBindingProtocolGenerator
8+
import aws.sdk.kotlin.codegen.protocols.json.AwsJsonProtocolParserGenerator
89
import aws.sdk.kotlin.codegen.protocols.json.JsonHttpBindingProtocolGenerator
910
import software.amazon.smithy.aws.traits.protocols.RestJson1Trait
1011
import software.amazon.smithy.kotlin.codegen.core.KotlinWriter
1112
import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes
1213
import software.amazon.smithy.kotlin.codegen.core.defaultName
1314
import software.amazon.smithy.kotlin.codegen.core.withBlock
1415
import software.amazon.smithy.kotlin.codegen.rendering.protocol.*
16+
import software.amazon.smithy.kotlin.codegen.rendering.serde.StructuredDataParserGenerator
1517
import software.amazon.smithy.model.Model
1618
import software.amazon.smithy.model.knowledge.HttpBinding
1719
import software.amazon.smithy.model.shapes.OperationShape
@@ -62,4 +64,7 @@ class RestJson1 : JsonHttpBindingProtocolGenerator() {
6264
}
6365
}
6466
}
67+
68+
override fun structuredDataParser(ctx: ProtocolGenerator.GenerationContext): StructuredDataParserGenerator =
69+
AwsJsonProtocolParserGenerator(this, supportsJsonNameTrait)
6570
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package aws.sdk.kotlin.codegen.protocols.json
7+
8+
import software.amazon.smithy.kotlin.codegen.core.KotlinWriter
9+
import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator
10+
import software.amazon.smithy.kotlin.codegen.rendering.protocol.toRenderingContext
11+
import software.amazon.smithy.kotlin.codegen.rendering.serde.JsonParserGenerator
12+
import software.amazon.smithy.kotlin.codegen.rendering.serde.JsonSerdeDescriptorGenerator
13+
import software.amazon.smithy.model.shapes.MemberShape
14+
import software.amazon.smithy.model.shapes.Shape
15+
16+
/**
17+
* Overrides the [JsonParserGenerator] when using `AWS Json 1.0`, `AWS Json 1.1`, and `AWS RestJson 1` protocols.
18+
*
19+
* See https://github.com/smithy-lang/smithy/pull/1945
20+
*/
21+
class AwsJsonProtocolParserGenerator(
22+
private val protocolGenerator: ProtocolGenerator,
23+
private val supportsJsonNameTrait: Boolean = true,
24+
) : JsonParserGenerator(protocolGenerator, supportsJsonNameTrait) {
25+
26+
override fun descriptorGenerator(
27+
ctx: ProtocolGenerator.GenerationContext,
28+
shape: Shape,
29+
members: List<MemberShape>,
30+
writer: KotlinWriter,
31+
): JsonSerdeDescriptorGenerator = AwsJsonProtocolSerdeDescriptorGenerator(ctx.toRenderingContext(protocolGenerator, shape, writer), members, supportsJsonNameTrait)
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package aws.sdk.kotlin.codegen.protocols.json
7+
8+
import software.amazon.smithy.kotlin.codegen.core.RenderingContext
9+
import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes
10+
import software.amazon.smithy.kotlin.codegen.rendering.serde.JsonSerdeDescriptorGenerator
11+
import software.amazon.smithy.kotlin.codegen.rendering.serde.SdkFieldDescriptorTrait
12+
import software.amazon.smithy.kotlin.codegen.rendering.serde.add
13+
import software.amazon.smithy.kotlin.codegen.utils.dq
14+
import software.amazon.smithy.model.shapes.MemberShape
15+
import software.amazon.smithy.model.shapes.Shape
16+
17+
/**
18+
* Overrides the [JsonSerdeDescriptorGenerator] when using `AWS Json 1.0`, `AWS Json 1.1`, and `AWS RestJson 1` protocols.
19+
*
20+
* See: https://github.com/smithy-lang/smithy/pull/1945
21+
*/
22+
class AwsJsonProtocolSerdeDescriptorGenerator(
23+
ctx: RenderingContext<Shape>,
24+
memberShapes: List<MemberShape>? = null,
25+
supportsJsonNameTrait: Boolean = true,
26+
) : JsonSerdeDescriptorGenerator(ctx, memberShapes, supportsJsonNameTrait) {
27+
28+
/**
29+
* Adds a trait to ignore `__type` in union shapes for `AWS Json 1.0`, `AWS Json 1.1`, `RestJson 1.0` protocols
30+
* Sometimes the unnecessary field `__type` is added and needs to be ignored
31+
*
32+
* NOTE: Will be ignored unless it's in the model
33+
*
34+
* Source: https://github.com/smithy-lang/smithy/pull/1945
35+
*/
36+
override fun getObjectDescriptorTraits(): List<SdkFieldDescriptorTrait> {
37+
val traitList = super.getObjectDescriptorTraits().toMutableList()
38+
val typeMember = memberShapes.find { it.memberName == "__type" }
39+
40+
if (ctx.shape?.isUnionShape == true && typeMember == null) {
41+
traitList.add(RuntimeTypes.Serde.SerdeJson.IgnoreKey, "__type".dq())
42+
}
43+
44+
return traitList
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package aws.sdk.kotlin.codegen.protocols.json
7+
8+
import software.amazon.smithy.kotlin.codegen.test.*
9+
import software.amazon.smithy.model.shapes.ShapeId
10+
import kotlin.test.Test
11+
12+
class AwsJsonProtocolSerdeDescriptorGeneratorTest {
13+
@Test
14+
fun itAddsIgnoreKeysTrait() {
15+
val model = """
16+
@http(method: "POST", uri: "/foo")
17+
operation Foo {
18+
input: FooRequest
19+
}
20+
21+
structure FooRequest {
22+
strVal: String,
23+
intVal: Integer
24+
}
25+
26+
union Bar {
27+
x: String,
28+
y: String,
29+
}
30+
""".prependNamespaceAndService(operations = listOf("Foo")).toSmithyModel()
31+
32+
val testCtx = model.newTestContext()
33+
val writer = testCtx.newWriter()
34+
val shape = model.expectShape(ShapeId.from("com.test#Bar"))
35+
val renderingCtx = testCtx.toRenderingContext(writer, shape)
36+
37+
AwsJsonProtocolSerdeDescriptorGenerator(renderingCtx).render()
38+
39+
val expectedDescriptors = """
40+
val X_DESCRIPTOR = SdkFieldDescriptor(SerialKind.String, JsonSerialName("x"))
41+
val Y_DESCRIPTOR = SdkFieldDescriptor(SerialKind.String, JsonSerialName("y"))
42+
val OBJ_DESCRIPTOR = SdkObjectDescriptor.build {
43+
trait(IgnoreKey("__type"))
44+
field(X_DESCRIPTOR)
45+
field(Y_DESCRIPTOR)
46+
}
47+
""".formatForTest("")
48+
49+
val contents = writer.toString()
50+
contents.shouldContainOnlyOnceWithDiff(expectedDescriptors)
51+
}
52+
53+
@Test
54+
fun itDoesNotAddIgnoreKeysTrait() {
55+
val model = """
56+
@http(method: "POST", uri: "/foo")
57+
operation Foo {
58+
input: FooRequest
59+
}
60+
61+
structure FooRequest {
62+
strVal: String,
63+
intVal: Integer
64+
}
65+
66+
union Bar {
67+
__type: String,
68+
y: String,
69+
}
70+
""".prependNamespaceAndService(operations = listOf("Foo")).toSmithyModel()
71+
72+
val testCtx = model.newTestContext()
73+
val writer = testCtx.newWriter()
74+
val shape = model.expectShape(ShapeId.from("com.test#Bar"))
75+
val renderingCtx = testCtx.toRenderingContext(writer, shape)
76+
77+
AwsJsonProtocolSerdeDescriptorGenerator(renderingCtx).render()
78+
79+
val expectedDescriptors = """
80+
val TYPE_DESCRIPTOR = SdkFieldDescriptor(SerialKind.String, JsonSerialName("__type"))
81+
val Y_DESCRIPTOR = SdkFieldDescriptor(SerialKind.String, JsonSerialName("y"))
82+
val OBJ_DESCRIPTOR = SdkObjectDescriptor.build {
83+
field(TYPE_DESCRIPTOR)
84+
field(Y_DESCRIPTOR)
85+
}
86+
""".formatForTest("")
87+
88+
val contents = writer.toString()
89+
contents.shouldContainOnlyOnceWithDiff(expectedDescriptors)
90+
}
91+
}

0 commit comments

Comments
 (0)