Skip to content

Commit 148ca4d

Browse files
committed
fix: idempotency tokens being applied to nested structures
1 parent 8b02b88 commit 148ca4d

File tree

12 files changed

+435
-27
lines changed

12 files changed

+435
-27
lines changed

codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/Ec2Query.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ private class Ec2QuerySerializerGenerator(
8080
shape: Shape,
8181
members: List<MemberShape>,
8282
writer: KotlinWriter,
83+
idempotencyTokenEligible: Boolean,
8384
) {
8485
// render the serde descriptors
8586
descriptorGenerator(ctx, shape, members, writer).render()

codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/core/QueryHttpBindingProtocolGenerator.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ abstract class AbstractQueryFormUrlSerializerGenerator(
132132
) {
133133
val shape = ctx.model.expectShape(op.input.get())
134134
writer.write("val serializer = #T()", RuntimeTypes.Serde.SerdeFormUrl.FormUrlSerializer)
135-
renderSerializerBody(ctx, shape, documentMembers, writer)
135+
renderSerializerBody(ctx, shape, documentMembers, writer, true)
136136
writer.write("return serializer.toByteArray()")
137137
}
138138

@@ -156,12 +156,13 @@ abstract class AbstractQueryFormUrlSerializerGenerator(
156156
shape: Shape,
157157
members: List<MemberShape>,
158158
writer: KotlinWriter,
159+
idempotencyTokenEligible: Boolean = false,
159160
) {
160161
// render the serde descriptors
161162
descriptorGenerator(ctx, shape, members, writer).render()
162163
when (shape) {
163164
is UnionShape -> SerializeUnionGenerator(ctx, shape, members, writer, defaultTimestampFormat).render()
164-
else -> SerializeStructGenerator(ctx, members, writer, defaultTimestampFormat).render()
165+
else -> SerializeStructGenerator(ctx, members, writer, defaultTimestampFormat, idempotencyTokenEligible).render()
165166
}
166167
}
167168

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package software.amazon.smithy.kotlin.codegen.aws.protocols
2+
3+
import software.amazon.smithy.kotlin.codegen.test.*
4+
import kotlin.test.Test
5+
6+
class AwsQueryTest {
7+
@Test
8+
fun testNonNestedIdempotencyToken() {
9+
val ctx = model.newTestContext("Example")
10+
11+
val generator = AwsQuery()
12+
generator.generateProtocolClient(ctx.generationCtx)
13+
14+
ctx.generationCtx.delegator.finalize()
15+
ctx.generationCtx.delegator.flushWriters()
16+
17+
val expected = """
18+
serializer.serializeStruct(OBJ_DESCRIPTOR) {
19+
input.bar?.let { field(BAR_DESCRIPTOR, it) } ?: field(BAR_DESCRIPTOR, context.idempotencyTokenProvider.generateToken())
20+
}
21+
""".trimIndent()
22+
23+
val actual = ctx
24+
.manifest
25+
.expectFileString("/src/main/kotlin/com/test/serde/GetBarUnNestedOperationSerializer.kt")
26+
.lines(" serializer.serializeStruct(OBJ_DESCRIPTOR) {", " }")
27+
.trimIndent()
28+
29+
actual.shouldContainOnlyOnceWithDiff(expected)
30+
}
31+
32+
@Test
33+
fun testNestedIdempotencyToken() {
34+
val ctx = model.newTestContext("Example")
35+
36+
val generator = AwsQuery()
37+
generator.generateProtocolClient(ctx.generationCtx)
38+
39+
ctx.generationCtx.delegator.finalize()
40+
ctx.generationCtx.delegator.flushWriters()
41+
42+
val expected = """
43+
serializer.serializeStruct(OBJ_DESCRIPTOR) {
44+
input.baz?.let { field(BAZ_DESCRIPTOR, it) }
45+
}
46+
""".trimIndent()
47+
48+
val actual = ctx
49+
.manifest
50+
.expectFileString("/src/main/kotlin/com/test/serde/NestDocumentSerializer.kt")
51+
.lines(" serializer.serializeStruct(OBJ_DESCRIPTOR) {", " }")
52+
.trimIndent()
53+
54+
actual.shouldContainOnlyOnceWithDiff(expected)
55+
56+
val unexpected = """
57+
serializer.serializeStruct(OBJ_DESCRIPTOR) {
58+
input.baz?.let { field(BAZ_DESCRIPTOR, it) } ?: field(BAR_DESCRIPTOR, context.idempotencyTokenProvider.generateToken())
59+
}
60+
""".trimIndent()
61+
62+
actual.shouldNotContainOnlyOnceWithDiff(unexpected)
63+
}
64+
65+
private val model = """
66+
${"$"}version: "2"
67+
68+
namespace com.test
69+
70+
use aws.protocols#awsQuery
71+
use aws.api#service
72+
73+
@awsQuery
74+
@service(sdkId: "Example")
75+
@xmlNamespace(uri: "http://foo.com")
76+
service Example {
77+
version: "1.0.0",
78+
operations: [GetBarUnNested, GetBarNested]
79+
}
80+
81+
@http(method: "POST", uri: "/get-bar-un-nested")
82+
operation GetBarUnNested {
83+
input: BarUnNested
84+
}
85+
86+
structure BarUnNested {
87+
@idempotencyToken
88+
bar: String
89+
}
90+
91+
@http(method: "POST", uri: "/get-bar-nested")
92+
operation GetBarNested {
93+
input: BarNested
94+
}
95+
96+
structure BarNested {
97+
bar: Nest
98+
}
99+
100+
structure Nest {
101+
@idempotencyToken
102+
baz: String
103+
}
104+
""".toSmithyModel()
105+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package software.amazon.smithy.kotlin.codegen.aws.protocols
2+
3+
import software.amazon.smithy.kotlin.codegen.test.*
4+
import kotlin.test.Test
5+
6+
class RestJson1Test {
7+
@Test
8+
fun testNonNestedIdempotencyToken() {
9+
val ctx = model.newTestContext("Example")
10+
11+
val generator = RestJson1()
12+
generator.generateProtocolClient(ctx.generationCtx)
13+
14+
ctx.generationCtx.delegator.finalize()
15+
ctx.generationCtx.delegator.flushWriters()
16+
17+
val expected = """
18+
serializer.serializeStruct(OBJ_DESCRIPTOR) {
19+
input.bar?.let { field(BAR_DESCRIPTOR, it) } ?: field(BAR_DESCRIPTOR, context.idempotencyTokenProvider.generateToken())
20+
}
21+
""".trimIndent()
22+
23+
val actual = ctx
24+
.manifest
25+
.expectFileString("/src/main/kotlin/com/test/serde/GetBarUnNestedOperationSerializer.kt")
26+
.lines(" serializer.serializeStruct(OBJ_DESCRIPTOR) {", " }")
27+
.trimIndent()
28+
29+
actual.shouldContainOnlyOnceWithDiff(expected)
30+
}
31+
32+
@Test
33+
fun testNestedIdempotencyToken() {
34+
val ctx = model.newTestContext("Example")
35+
36+
val generator = RestJson1()
37+
generator.generateProtocolClient(ctx.generationCtx)
38+
39+
ctx.generationCtx.delegator.finalize()
40+
ctx.generationCtx.delegator.flushWriters()
41+
42+
val expected = """
43+
serializer.serializeStruct(OBJ_DESCRIPTOR) {
44+
input.baz?.let { field(BAZ_DESCRIPTOR, it) }
45+
}
46+
""".trimIndent()
47+
48+
val actual = ctx
49+
.manifest
50+
.expectFileString("/src/main/kotlin/com/test/serde/NestDocumentSerializer.kt")
51+
.lines(" serializer.serializeStruct(OBJ_DESCRIPTOR) {", " }")
52+
.trimIndent()
53+
54+
actual.shouldContainOnlyOnceWithDiff(expected)
55+
56+
val unexpected = """
57+
serializer.serializeStruct(OBJ_DESCRIPTOR) {
58+
input.baz?.let { field(BAZ_DESCRIPTOR, it) } ?: field(BAR_DESCRIPTOR, context.idempotencyTokenProvider.generateToken())
59+
}
60+
""".trimIndent()
61+
62+
actual.shouldNotContainOnlyOnceWithDiff(unexpected)
63+
}
64+
65+
private val model = """
66+
${"$"}version: "2"
67+
68+
namespace com.test
69+
70+
use aws.protocols#restJson1
71+
use aws.api#service
72+
73+
@restJson1
74+
@service(sdkId: "Example")
75+
service Example {
76+
version: "1.0.0",
77+
operations: [GetBarUnNested, GetBarNested]
78+
}
79+
80+
@http(method: "POST", uri: "/get-bar-un-nested")
81+
operation GetBarUnNested {
82+
input: BarUnNested
83+
}
84+
85+
structure BarUnNested {
86+
@idempotencyToken
87+
bar: String
88+
}
89+
90+
@http(method: "POST", uri: "/get-bar-nested")
91+
operation GetBarNested {
92+
input: BarNested
93+
}
94+
95+
structure BarNested {
96+
bar: Nest
97+
}
98+
99+
structure Nest {
100+
@idempotencyToken
101+
baz: String
102+
}
103+
""".toSmithyModel()
104+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package software.amazon.smithy.kotlin.codegen.aws.protocols
2+
3+
import software.amazon.smithy.kotlin.codegen.test.*
4+
import kotlin.test.Test
5+
6+
class RestXmlTest {
7+
@Test
8+
fun testNonNestedIdempotencyToken() {
9+
val ctx = model.newTestContext("Example")
10+
11+
val generator = RestXml()
12+
generator.generateProtocolClient(ctx.generationCtx)
13+
14+
ctx.generationCtx.delegator.finalize()
15+
ctx.generationCtx.delegator.flushWriters()
16+
17+
val expected = """
18+
serializer.serializeStruct(OBJ_DESCRIPTOR) {
19+
input.bar?.let { field(BAR_DESCRIPTOR, it) } ?: field(BAR_DESCRIPTOR, context.idempotencyTokenProvider.generateToken())
20+
}
21+
""".trimIndent()
22+
23+
val actual = ctx
24+
.manifest
25+
.expectFileString("/src/main/kotlin/com/test/serde/GetBarUnNestedOperationSerializer.kt")
26+
.lines(" serializer.serializeStruct(OBJ_DESCRIPTOR) {", " }")
27+
.trimIndent()
28+
29+
actual.shouldContainOnlyOnceWithDiff(expected)
30+
}
31+
32+
@Test
33+
fun testNestedIdempotencyToken() {
34+
val ctx = model.newTestContext("Example")
35+
36+
val generator = RestXml()
37+
generator.generateProtocolClient(ctx.generationCtx)
38+
39+
ctx.generationCtx.delegator.finalize()
40+
ctx.generationCtx.delegator.flushWriters()
41+
42+
val expected = """
43+
serializer.serializeStruct(OBJ_DESCRIPTOR) {
44+
input.baz?.let { field(BAZ_DESCRIPTOR, it) }
45+
}
46+
""".trimIndent()
47+
48+
val actual = ctx
49+
.manifest
50+
.expectFileString("/src/main/kotlin/com/test/serde/NestDocumentSerializer.kt")
51+
.lines(" serializer.serializeStruct(OBJ_DESCRIPTOR) {", " }")
52+
.trimIndent()
53+
54+
actual.shouldContainOnlyOnceWithDiff(expected)
55+
56+
val unexpected = """
57+
serializer.serializeStruct(OBJ_DESCRIPTOR) {
58+
input.baz?.let { field(BAZ_DESCRIPTOR, it) } ?: field(BAR_DESCRIPTOR, context.idempotencyTokenProvider.generateToken())
59+
}
60+
""".trimIndent()
61+
62+
actual.shouldNotContainOnlyOnceWithDiff(unexpected)
63+
}
64+
65+
private val model = """
66+
${"$"}version: "2"
67+
68+
namespace com.test
69+
70+
use aws.protocols#restXml
71+
use aws.api#service
72+
73+
@restXml
74+
@service(sdkId: "Example")
75+
service Example {
76+
version: "1.0.0",
77+
operations: [GetBarUnNested, GetBarNested]
78+
}
79+
80+
@http(method: "POST", uri: "/get-bar-un-nested")
81+
operation GetBarUnNested {
82+
input: BarUnNested
83+
}
84+
85+
structure BarUnNested {
86+
@idempotencyToken
87+
bar: String
88+
}
89+
90+
@http(method: "POST", uri: "/get-bar-nested")
91+
operation GetBarNested {
92+
input: BarNested
93+
}
94+
95+
structure BarNested {
96+
bar: Nest
97+
}
98+
99+
structure Nest {
100+
@idempotencyToken
101+
baz: String
102+
}
103+
""".toSmithyModel()
104+
}

0 commit comments

Comments
 (0)