Skip to content

Commit 2fa77b5

Browse files
kstichAllanZhengYP
authored andcommitted
Add support for AWS JSON RPC protocols (aws#475)
This commit adds support for the AWS JSON RPC protocols, building on top of the `HttpRpcProtocolGenerator` via the `JsonRpcProtocolGenerator` abstract class. Specific implementations are provided for `aws.json-1.0` and `aws.json-1.1`. Shared code between the JSON-based `ProtocolGenerator` implementations has been moved to a utils class, specific implementations have been updated. Insubstantial tweaks and documentation updates have also been made for clarity.
1 parent 5442275 commit 2fa77b5

File tree

10 files changed

+255
-27
lines changed

10 files changed

+255
-27
lines changed

codegen/config/checkstyle/suppressions.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@
1919
"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
2020
<suppressions>
2121
<suppress checks="TypeName"
22-
files="AwsRestJson1_1.java|AwsRestJson1_0.java"/>
22+
files="/Aws.*\d_\d.java$"/>
2323
</suppressions>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public List<RuntimeClientPlugin> getClientPlugins() {
5656
RuntimeClientPlugin.builder()
5757
.withConventions("@aws-sdk/middleware-sdk-api-gateway", "^0.1.0-preview.5",
5858
"AcceptsHeader", HAS_MIDDLEWARE)
59-
.servicePredicate((m,s) -> s.getId().getName().equals("BackplaneControlService"))
59+
.servicePredicate((m, s) -> s.getId().getName().equals("BackplaneControlService"))
6060
.build()
6161
);
6262
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public class AddProtocols implements TypeScriptIntegration {
2727

2828
@Override
2929
public List<ProtocolGenerator> getProtocolGenerators() {
30-
return ListUtils.of(new AwsRestJson1_0(), new AwsRestJson1_1());
30+
return ListUtils.of(new AwsRestJson1_0(), new AwsRestJson1_1(),
31+
new AwsJsonRpc1_0(), new AwsJsonRpc1_1());
3132
}
3233
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.smithy.aws.typescript.codegen;
17+
18+
/**
19+
* Handles generating the aws.json-1.0 protocol for services.
20+
*
21+
* @inheritDoc
22+
*
23+
* @see JsonRpcProtocolGenerator
24+
*/
25+
public class AwsJsonRpc1_0 extends JsonRpcProtocolGenerator {
26+
27+
@Override
28+
protected String getDocumentContentType() {
29+
return "application/x-amz-json-1.0";
30+
}
31+
32+
@Override
33+
public String getName() {
34+
return "aws.json-1.0";
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.smithy.aws.typescript.codegen;
17+
18+
/**
19+
* Handles generating the aws.json-1.1 protocol for services.
20+
*
21+
* @inheritDoc
22+
*
23+
* @see JsonRpcProtocolGenerator
24+
*/
25+
public class AwsJsonRpc1_1 extends JsonRpcProtocolGenerator {
26+
27+
@Override
28+
protected String getDocumentContentType() {
29+
return "application/x-amz-json-1.1";
30+
}
31+
32+
@Override
33+
public String getName() {
34+
return "aws.json-1.1";
35+
}
36+
}

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@
2525
public final class AwsRestJson1_0 extends RestJsonProtocolGenerator {
2626

2727
@Override
28-
public String getName() {
29-
return "aws.rest-json-1.0";
28+
protected String getDocumentContentType() {
29+
return "application/x-amz-json-1.0";
3030
}
3131

3232
@Override
33-
protected String getDocumentContentType() {
34-
return "application/x-amz-json-1.0";
33+
public String getName() {
34+
return "aws.rest-json-1.0";
3535
}
3636
}

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@
2525
public final class AwsRestJson1_1 extends RestJsonProtocolGenerator {
2626

2727
@Override
28-
public String getName() {
29-
return "aws.rest-json-1.1";
28+
protected String getDocumentContentType() {
29+
return "application/x-amz-json-1.1";
3030
}
3131

3232
@Override
33-
protected String getDocumentContentType() {
34-
return "application/x-amz-json-1.1";
33+
public String getName() {
34+
return "aws.rest-json-1.1";
3535
}
3636
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.smithy.aws.typescript.codegen;
17+
18+
import software.amazon.smithy.typescript.codegen.TypeScriptWriter;
19+
import software.amazon.smithy.typescript.codegen.integration.ProtocolGenerator.GenerationContext;
20+
21+
/**
22+
* Utility methods for generating JSON based protocols.
23+
*/
24+
final class JsonProtocolUtils {
25+
26+
private JsonProtocolUtils() {}
27+
28+
/**
29+
* Writes a response body parser function for JSON protocols. This
30+
* will parse a present body after converting it to utf-8.
31+
*
32+
* @param context The generation context.
33+
*/
34+
static void generateParseBody(GenerationContext context) {
35+
TypeScriptWriter writer = context.getWriter();
36+
37+
// Include a JSON body parser used to deserialize documents from HTTP responses.
38+
writer.addImport("SerdeContext", "SerdeContext", "@aws-sdk/types");
39+
writer.openBlock("const parseBody = (streamBody: any, context: SerdeContext): any => {", "};", () -> {
40+
writer.openBlock("return context.streamCollector(streamBody).then((body: any) => {", "});", () -> {
41+
writer.write("const encoded = context.utf8Encoder(body);");
42+
writer.openBlock("if (encoded.length) {", "}", () -> {
43+
writer.write("return JSON.parse(encoded);");
44+
});
45+
writer.write("return {};");
46+
});
47+
});
48+
49+
writer.write("");
50+
}
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.smithy.aws.typescript.codegen;
17+
18+
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;
22+
import software.amazon.smithy.model.shapes.OperationShape;
23+
import software.amazon.smithy.model.shapes.Shape;
24+
import software.amazon.smithy.model.shapes.ShapeVisitor;
25+
import software.amazon.smithy.model.shapes.StructureShape;
26+
import software.amazon.smithy.model.traits.TimestampFormatTrait.Format;
27+
import software.amazon.smithy.typescript.codegen.TypeScriptWriter;
28+
import software.amazon.smithy.typescript.codegen.integration.DocumentMemberDeserVisitor;
29+
import software.amazon.smithy.typescript.codegen.integration.DocumentMemberSerVisitor;
30+
import software.amazon.smithy.typescript.codegen.integration.HttpRpcProtocolGenerator;
31+
32+
/**
33+
* Handles general components across the AWS JSON protocols that have do not have
34+
* HTTP bindings. It handles reading and writing from document bodies, including
35+
* generating any functions needed for performing serde.
36+
*
37+
* This builds on the foundations of the {@link HttpRpcProtocolGenerator} to handle
38+
* standard components of the HTTP requests and responses.
39+
*
40+
* @see JsonShapeSerVisitor
41+
* @see JsonShapeDeserVisitor
42+
* @see JsonMemberSerVisitor
43+
* @see JsonMemberDeserVisitor
44+
*/
45+
abstract class JsonRpcProtocolGenerator extends HttpRpcProtocolGenerator {
46+
47+
protected Format getDocumentTimestampFormat() {
48+
return Format.EPOCH_SECONDS;
49+
}
50+
51+
@Override
52+
protected void generateDocumentShapeSerializers(GenerationContext context, Set<Shape> shapes) {
53+
generateDocumentShapeSerde(context, shapes, new JsonShapeSerVisitor(context));
54+
}
55+
56+
@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));
67+
}
68+
69+
@Override
70+
public void generateSharedComponents(GenerationContext context) {
71+
super.generateSharedComponents(context);
72+
JsonProtocolUtils.generateParseBody(context);
73+
}
74+
75+
@Override
76+
protected void serializeInputDocument(
77+
GenerationContext context,
78+
OperationShape operation,
79+
StructureShape inputStructure
80+
) {
81+
TypeScriptWriter writer = context.getWriter();
82+
83+
// Input documents are wrapped in an input shape named wrapper, build that.
84+
writer.openBlock("let wrappedBody: any = {", "};", () -> {
85+
writer.write("$L: $L,", inputStructure.getId().getName(),
86+
inputStructure.accept(getMemberSerVisitor(context, "input")));
87+
});
88+
writer.write("body = JSON.stringify(wrappedBody);");
89+
}
90+
91+
private DocumentMemberSerVisitor getMemberSerVisitor(GenerationContext context, String dataSource) {
92+
return new JsonMemberSerVisitor(context, dataSource, getDocumentTimestampFormat());
93+
}
94+
95+
@Override
96+
protected void writeErrorCodeParser(GenerationContext context) {
97+
TypeScriptWriter writer = context.getWriter();
98+
99+
writer.write("let errorTypeParts: String = data[\"__type\"].split('#');");
100+
writer.write("errorCode = (errorTypeParts[1] === undefined) ? errorTypeParts[0] : errorTypeParts[1];");
101+
}
102+
103+
@Override
104+
protected void deserializeOutputDocument(
105+
GenerationContext context,
106+
OperationShape operation,
107+
StructureShape outputStructure
108+
) {
109+
TypeScriptWriter writer = context.getWriter();
110+
111+
writer.write("contents = $L;",
112+
outputStructure.accept(getMemberDeserVisitor(context, "data." + outputStructure.getId().getName())));
113+
}
114+
115+
private DocumentMemberDeserVisitor getMemberDeserVisitor(GenerationContext context, String dataSource) {
116+
return new JsonMemberDeserVisitor(context, dataSource, getDocumentTimestampFormat());
117+
}
118+
}

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

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
* @see JsonShapeSerVisitor
4545
* @see JsonShapeDeserVisitor
4646
* @see JsonMemberSerVisitor
47-
* @see DocumentMemberDeserVisitor
47+
* @see JsonMemberDeserVisitor
4848
* @see <a href="https://awslabs.github.io/smithy/spec/http.html">Smithy HTTP protocol bindings.</a>
4949
*/
5050
abstract class RestJsonProtocolGenerator extends HttpBindingProtocolGenerator {
@@ -75,21 +75,7 @@ private void generateDocumentShapeSerde(GenerationContext context, Set<Shape> sh
7575
@Override
7676
public void generateSharedComponents(GenerationContext context) {
7777
super.generateSharedComponents(context);
78-
TypeScriptWriter writer = context.getWriter();
79-
80-
// Include a JSON body parser used to deserialize documents from HTTP responses.
81-
writer.addImport("SerdeContext", "SerdeContext", "@aws-sdk/types");
82-
writer.openBlock("const parseBody = (streamBody: any, context: SerdeContext): any => {", "};", () -> {
83-
writer.openBlock("return context.streamCollector(streamBody).then((body: any) => {", "});", () -> {
84-
writer.write("const encoded = context.utf8Encoder(body);");
85-
writer.openBlock("if (encoded.length) {", "}", () -> {
86-
writer.write("return JSON.parse(encoded);");
87-
});
88-
writer.write("return {};");
89-
});
90-
});
91-
92-
writer.write("");
78+
JsonProtocolUtils.generateParseBody(context);
9379
}
9480

9581
@Override

0 commit comments

Comments
 (0)