54
54
import software .amazon .smithy .model .shapes .UnionShape ;
55
55
import software .amazon .smithy .model .traits .EndpointTrait ;
56
56
import software .amazon .smithy .model .traits .ErrorTrait ;
57
+ import software .amazon .smithy .model .traits .EventHeaderTrait ;
58
+ import software .amazon .smithy .model .traits .EventPayloadTrait ;
57
59
import software .amazon .smithy .model .traits .EventStreamTrait ;
58
60
import software .amazon .smithy .model .traits .HttpTrait ;
59
61
import software .amazon .smithy .model .traits .StreamingTrait ;
@@ -74,8 +76,8 @@ public abstract class HttpBindingProtocolGenerator implements ProtocolGenerator
74
76
private final Set <Shape > serializingDocumentShapes = new TreeSet <>();
75
77
private final Set <Shape > deserializingDocumentShapes = new TreeSet <>();
76
78
private final Set <StructureShape > deserializingErrorShapes = new TreeSet <>();
77
- private final Set <Shape > serializeEventShapes = new TreeSet <>();
78
- private final Set <Shape > deserializingEventShapes = new TreeSet <>();
79
+ private final Set <StructureShape > serializeEventShapes = new TreeSet <>();
80
+ private final Set <StructureShape > deserializingEventShapes = new TreeSet <>();
79
81
private final Set <UnionShape > serializeEventUnions = new TreeSet <>();
80
82
private final Set <UnionShape > deserializeEventUnions = new TreeSet <>();
81
83
private final boolean isErrorCodeInBody ;
@@ -134,12 +136,14 @@ public ApplicationProtocol getApplicationProtocol() {
134
136
@ Override
135
137
public void generateSharedComponents (GenerationContext context ) {
136
138
deserializeEventUnions .forEach (eventUnion -> {
137
- Set <StructureShape > events = HttpProtocolGeneratorUtils .generateSerializingEventUnion (context , eventUnion );
139
+ Set <StructureShape > events =
140
+ HttpProtocolGeneratorUtils .generateDeserializingEventUnion (context , eventUnion );
138
141
deserializingEventShapes .addAll (events );
139
142
});
140
143
serializeEventUnions .forEach (eventUnion -> {
141
144
142
145
});
146
+ deserializingEventShapes .forEach (event -> generateEventDeserializer (context , event ));
143
147
144
148
deserializingErrorShapes .forEach (error -> generateErrorDeserializer (context , error ));
145
149
generateDocumentBodyShapeSerializers (context , serializingDocumentShapes );
@@ -618,7 +622,7 @@ protected void serializeInputPayload(
618
622
private void collectSerializingEventShapes (GenerationContext context , Shape target ) {
619
623
if (target instanceof StructureShape ) {
620
624
// Single-event event stream
621
- this .serializeEventShapes .add (target );
625
+ this .serializeEventShapes .add (target . asStructureShape (). get () );
622
626
} else if (target instanceof UnionShape ) {
623
627
// Multi-event event stream. Save the dispatcher
624
628
this .serializeEventUnions .add (target .asUnionShape ().get ());
@@ -844,7 +848,8 @@ private List<HttpBinding> readResponsePayload(
844
848
eventStreamDeserializerWrapper (context , binding .getMember (), target );
845
849
writer .write ("" ).dedent ();
846
850
writer .write ("contents.$L = data;" , binding .getMemberName ());
847
- return payloadBindings ;
851
+ //Not to generate non-eventstream payload shape again
852
+ return ListUtils .of ();
848
853
} else if (hasStreamingComponent ) {
849
854
// If payload is streaming, return raw low-level stream directly.
850
855
writer .write ("const data: any = output.body;" );
@@ -890,7 +895,7 @@ private void eventStreamDeserializerWrapper(GenerationContext context, MemberSha
890
895
});
891
896
if (target instanceof StructureShape ) {
892
897
// Single-event event stream
893
- this .deserializingEventShapes .add (target );
898
+ this .deserializingEventShapes .add (target . asStructureShape (). get () );
894
899
writer .write ("return $L;" ,
895
900
getOutputValue (context , Location .PAYLOAD , "eventMessage" , member , target ));
896
901
} else if (target instanceof UnionShape ) {
@@ -899,12 +904,90 @@ private void eventStreamDeserializerWrapper(GenerationContext context, MemberSha
899
904
Symbol symbol = context .getSymbolProvider ().toSymbol (target );
900
905
String deserFunction = ProtocolGenerator .getDeserFunctionName (symbol , context .getProtocolName ())
901
906
+ "_event(parsedEvent, context)" ;
902
- writer .write ("return $L;" , deserFunction );
907
+ writer .write ("return await $L;" , deserFunction );
903
908
}
904
909
});
905
910
});
906
911
}
907
912
913
+ private void generateEventDeserializer (
914
+ GenerationContext context ,
915
+ StructureShape event
916
+ ) {
917
+ TypeScriptWriter writer = context .getWriter ();
918
+ SymbolProvider symbolProvider = context .getSymbolProvider ();
919
+ Symbol symbol = symbolProvider .toSymbol (event );
920
+ String methodName = ProtocolGenerator .getDeserFunctionName (symbol , context .getProtocolName ()) + "_event" ;
921
+ Model model = context .getModel ();
922
+ // Handle the general response.
923
+ writer .openBlock ("const $L = async (\n "
924
+ + " output: any,\n "
925
+ + " context: __SerdeContext\n "
926
+ + "): Promise<$T> => {" , "}" ,
927
+ methodName ,
928
+ symbol , () -> {
929
+ writer .openBlock ("let contents: $L = {" , "} as any;" , symbol .getName (), () -> {
930
+ if (!event .getAllMembers ().values ().isEmpty ()) {
931
+ writer .write ("__type: $S," , event .getId ().getName ());
932
+ }
933
+ });
934
+ List <MemberShape > payloadMembers = event .getAllMembers ().values ().stream ()
935
+ .filter (member -> member .hasTrait (EventPayloadTrait .class )).collect (Collectors .toList ());
936
+ List <MemberShape > headerMembers = event .getAllMembers ().values ().stream ()
937
+ .filter (member -> member .hasTrait (EventHeaderTrait .class )).collect (Collectors .toList ());
938
+ List <MemberShape > documentMembers = event .getAllMembers ().values ().stream ()
939
+ .filter (member -> !member .hasTrait (EventHeaderTrait .class )
940
+ && !member .hasTrait (EventPayloadTrait .class ))
941
+ .collect (Collectors .toList ());
942
+ if (!payloadMembers .isEmpty ()) {
943
+ //if EventPayload trait exists, all other traits are EventHeader
944
+ headerMembers .addAll (documentMembers );
945
+ documentMembers = ListUtils .of ();
946
+ }
947
+ for (MemberShape headerMember : headerMembers ) {
948
+ String memberName = headerMember .getMemberName ();
949
+ writer .openBlock ("if (output.headers[$S] !== undefined) {" , "}" , memberName , () -> {
950
+ Shape target = model .expectShape (headerMember .getTarget ());
951
+ String headerValue = getOutputValue (context , Location .HEADER ,
952
+ "output.headers['" + memberName + "']" , headerMember , target );
953
+ writer .write ("contents.$L = $L;" , memberName , headerValue );
954
+ });
955
+ }
956
+ if (!documentMembers .isEmpty ()) {
957
+ // If response has document binding, the body can be parsed to JavaScript object.
958
+ writer .write ("const data: any = await parseBody(output.body, context);" );
959
+ // Deser the event document with the original shape deser function
960
+ writer .openBlock ("contents = {" , "} as any" , () -> {
961
+ writer .write ("...contents," );
962
+ writer .write ("...$L(data, context)" ,
963
+ ProtocolGenerator .getDeserFunctionName (symbol , context .getProtocolName ()));
964
+ });
965
+ //need original structure shape deserializer to deserialize event body.
966
+ deserializingDocumentShapes .add (event );
967
+ }
968
+ if (!payloadMembers .isEmpty ()) {
969
+ //There's only one event payload member
970
+ MemberShape payloadMember = payloadMembers .get (0 );
971
+ Shape payloadTarget = model .expectShape (payloadMember .getTarget ());
972
+ if (payloadTarget instanceof BlobShape ) {
973
+ // If event payload is a blob, only need to collect stream to binary data(Uint8Array).
974
+ writer .write ("const data: any = output.body" );
975
+ } else if (payloadTarget instanceof StructureShape || payloadTarget instanceof UnionShape ) {
976
+ // If body is Structure or Union, they we need to parse the string into JavaScript object.
977
+ writer .write ("const data: any = await parseBody(output.body, context);" );
978
+ } else if (payloadTarget instanceof StringShape ) {
979
+ // If payload is string, we need to collect body and encode binary to string.
980
+ writer .write ("const data: any = await collectBodyString(output.body, context);" );
981
+ } else {
982
+ throw new CodegenException (String .format ("Unexpected shape type bound to payload: `%s`" ,
983
+ payloadTarget .getType ()));
984
+ }
985
+ writer .write ("contents.$L = data" , payloadMember .getMemberName ());
986
+ }
987
+ writer .write ("return contents;" );
988
+ });
989
+ }
990
+
908
991
/**
909
992
* Given context and a source of data, generate an output value provider for the
910
993
* shape. This may use native types (like generating a Date for timestamps,)
0 commit comments