Skip to content

Commit b8255f4

Browse files
AllanZhengYPsrchase
authored andcommitted
support eventstream traits (smithy-lang#110)
This change adds codegen support for eventstream serde. If a member has event stream trait, it will generate specitial event serde functions on top of original ones: * add eventstream serde generator * generate special serde for events and event unions * complete event deserializer and event union deserializer * Add event stream and events unions serializer * formatting code and add necessary docs * add eventstream-serde-browser dependency * address PR feedbacks * use normal error deserializer to deserialize error event in event stream
1 parent 1345442 commit b8255f4

File tree

8 files changed

+585
-13
lines changed

8 files changed

+585
-13
lines changed

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/CodegenUtils.java

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@
1515

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

18+
import java.util.ArrayList;
19+
import java.util.List;
20+
21+
import software.amazon.smithy.model.Model;
22+
import software.amazon.smithy.model.knowledge.EventStreamIndex;
23+
import software.amazon.smithy.model.shapes.OperationShape;
24+
1825
/**
1926
* Utility methods needed across Java packages.
2027
*/
@@ -31,4 +38,58 @@ private CodegenUtils() {}
3138
public static boolean isJsonMediaType(String mediaType) {
3239
return mediaType.equals("application/json") || mediaType.endsWith("+json");
3340
}
41+
42+
/**
43+
* Get context type for command serializer functions.
44+
* @param writer The code writer.
45+
* @param model The model for the service containing the given command.
46+
* @param operation The operation shape for given command.
47+
* @return The TypeScript type for the serializer context
48+
*/
49+
public static String getOperationSerializerContextType(
50+
TypeScriptWriter writer,
51+
Model model,
52+
OperationShape operation
53+
) {
54+
// Get default SerdeContext.
55+
List<String> contextInterfaceList = getDefaultOperationSerdeContextTypes(writer);
56+
// If event stream trait exists, add corresponding serde context type to the intersection type.
57+
EventStreamIndex eventStreamIndex = model.getKnowledge(EventStreamIndex.class);
58+
if (eventStreamIndex.getInputInfo(operation).isPresent()) {
59+
writer.addImport("EventStreamSerdeContext", "__EventStreamSerdeContext", "@aws-sdk/types");
60+
contextInterfaceList.add("__EventStreamSerdeContext");
61+
}
62+
return String.join(" & ", contextInterfaceList);
63+
}
64+
65+
/**
66+
* Get context type for command deserializer function.
67+
* @param writer The code writer.
68+
* @param model The model for the service containing the given command.
69+
* @param operation The operation shape for given command.
70+
* @return The TypeScript type for the deserializer context
71+
*/
72+
public static String getOperationDeserializerContextType(
73+
TypeScriptWriter writer,
74+
Model model,
75+
OperationShape operation
76+
) {
77+
// Get default SerdeContext.
78+
List<String> contextInterfaceList = getDefaultOperationSerdeContextTypes(writer);
79+
// If event stream trait exists, add corresponding serde context type to the intersection type.
80+
EventStreamIndex eventStreamIndex = model.getKnowledge(EventStreamIndex.class);
81+
if (eventStreamIndex.getOutputInfo(operation).isPresent()) {
82+
writer.addImport("EventStreamSerdeContext", "__EventStreamSerdeContext", "@aws-sdk/types");
83+
contextInterfaceList.add("__EventStreamSerdeContext");
84+
}
85+
return String.join(" & ", contextInterfaceList);
86+
}
87+
88+
private static List<String> getDefaultOperationSerdeContextTypes(TypeScriptWriter writer) {
89+
List<String> contextInterfaceList = new ArrayList<>();
90+
// Get default SerdeContext.
91+
writer.addImport("SerdeContext", "__SerdeContext", "@aws-sdk/types");
92+
contextInterfaceList.add("__SerdeContext");
93+
return contextInterfaceList;
94+
}
3495
}

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/CommandGenerator.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ public void run() {
9393
writer.addImport("Handler", "Handler", "@aws-sdk/types");
9494
writer.addImport("HandlerExecutionContext", "HandlerExecutionContext", "@aws-sdk/types");
9595
writer.addImport("MiddlewareStack", "MiddlewareStack", "@aws-sdk/types");
96-
writer.addImport("SerdeContext", "SerdeContext", "@aws-sdk/types");
9796

9897
addInputAndOutputTypes();
9998

@@ -204,7 +203,7 @@ private void writeSerde() {
204203
.write("private serialize(")
205204
.indent()
206205
.write("input: $T,", inputType)
207-
.write("context: SerdeContext")
206+
.write("context: $L", CodegenUtils.getOperationSerializerContextType(writer, model, operation))
208207
.dedent()
209208
.openBlock(
210209
"): Promise<$T> {", "}",
@@ -216,7 +215,7 @@ private void writeSerde() {
216215
.write("private deserialize(")
217216
.indent()
218217
.write("output: $T,", applicationProtocol.getResponseType())
219-
.write("context: SerdeContext")
218+
.write("context: $L", CodegenUtils.getOperationDeserializerContextType(writer, model, operation))
220219
.dedent()
221220
.openBlock("): Promise<$T> {", "}", outputType, () -> writeSerdeDispatcher(false))
222221
.write("");

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/SymbolVisitor.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import software.amazon.smithy.model.shapes.ToShapeId;
5858
import software.amazon.smithy.model.shapes.UnionShape;
5959
import software.amazon.smithy.model.traits.EnumTrait;
60+
import software.amazon.smithy.model.traits.EventStreamTrait;
6061
import software.amazon.smithy.model.traits.MediaTypeTrait;
6162
import software.amazon.smithy.model.traits.StreamingTrait;
6263
import software.amazon.smithy.utils.StringUtils;
@@ -322,6 +323,10 @@ public Symbol memberShape(MemberShape shape) {
322323
return createMemberSymbolWithEnumTarget(targetSymbol);
323324
}
324325

326+
if (shape.hasTrait(EventStreamTrait.class)) {
327+
return createMemberSymbolWithEventStream(targetSymbol);
328+
}
329+
325330
return targetSymbol;
326331
}
327332

@@ -333,6 +338,14 @@ private Symbol createMemberSymbolWithEnumTarget(Symbol targetSymbol) {
333338
.build();
334339
}
335340

341+
private Symbol createMemberSymbolWithEventStream(Symbol targetSymbol) {
342+
return targetSymbol.toBuilder()
343+
.namespace(null, "/")
344+
.name(String.format("AsyncIterable<%s>", targetSymbol.getName()))
345+
.addReference(targetSymbol)
346+
.build();
347+
}
348+
336349
@Override
337350
public Symbol timestampShape(TimestampShape shape) {
338351
return createSymbolBuilder(shape, "Date").build();

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptDependency.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.ArrayList;
1919
import java.util.Collections;
2020
import java.util.List;
21+
2122
import software.amazon.smithy.codegen.core.Symbol;
2223
import software.amazon.smithy.codegen.core.SymbolDependency;
2324
import software.amazon.smithy.codegen.core.SymbolDependencyContainer;
@@ -67,6 +68,12 @@ public enum TypeScriptDependency implements SymbolDependencyContainer {
6768
AWS_SDK_FETCH_HTTP_HANDLER("dependencies", "@aws-sdk/fetch-http-handler", "^1.0.0-alpha.1", false),
6869
AWS_SDK_NODE_HTTP_HANDLER("dependencies", "@aws-sdk/node-http-handler", "^1.0.0-alpha.1", false),
6970

71+
// Conditionally added if a event stream shape is found anywhere in the model
72+
AWS_SDK_EVENTSTREAM_SERDE_CONFIG_RESOLVER("dependencies", "@aws-sdk/eventstream-serde-config-resolver",
73+
"^1.0.0-alpha.0", false),
74+
AWS_SDK_EVENTSTREAM_SERDE_NODE("dependencies", "@aws-sdk/eventstream-serde-node", "^1.0.0-alpha.0", false),
75+
AWS_SDK_EVENTSTREAM_SERDE_BROWSER("dependencies", "@aws-sdk/eventstream-serde-browser", "^1.0.0-alpha.0", false),
76+
7077
// Conditionally added if a big decimal shape is found in a model.
7178
BIG_JS("dependencies", "big.js", "^5.2.2", false),
7279
TYPES_BIG_JS("devDependencies", "@types/big.js", "^4.0.5", false);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
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.typescript.codegen.integration;
17+
18+
import java.util.List;
19+
import java.util.Set;
20+
21+
import software.amazon.smithy.codegen.core.SymbolProvider;
22+
import software.amazon.smithy.model.Model;
23+
import software.amazon.smithy.model.knowledge.EventStreamIndex;
24+
import software.amazon.smithy.model.knowledge.TopDownIndex;
25+
import software.amazon.smithy.model.shapes.OperationShape;
26+
import software.amazon.smithy.model.shapes.ServiceShape;
27+
import software.amazon.smithy.typescript.codegen.LanguageTarget;
28+
import software.amazon.smithy.typescript.codegen.TypeScriptDependency;
29+
import software.amazon.smithy.typescript.codegen.TypeScriptSettings;
30+
import software.amazon.smithy.typescript.codegen.TypeScriptWriter;
31+
import software.amazon.smithy.utils.ListUtils;
32+
33+
/**
34+
* Adds event streams if needed.
35+
*/
36+
public final class AddEventStreamDependency implements TypeScriptIntegration {
37+
38+
@Override
39+
public List<RuntimeClientPlugin> getClientPlugins() {
40+
return ListUtils.of(
41+
RuntimeClientPlugin.builder()
42+
.withConventions(TypeScriptDependency.AWS_SDK_EVENTSTREAM_SERDE_CONFIG_RESOLVER.dependency,
43+
"EventStreamSerde", RuntimeClientPlugin.Convention.HAS_CONFIG)
44+
.servicePredicate(AddEventStreamDependency::hasEventStream)
45+
.build()
46+
);
47+
}
48+
49+
@Override
50+
public void addConfigInterfaceFields(
51+
TypeScriptSettings settings,
52+
Model model,
53+
SymbolProvider symbolProvider,
54+
TypeScriptWriter writer
55+
) {
56+
if (!hasEventStream(model, settings.getService(model))) {
57+
return;
58+
}
59+
60+
writer.addDependency(TypeScriptDependency.AWS_SDK_EVENTSTREAM_SERDE_CONFIG_RESOLVER);
61+
writer.addImport("EventStreamSerdeProvider", "__EventStreamSerdeProvider",
62+
TypeScriptDependency.AWS_SDK_TYPES.packageName);
63+
writer.writeDocs("The function that provides necessary utilities for generating and signing event stream");
64+
writer.write("eventStreamSerdeProvider?: __EventStreamSerdeProvider;\n");
65+
}
66+
67+
@Override
68+
public void addRuntimeConfigValues(
69+
TypeScriptSettings settings,
70+
Model model,
71+
SymbolProvider symbolProvider,
72+
TypeScriptWriter writer,
73+
LanguageTarget target
74+
) {
75+
if (!hasEventStream(model, settings.getService(model))) {
76+
return;
77+
}
78+
79+
switch (target) {
80+
case NODE:
81+
writer.addDependency(TypeScriptDependency.AWS_SDK_EVENTSTREAM_SERDE_NODE);
82+
writer.addImport("eventStreamSerdeProvider", "eventStreamSerdeProvider",
83+
TypeScriptDependency.AWS_SDK_EVENTSTREAM_SERDE_NODE.packageName);
84+
writer.write("eventStreamSerdeProvider");
85+
break;
86+
case BROWSER:
87+
writer.addDependency(TypeScriptDependency.AWS_SDK_EVENTSTREAM_SERDE_BROWSER);
88+
writer.addImport("eventStreamSerdeProvider", "eventStreamSerdeProvider",
89+
TypeScriptDependency.AWS_SDK_EVENTSTREAM_SERDE_BROWSER.packageName);
90+
writer.write("eventStreamSerdeProvider");
91+
break;
92+
case REACT_NATIVE:
93+
// TODO: add ReactNative eventstream support
94+
writer.addDependency(TypeScriptDependency.INVALID_DEPENDENCY);
95+
writer.addImport("invalidFunction", "invalidFunction",
96+
TypeScriptDependency.INVALID_DEPENDENCY.packageName);
97+
writer.write("eventStreamSerdeProvider: invalidFunction(\"event stream is not supported in "
98+
+ "ReactNative\") as any,");
99+
break;
100+
default:
101+
// do nothing
102+
}
103+
}
104+
105+
private static boolean hasEventStream(
106+
Model model,
107+
ServiceShape service
108+
) {
109+
TopDownIndex topDownIndex = model.getKnowledge(TopDownIndex.class);
110+
Set<OperationShape> operations = topDownIndex.getContainedOperations(service);
111+
EventStreamIndex eventStreamIndex = model.getKnowledge(EventStreamIndex.class);
112+
for (OperationShape operation : operations) {
113+
if (eventStreamIndex.getInputInfo(operation).isPresent()
114+
|| eventStreamIndex.getOutputInfo(operation).isPresent()
115+
) {
116+
return true;
117+
}
118+
}
119+
return false;
120+
}
121+
}

0 commit comments

Comments
 (0)