Skip to content

Commit 649acaf

Browse files
authored
Auto-fill idempotency token values (#814)
This commit adds support for generating idempotency token values when not set on input. These values are populated with a UUID v4 value provided by the `uuid` package. This is done through setting the value on the provided input as to not further complicate protocol specific member serialization.
1 parent c18166b commit 649acaf

File tree

10 files changed

+79
-9
lines changed

10 files changed

+79
-9
lines changed

codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsDependency.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ public enum AwsDependency implements SymbolDependencyContainer {
5252
SQS_MIDDLEWARE(NORMAL_DEPENDENCY, "@aws-sdk/middleware-sdk-sqs", "^1.0.0-alpha.0"),
5353
XML_BUILDER(NORMAL_DEPENDENCY, "@aws-sdk/xml-builder", "^1.0.0-alpha.1"),
5454
XML_PARSER(NORMAL_DEPENDENCY, "pixl-xml", "^1.0.13"),
55-
XML_PARSER_TYPES(DEV_DEPENDENCY, "@types/pixl-xml", "^1.0.1");
55+
XML_PARSER_TYPES(DEV_DEPENDENCY, "@types/pixl-xml", "^1.0.1"),
56+
UUID_GENERATOR(NORMAL_DEPENDENCY, "uuid", "^3.4.0"),
57+
UUID_GENERATOR_TYPES(DEV_DEPENDENCY, "@types/uuid", "^3.4.7");
5658

5759
public final String packageName;
5860
public final String version;

codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsEc2.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public void generateSharedComponents(GenerationContext context) {
7676
super.generateSharedComponents(context);
7777
AwsProtocolUtils.generateXmlParseBody(context);
7878
AwsProtocolUtils.generateBuildFormUrlencodedString(context);
79+
AwsProtocolUtils.addItempotencyAutofillImport(context);
7980

8081
TypeScriptWriter writer = context.getWriter();
8182

codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsProtocolUtils.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@
2121
import software.amazon.smithy.aws.traits.UnsignedPayloadTrait;
2222
import software.amazon.smithy.model.knowledge.NeighborProviderIndex;
2323
import software.amazon.smithy.model.neighbor.Walker;
24+
import software.amazon.smithy.model.shapes.MemberShape;
2425
import software.amazon.smithy.model.shapes.OperationShape;
2526
import software.amazon.smithy.model.shapes.Shape;
2627
import software.amazon.smithy.model.shapes.ShapeVisitor;
28+
import software.amazon.smithy.model.traits.IdempotencyTokenTrait;
2729
import software.amazon.smithy.model.traits.XmlNamespaceTrait;
2830
import software.amazon.smithy.typescript.codegen.TypeScriptWriter;
2931
import software.amazon.smithy.typescript.codegen.integration.ProtocolGenerator.GenerationContext;
@@ -101,6 +103,12 @@ static void generateJsonParseBody(GenerationContext context) {
101103
writer.write("");
102104
}
103105

106+
/**
107+
* Writes a response body parser function for XML protocols. This
108+
* will parse a present body after converting it to utf-8.
109+
*
110+
* @param context The generation context.
111+
*/
104112
static void generateXmlParseBody(GenerationContext context) {
105113
TypeScriptWriter writer = context.getWriter();
106114

@@ -156,4 +164,40 @@ static void writeXmlNamespace(GenerationContext context, Shape shape, String nod
156164
writer.write("$L.addAttribute($S, $S);", nodeName, xmlns, trait.getUri());
157165
});
158166
}
167+
168+
/**
169+
* Imports a UUID v4 generating function used for auto-filling idempotency tokens.
170+
*
171+
* @param context The generation context.
172+
*/
173+
static void addItempotencyAutofillImport(GenerationContext context) {
174+
context.getModel().shapes(MemberShape.class)
175+
.filter(memberShape -> memberShape.hasTrait(IdempotencyTokenTrait.class))
176+
.findFirst()
177+
.ifPresent(memberShape -> {
178+
TypeScriptWriter writer = context.getWriter();
179+
180+
// Include the uuid package and import the v4 function as our more clearly named alias.
181+
writer.addDependency(AwsDependency.UUID_GENERATOR);
182+
writer.addDependency(AwsDependency.UUID_GENERATOR_TYPES);
183+
writer.addImport("v4", "generateIdempotencyToken", "uuid");
184+
});
185+
}
186+
187+
/**
188+
* Writes a statement that auto-fills the value of a member that is an idempotency
189+
* token if it is undefined at the time of serialization.
190+
*
191+
* @param context The generation context.
192+
* @param memberShape The member that may be an idempotency token.
193+
* @param inputLocation The location of input data for the member.
194+
*/
195+
static void writeIdempotencyAutofill(GenerationContext context, MemberShape memberShape, String inputLocation) {
196+
if (memberShape.hasTrait(IdempotencyTokenTrait.class)) {
197+
TypeScriptWriter writer = context.getWriter();
198+
199+
writer.openBlock("if ($L === undefined) {", "}", inputLocation, () ->
200+
writer.write("$L = generateIdempotencyToken();", inputLocation));
201+
}
202+
}
159203
}

codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsQuery.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public void generateSharedComponents(GenerationContext context) {
7676
super.generateSharedComponents(context);
7777
AwsProtocolUtils.generateXmlParseBody(context);
7878
AwsProtocolUtils.generateBuildFormUrlencodedString(context);
79+
AwsProtocolUtils.addItempotencyAutofillImport(context);
7980

8081
TypeScriptWriter writer = context.getWriter();
8182

codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsRestXml.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ protected void generateDocumentBodyShapeDeserializers(GenerationContext context,
8888
public void generateSharedComponents(GenerationContext context) {
8989
super.generateSharedComponents(context);
9090
AwsProtocolUtils.generateXmlParseBody(context);
91+
AwsProtocolUtils.addItempotencyAutofillImport(context);
9192

9293
TypeScriptWriter writer = context.getWriter();
9394
writer.addDependency(AwsDependency.XML_BUILDER);
@@ -159,8 +160,11 @@ protected void serializeInputDocument(
159160
MemberShape memberShape = binding.getMember();
160161
// The name of the member to get from the input shape.
161162
String memberName = symbolProvider.toMemberName(memberShape);
162-
163163
String inputLocation = "input." + memberName;
164+
165+
// Handle if the member is an idempotency token that should be auto-filled.
166+
AwsProtocolUtils.writeIdempotencyAutofill(context, memberShape, inputLocation);
167+
164168
writer.openBlock("if ($L !== undefined) {", "}", inputLocation, () -> {
165169
shapeSerVisitor.serializeNamedMember(context, memberName, memberShape, () -> inputLocation);
166170
});

codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/JsonRpcProtocolGenerator.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ protected void generateDocumentBodyShapeDeserializers(GenerationContext context,
7373
public void generateSharedComponents(GenerationContext context) {
7474
super.generateSharedComponents(context);
7575
AwsProtocolUtils.generateJsonParseBody(context);
76+
AwsProtocolUtils.addItempotencyAutofillImport(context);
7677
}
7778

7879
@Override

codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/JsonShapeSerVisitor.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,13 @@ public void serializeStructure(GenerationContext context, StructureShape shape)
9797
.map(JsonNameTrait::getValue)
9898
.orElse(memberName);
9999
Shape target = context.getModel().expectShape(memberShape.getTarget());
100+
String inputLocation = "input." + memberName;
101+
102+
// Handle if the member is an idempotency token that should be auto-filled.
103+
AwsProtocolUtils.writeIdempotencyAutofill(context, memberShape, inputLocation);
100104

101105
// Generate an if statement to set the bodyParam if the member is set.
102-
writer.openBlock("if (input.$L !== undefined) {", "}", memberName, () -> {
106+
writer.openBlock("if ($L !== undefined) {", "}", inputLocation, () -> {
103107
// Dispatch to the input value provider for any additional handling.
104108
writer.write("bodyParams['$L'] = $L;", locationName,
105109
target.accept(getMemberVisitor("input." + memberName)));

codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/QueryShapeSerVisitor.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ protected void serializeStructure(GenerationContext context, StructureShape shap
140140
// Serialize every member of the structure if present.
141141
shape.getAllMembers().forEach((memberName, memberShape) -> {
142142
String inputLocation = "input." + memberName;
143+
144+
// Handle if the member is an idempotency token that should be auto-filled.
145+
AwsProtocolUtils.writeIdempotencyAutofill(context, memberShape, inputLocation);
146+
143147
writer.openBlock("if ($L !== undefined) {", "}", inputLocation,
144148
() -> serializeNamedMember(context, memberName, memberShape, inputLocation));
145149
});

codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/RestJsonProtocolGenerator.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ protected void generateDocumentBodyShapeDeserializers(GenerationContext context,
7171
public void generateSharedComponents(GenerationContext context) {
7272
super.generateSharedComponents(context);
7373
AwsProtocolUtils.generateJsonParseBody(context);
74+
AwsProtocolUtils.addItempotencyAutofillImport(context);
7475
}
7576

7677
@Override
@@ -90,19 +91,23 @@ public void serializeInputDocument(
9091

9192
writer.write("const bodyParams: any = {};");
9293
for (HttpBinding binding : documentBindings) {
93-
MemberShape member = binding.getMember();
94+
MemberShape memberShape = binding.getMember();
9495
// The name of the member to get from the input shape.
95-
String memberName = symbolProvider.toMemberName(member);
96+
String memberName = symbolProvider.toMemberName(memberShape);
97+
String inputLocation = "input." + memberName;
9698
// Use the jsonName trait value if present, otherwise use the member name.
97-
String locationName = member.getTrait(JsonNameTrait.class)
99+
String locationName = memberShape.getTrait(JsonNameTrait.class)
98100
.map(JsonNameTrait::getValue)
99101
.orElseGet(binding::getLocationName);
100-
Shape target = context.getModel().expectShape(member.getTarget());
102+
Shape target = context.getModel().expectShape(memberShape.getTarget());
103+
104+
// Handle if the member is an idempotency token that should be auto-filled.
105+
AwsProtocolUtils.writeIdempotencyAutofill(context, memberShape, inputLocation);
101106

102107
// Generate an if statement to set the bodyParam if the member is set.
103-
writer.openBlock("if (input.$L !== undefined) {", "}", memberName, () -> {
108+
writer.openBlock("if ($L !== undefined) {", "}", inputLocation, () -> {
104109
writer.write("bodyParams['$L'] = $L;", locationName,
105-
target.accept(getMemberSerVisitor(context, "input." + memberName)));
110+
target.accept(getMemberSerVisitor(context, inputLocation)));
106111
});
107112
}
108113

codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/XmlShapeSerVisitor.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,10 @@ protected void serializeStructure(GenerationContext context, StructureShape shap
147147
Map<String, MemberShape> members = shape.getAllMembers();
148148
members.forEach((memberName, memberShape) -> {
149149
String inputLocation = "input." + memberName;
150+
151+
// Handle if the member is an idempotency token that should be auto-filled.
152+
AwsProtocolUtils.writeIdempotencyAutofill(context, memberShape, inputLocation);
153+
150154
writer.openBlock("if ($L !== undefined) {", "}", inputLocation, () -> {
151155
serializeNamedMember(context, memberName, memberShape, () -> inputLocation);
152156
});

0 commit comments

Comments
 (0)