Skip to content

Commit fe19f6a

Browse files
kstichtrivikr
authored andcommitted
feat: update JSON protocol codegen for cleanliness (#547)
* Update JSON protocol codegen for cleanliness This commit includes assorted updates to the protocol generators to improve reusability, set proper visibility, use an import alias for SerdeContext, and other minor updates. It also includes a fix for handling the unknown case when deserializing a union. * Use Model instead of deprecated ShapeIndex * Use better name for document body shape serde gen
1 parent bd8d970 commit fe19f6a

File tree

10 files changed

+65
-64
lines changed

10 files changed

+65
-64
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
*
2323
* @see JsonRpcProtocolGenerator
2424
*/
25-
public class AwsJsonRpc1_0 extends JsonRpcProtocolGenerator {
25+
final class AwsJsonRpc1_0 extends JsonRpcProtocolGenerator {
2626

2727
@Override
2828
protected String getDocumentContentType() {

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
*
2323
* @see JsonRpcProtocolGenerator
2424
*/
25-
public class AwsJsonRpc1_1 extends JsonRpcProtocolGenerator {
25+
final class AwsJsonRpc1_1 extends JsonRpcProtocolGenerator {
2626

2727
@Override
2828
protected String getDocumentContentType() {

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

+32-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,14 @@
1515

1616
package software.amazon.smithy.aws.typescript.codegen;
1717

18+
import java.util.Set;
19+
import java.util.TreeSet;
1820
import software.amazon.smithy.aws.traits.UnsignedPayloadTrait;
21+
import software.amazon.smithy.model.knowledge.NeighborProviderIndex;
22+
import software.amazon.smithy.model.neighbor.Walker;
1923
import software.amazon.smithy.model.shapes.OperationShape;
24+
import software.amazon.smithy.model.shapes.Shape;
25+
import software.amazon.smithy.model.shapes.ShapeVisitor;
2026
import software.amazon.smithy.typescript.codegen.TypeScriptWriter;
2127
import software.amazon.smithy.typescript.codegen.integration.ProtocolGenerator.GenerationContext;
2228

@@ -46,6 +52,30 @@ static void generateUnsignedPayloadSigV4Header(GenerationContext context, Operat
4652
});
4753
}
4854

55+
/**
56+
* Writes a serde function for a set of shapes using the passed visitor.
57+
* This will walk the input set of shapes and invoke the visitor for any
58+
* members of aggregate shapes in the set.
59+
*
60+
* @see software.amazon.smithy.typescript.codegen.integration.DocumentShapeSerVisitor
61+
* @see software.amazon.smithy.typescript.codegen.integration.DocumentShapeDeserVisitor
62+
*
63+
* @param context The generation context.
64+
* @param shapes A list of shapes to generate serde for, including their members.
65+
* @param visitor A ShapeVisitor that generates a serde function for shapes.
66+
*/
67+
static void generateDocumentBodyShapeSerde(
68+
GenerationContext context,
69+
Set<Shape> shapes,
70+
ShapeVisitor<Void> visitor
71+
) {
72+
// Walk all the shapes within those in the document and generate for them as well.
73+
Walker shapeWalker = new Walker(context.getModel().getKnowledge(NeighborProviderIndex.class).getProvider());
74+
Set<Shape> shapesToGenerate = new TreeSet<>(shapes);
75+
shapes.forEach(shape -> shapesToGenerate.addAll(shapeWalker.walkShapes(shape)));
76+
shapesToGenerate.forEach(shape -> shape.accept(visitor));
77+
}
78+
4979
/**
5080
* Writes a response body parser function for JSON protocols. This
5181
* will parse a present body after converting it to utf-8.
@@ -56,8 +86,8 @@ static void generateJsonParseBody(GenerationContext context) {
5686
TypeScriptWriter writer = context.getWriter();
5787

5888
// Include a JSON body parser used to deserialize documents from HTTP responses.
59-
writer.addImport("SerdeContext", "SerdeContext", "@aws-sdk/types");
60-
writer.openBlock("const parseBody = (streamBody: any, context: SerdeContext): any => {", "};", () -> {
89+
writer.addImport("SerdeContext", "__SerdeContext", "@aws-sdk/types");
90+
writer.openBlock("const parseBody = (streamBody: any, context: __SerdeContext): any => {", "};", () -> {
6191
writer.openBlock("return context.streamCollector(streamBody).then((body: any) => {", "});", () -> {
6292
writer.write("const encoded = context.utf8Encoder(body);");
6393
writer.openBlock("if (encoded.length) {", "}", () -> {

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

+1-5
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,7 @@ final class JsonMemberDeserVisitor extends DocumentMemberDeserVisitor {
3232
/**
3333
* @inheritDoc
3434
*/
35-
JsonMemberDeserVisitor(
36-
GenerationContext context,
37-
String dataSource,
38-
Format defaultTimestampFormat
39-
) {
35+
JsonMemberDeserVisitor(GenerationContext context, String dataSource, Format defaultTimestampFormat) {
4036
super(context, dataSource, defaultTimestampFormat);
4137
}
4238

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

+2-6
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,7 @@ final class JsonMemberSerVisitor extends DocumentMemberSerVisitor {
3232
/**
3333
* @inheritDoc
3434
*/
35-
JsonMemberSerVisitor(
36-
GenerationContext context,
37-
String dataSource,
38-
Format defaultTimestampFormat
39-
) {
35+
JsonMemberSerVisitor(GenerationContext context, String dataSource, Format defaultTimestampFormat) {
4036
super(context, dataSource, defaultTimestampFormat);
4137
}
4238

@@ -53,7 +49,7 @@ public String bigIntegerShape(BigIntegerShape shape) {
5349
}
5450

5551
private String unsupportedShape(Shape shape) {
56-
throw new CodegenException(String.format("Cannot deserialize shape type %s on protocol, shape: %s.",
52+
throw new CodegenException(String.format("Cannot serialize shape type %s on protocol, shape: %s.",
5753
shape.getType(), shape.getId()));
5854
}
5955
}

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

+5-16
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,8 @@
1616
package software.amazon.smithy.aws.typescript.codegen;
1717

1818
import java.util.Set;
19-
import java.util.TreeSet;
20-
import software.amazon.smithy.model.knowledge.NeighborProviderIndex;
21-
import software.amazon.smithy.model.neighbor.Walker;
2219
import software.amazon.smithy.model.shapes.OperationShape;
2320
import software.amazon.smithy.model.shapes.Shape;
24-
import software.amazon.smithy.model.shapes.ShapeVisitor;
2521
import software.amazon.smithy.model.shapes.StructureShape;
2622
import software.amazon.smithy.model.traits.TimestampFormatTrait.Format;
2723
import software.amazon.smithy.typescript.codegen.TypeScriptWriter;
@@ -41,6 +37,7 @@
4137
* @see JsonShapeDeserVisitor
4238
* @see JsonMemberSerVisitor
4339
* @see JsonMemberDeserVisitor
40+
* @see AwsProtocolUtils
4441
*/
4542
abstract class JsonRpcProtocolGenerator extends HttpRpcProtocolGenerator {
4643

@@ -49,21 +46,13 @@ protected Format getDocumentTimestampFormat() {
4946
}
5047

5148
@Override
52-
protected void generateDocumentShapeSerializers(GenerationContext context, Set<Shape> shapes) {
53-
generateDocumentShapeSerde(context, shapes, new JsonShapeSerVisitor(context));
49+
protected void generateDocumentBodyShapeSerializers(GenerationContext context, Set<Shape> shapes) {
50+
AwsProtocolUtils.generateDocumentBodyShapeSerde(context, shapes, new JsonShapeSerVisitor(context));
5451
}
5552

5653
@Override
57-
protected void generateDocumentShapeDeserializers(GenerationContext context, Set<Shape> shapes) {
58-
generateDocumentShapeSerde(context, shapes, new JsonShapeDeserVisitor(context));
59-
}
60-
61-
private void generateDocumentShapeSerde(GenerationContext context, Set<Shape> shapes, ShapeVisitor<Void> visitor) {
62-
// Walk all the shapes within those in the document and generate for them as well.
63-
Walker shapeWalker = new Walker(context.getModel().getKnowledge(NeighborProviderIndex.class).getProvider());
64-
Set<Shape> shapesToDeserialize = new TreeSet<>(shapes);
65-
shapes.forEach(shape -> shapesToDeserialize.addAll(shapeWalker.walkShapes(shape)));
66-
shapesToDeserialize.forEach(shape -> shape.accept(visitor));
54+
protected void generateDocumentBodyShapeDeserializers(GenerationContext context, Set<Shape> shapes) {
55+
AwsProtocolUtils.generateDocumentBodyShapeSerde(context, shapes, new JsonShapeDeserVisitor(context));
6756
}
6857

6958
@Override

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

+8-7
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@
1717

1818
import java.util.Map;
1919
import java.util.TreeMap;
20+
import software.amazon.smithy.model.Model;
2021
import software.amazon.smithy.model.shapes.CollectionShape;
2122
import software.amazon.smithy.model.shapes.DocumentShape;
2223
import software.amazon.smithy.model.shapes.MapShape;
2324
import software.amazon.smithy.model.shapes.MemberShape;
2425
import software.amazon.smithy.model.shapes.Shape;
25-
import software.amazon.smithy.model.shapes.ShapeIndex;
2626
import software.amazon.smithy.model.shapes.StructureShape;
2727
import software.amazon.smithy.model.shapes.UnionShape;
2828
import software.amazon.smithy.model.traits.JsonNameTrait;
@@ -54,7 +54,7 @@ private DocumentMemberDeserVisitor getMemberVisitor(String dataSource) {
5454
@Override
5555
protected void deserializeCollection(GenerationContext context, CollectionShape shape) {
5656
TypeScriptWriter writer = context.getWriter();
57-
Shape target = context.getModel().getShapeIndex().getShape(shape.getMember().getTarget()).get();
57+
Shape target = context.getModel().expectShape(shape.getMember().getTarget());
5858

5959
// Dispatch to the output value provider for any additional handling.
6060
writer.openBlock("return (output || []).map((entry: any) =>", ");", () -> {
@@ -72,7 +72,7 @@ protected void deserializeDocument(GenerationContext context, DocumentShape shap
7272
@Override
7373
protected void deserializeMap(GenerationContext context, MapShape shape) {
7474
TypeScriptWriter writer = context.getWriter();
75-
Shape target = context.getModel().getShapeIndex().getShape(shape.getValue().getTarget()).get();
75+
Shape target = context.getModel().expectShape(shape.getValue().getTarget());
7676

7777
// Get the right serialization for each entry in the map. Undefined
7878
// outputs won't have this deserializer invoked.
@@ -101,7 +101,7 @@ protected void deserializeStructure(GenerationContext context, StructureShape sh
101101
String locationName = memberShape.getTrait(JsonNameTrait.class)
102102
.map(JsonNameTrait::getValue)
103103
.orElse(memberName);
104-
Shape target = context.getModel().getShapeIndex().getShape(memberShape.getTarget()).get();
104+
Shape target = context.getModel().expectShape(memberShape.getTarget());
105105

106106
// Generate an if statement to set the bodyParam if the member is set.
107107
writer.openBlock("if (output.$L !== undefined) {", "}", locationName, () -> {
@@ -117,12 +117,12 @@ protected void deserializeStructure(GenerationContext context, StructureShape sh
117117
@Override
118118
protected void deserializeUnion(GenerationContext context, UnionShape shape) {
119119
TypeScriptWriter writer = context.getWriter();
120-
ShapeIndex index = context.getModel().getShapeIndex();
120+
Model model = context.getModel();
121121

122122
// Check for any known union members and return when we find one.
123123
Map<String, MemberShape> members = new TreeMap<>(shape.getAllMembers());
124124
members.forEach((memberName, memberShape) -> {
125-
Shape target = index.getShape(memberShape.getTarget()).get();
125+
Shape target = model.expectShape(memberShape.getTarget());
126126
// Use the jsonName trait value if present, otherwise use the member name.
127127
String locationName = memberShape.getTrait(JsonNameTrait.class)
128128
.map(JsonNameTrait::getValue)
@@ -135,6 +135,7 @@ protected void deserializeUnion(GenerationContext context, UnionShape shape) {
135135
});
136136
});
137137
// Or write to the unknown member the element in the output.
138-
writer.write("return { $$unknown: output[Object.keys(output)[0]] };");
138+
writer.write("const key = Object.keys(output)[0];");
139+
writer.write("return { $$unknown: [key, output[key]] };");
139140
}
140141
}

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

+6-6
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@
1717

1818
import java.util.Map;
1919
import java.util.TreeMap;
20+
import software.amazon.smithy.model.Model;
2021
import software.amazon.smithy.model.shapes.CollectionShape;
2122
import software.amazon.smithy.model.shapes.DocumentShape;
2223
import software.amazon.smithy.model.shapes.MapShape;
2324
import software.amazon.smithy.model.shapes.MemberShape;
2425
import software.amazon.smithy.model.shapes.Shape;
25-
import software.amazon.smithy.model.shapes.ShapeIndex;
2626
import software.amazon.smithy.model.shapes.StructureShape;
2727
import software.amazon.smithy.model.shapes.UnionShape;
2828
import software.amazon.smithy.model.traits.JsonNameTrait;
@@ -54,7 +54,7 @@ private DocumentMemberSerVisitor getMemberVisitor(String dataSource) {
5454
@Override
5555
public void serializeCollection(GenerationContext context, CollectionShape shape) {
5656
TypeScriptWriter writer = context.getWriter();
57-
Shape target = context.getModel().getShapeIndex().getShape(shape.getMember().getTarget()).get();
57+
Shape target = context.getModel().expectShape(shape.getMember().getTarget());
5858

5959
// Dispatch to the input value provider for any additional handling.
6060
writer.openBlock("return (input || []).map(entry =>", ");", () -> {
@@ -72,7 +72,7 @@ public void serializeDocument(GenerationContext context, DocumentShape shape) {
7272
@Override
7373
public void serializeMap(GenerationContext context, MapShape shape) {
7474
TypeScriptWriter writer = context.getWriter();
75-
Shape target = context.getModel().getShapeIndex().getShape(shape.getValue().getTarget()).get();
75+
Shape target = context.getModel().expectShape(shape.getValue().getTarget());
7676

7777
// Get the right serialization for each entry in the map. Undefined
7878
// inputs won't have this serializer invoked.
@@ -96,7 +96,7 @@ public void serializeStructure(GenerationContext context, StructureShape shape)
9696
String locationName = memberShape.getTrait(JsonNameTrait.class)
9797
.map(JsonNameTrait::getValue)
9898
.orElse(memberName);
99-
Shape target = context.getModel().getShapeIndex().getShape(memberShape.getTarget()).get();
99+
Shape target = context.getModel().expectShape(memberShape.getTarget());
100100

101101
// Generate an if statement to set the bodyParam if the member is set.
102102
writer.openBlock("if (input.$L !== undefined) {", "}", memberName, () -> {
@@ -111,14 +111,14 @@ public void serializeStructure(GenerationContext context, StructureShape shape)
111111
@Override
112112
public void serializeUnion(GenerationContext context, UnionShape shape) {
113113
TypeScriptWriter writer = context.getWriter();
114-
ShapeIndex index = context.getModel().getShapeIndex();
114+
Model model = context.getModel();
115115

116116
// Visit over the union type, then get the right serialization for the member.
117117
writer.openBlock("return $L.visit(input, {", "});", shape.getId().getName(), () -> {
118118
// Use a TreeMap to sort the members.
119119
Map<String, MemberShape> members = new TreeMap<>(shape.getAllMembers());
120120
members.forEach((memberName, memberShape) -> {
121-
Shape target = index.getShape(memberShape.getTarget()).get();
121+
Shape target = model.expectShape(memberShape.getTarget());
122122
// Dispatch to the input value provider for any additional handling.
123123
writer.write("$L: value => $L,", memberName, target.accept(getMemberVisitor("value")));
124124
});

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

+8-19
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,11 @@
1717

1818
import java.util.List;
1919
import java.util.Set;
20-
import java.util.TreeSet;
2120
import software.amazon.smithy.codegen.core.SymbolProvider;
2221
import software.amazon.smithy.model.knowledge.HttpBinding;
23-
import software.amazon.smithy.model.knowledge.NeighborProviderIndex;
24-
import software.amazon.smithy.model.neighbor.Walker;
2522
import software.amazon.smithy.model.shapes.MemberShape;
2623
import software.amazon.smithy.model.shapes.OperationShape;
2724
import software.amazon.smithy.model.shapes.Shape;
28-
import software.amazon.smithy.model.shapes.ShapeVisitor;
2925
import software.amazon.smithy.model.traits.JsonNameTrait;
3026
import software.amazon.smithy.model.traits.TimestampFormatTrait;
3127
import software.amazon.smithy.typescript.codegen.TypeScriptWriter;
@@ -45,6 +41,7 @@
4541
* @see JsonShapeDeserVisitor
4642
* @see JsonMemberSerVisitor
4743
* @see JsonMemberDeserVisitor
44+
* @see AwsProtocolUtils
4845
* @see <a href="https://awslabs.github.io/smithy/spec/http.html">Smithy HTTP protocol bindings.</a>
4946
*/
5047
abstract class RestJsonProtocolGenerator extends HttpBindingProtocolGenerator {
@@ -55,21 +52,13 @@ protected TimestampFormatTrait.Format getDocumentTimestampFormat() {
5552
}
5653

5754
@Override
58-
protected void generateDocumentShapeSerializers(GenerationContext context, Set<Shape> shapes) {
59-
generateDocumentShapeSerde(context, shapes, new JsonShapeSerVisitor(context));
55+
protected void generateDocumentBodyShapeSerializers(GenerationContext context, Set<Shape> shapes) {
56+
AwsProtocolUtils.generateDocumentBodyShapeSerde(context, shapes, new JsonShapeSerVisitor(context));
6057
}
6158

6259
@Override
63-
protected void generateDocumentShapeDeserializers(GenerationContext context, Set<Shape> shapes) {
64-
generateDocumentShapeSerde(context, shapes, new JsonShapeDeserVisitor(context));
65-
}
66-
67-
private void generateDocumentShapeSerde(GenerationContext context, Set<Shape> shapes, ShapeVisitor<Void> visitor) {
68-
// Walk all the shapes within those in the document and generate for them as well.
69-
Walker shapeWalker = new Walker(context.getModel().getKnowledge(NeighborProviderIndex.class).getProvider());
70-
Set<Shape> shapesToDeserialize = new TreeSet<>(shapes);
71-
shapes.forEach(shape -> shapesToDeserialize.addAll(shapeWalker.walkShapes(shape)));
72-
shapesToDeserialize.forEach(shape -> shape.accept(visitor));
60+
protected void generateDocumentBodyShapeDeserializers(GenerationContext context, Set<Shape> shapes) {
61+
AwsProtocolUtils.generateDocumentBodyShapeSerde(context, shapes, new JsonShapeDeserVisitor(context));
7362
}
7463

7564
@Override
@@ -99,10 +88,10 @@ public void serializeInputDocument(
9988
// The name of the member to get from the input shape.
10089
String memberName = symbolProvider.toMemberName(member);
10190
// Use the jsonName trait value if present, otherwise use the member name.
102-
String locationName = binding.getMember().getTrait(JsonNameTrait.class)
91+
String locationName = member.getTrait(JsonNameTrait.class)
10392
.map(JsonNameTrait::getValue)
10493
.orElseGet(binding::getLocationName);
105-
Shape target = context.getModel().getShapeIndex().getShape(member.getTarget()).get();
94+
Shape target = context.getModel().expectShape(member.getTarget());
10695

10796
// Generate an if statement to set the bodyParam if the member is set.
10897
writer.openBlock("if (input.$L !== undefined) {", "}", memberName, () -> {
@@ -135,7 +124,7 @@ public void deserializeOutputDocument(
135124
SymbolProvider symbolProvider = context.getSymbolProvider();
136125

137126
for (HttpBinding binding : documentBindings) {
138-
Shape target = context.getModel().getShapeIndex().getShape(binding.getMember().getTarget()).get();
127+
Shape target = context.getModel().expectShape(binding.getMember().getTarget());
139128
// The name of the member to get from the input shape.
140129
String memberName = symbolProvider.toMemberName(binding.getMember());
141130
// Use the jsonName trait value if present, otherwise use the member name.

codegen/smithy-aws-typescript-codegen/src/test/java/software/amazon/smithy/aws/typescript/codegen/AwsServiceIdIntegrationTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public void testSomeLibraryMethod() {
2323
.discoverModels()
2424
.assemble()
2525
.unwrap();
26-
Shape service = model.getShapeIndex().getShape(ShapeId.from("smithy.example#OriginalName")).get();
26+
Shape service = model.expectShape((ShapeId.from("smithy.example#OriginalName")));
2727
AwsServiceIdIntegration integration = new AwsServiceIdIntegration();
2828
SymbolProvider provider = TypeScriptCodegenPlugin.createSymbolProvider(model);
2929
SymbolProvider decorated = integration.decorateSymbolProvider(

0 commit comments

Comments
 (0)