Skip to content

Commit 1ea876c

Browse files
committed
fix when response payload is not structured string
1 parent 15133ec commit 1ea876c

File tree

2 files changed

+48
-21
lines changed

2 files changed

+48
-21
lines changed

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/integration/HttpBindingProtocolGenerator.java

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ public void generateSharedComponents(GenerationContext context) {
116116
generateDocumentBodyShapeSerializers(context, serializingDocumentShapes);
117117
generateDocumentBodyShapeDeserializers(context, deserializingDocumentShapes);
118118
HttpProtocolGeneratorUtils.generateMetadataDeserializer(context, getApplicationProtocol().getResponseType());
119+
HttpProtocolGeneratorUtils.generateCollectBody(context);
120+
HttpProtocolGeneratorUtils.generateCollectBodyString(context);
119121
}
120122

121123
/**
@@ -675,43 +677,44 @@ private List<HttpBinding> readResponseBody(
675677
List<HttpBinding> documentBindings = bindingIndex.getResponseBindings(operationOrError, Location.DOCUMENT);
676678
documentBindings.sort(Comparator.comparing(HttpBinding::getMemberName));
677679
List<HttpBinding> payloadBindings = bindingIndex.getResponseBindings(operationOrError, Location.PAYLOAD);
680+
OperationIndex operationIndex = context.getModel().getKnowledge(OperationIndex.class);
681+
StructureShape operationOutputOrError = operationOrError.asStructureShape()
682+
.orElseGet(() -> operationIndex.getOutput(operationOrError).orElse(null));
683+
boolean hasStreamingComponent = Optional.ofNullable(operationOutputOrError)
684+
.map(structure -> structure.getAllMembers().values().stream()
685+
.anyMatch(memberShape -> memberShape.hasTrait(StreamingTrait.class)))
686+
.orElse(false);
678687

679688
if (!documentBindings.isEmpty()) {
680-
readReponseBodyData(context, operationOrError);
689+
writer.write("const data: any = await parseBody(output.body, context);");
681690
deserializeOutputDocument(context, operationOrError, documentBindings);
682691
return documentBindings;
683692
}
684693
if (!payloadBindings.isEmpty()) {
685-
readReponseBodyData(context, operationOrError);
686694
// There can only be one payload binding.
687695
HttpBinding binding = payloadBindings.get(0);
688696
Shape target = context.getModel().expectShape(binding.getMember().getTarget());
697+
if (hasStreamingComponent) {
698+
// If payload is streaming, return raw low-level stream directly.
699+
writer.write("const data: any = output.body;");
700+
} else if (target instanceof BlobShape) {
701+
// If payload is blob, only need to collect stream to binary data(Uint8Array).
702+
writer.write("const data: any = await collectBody(output.body, context);");
703+
} else if (target instanceof CollectionShape || target instanceof StructureShape) {
704+
// If body is Collection or Structure, they we need to parse the string into JavaScript object.
705+
writer.write("const data: any = await parseBody(output.body, context);");
706+
} else {
707+
// If payload is other scalar types(not Collection or Structure), because payload will be values in
708+
// string instead of valid JSON or XML. So we need to collect body and encode binary to string.
709+
writer.write("const data: any = await collectBodyString(output.body, context);");
710+
}
689711
writer.write("contents.$L = $L;", binding.getMemberName(), getOutputValue(context,
690712
Location.PAYLOAD, "data", binding.getMember(), target));
691713
return payloadBindings;
692714
}
693715
return ListUtils.of();
694716
}
695717

696-
private void readReponseBodyData(GenerationContext context, Shape operationOrError) {
697-
TypeScriptWriter writer = context.getWriter();
698-
// Prepare response body for deserializing.
699-
OperationIndex operationIndex = context.getModel().getKnowledge(OperationIndex.class);
700-
StructureShape operationOutputOrError = operationOrError.asStructureShape()
701-
.orElseGet(() -> operationIndex.getOutput(operationOrError).orElse(null));
702-
boolean hasStreamingComponent = Optional.ofNullable(operationOutputOrError)
703-
.map(structure -> structure.getAllMembers().values().stream()
704-
.anyMatch(memberShape -> memberShape.hasTrait(StreamingTrait.class)))
705-
.orElse(false);
706-
if (hasStreamingComponent) {
707-
// For operations with streaming output or errors with streaming body we keep the body intact.
708-
writer.write("const data: any = output.body;");
709-
} else {
710-
// Otherwise, we collect the response body to structured object with parseBody().
711-
writer.write("const data: any = await parseBody(output.body, context);");
712-
}
713-
}
714-
715718
/**
716719
* Given context and a source of data, generate an output value provider for the
717720
* shape. This may use native types (like generating a Date for timestamps,)

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/integration/HttpProtocolGeneratorUtils.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,30 @@ static void generateMetadataDeserializer(GenerationContext context, SymbolRefere
108108
writer.write("");
109109
}
110110

111+
static void generateCollectBody(GenerationContext context) {
112+
TypeScriptWriter writer = context.getWriter();
113+
114+
writer.addImport("SerdeContext", "__SerdeContext", "@aws-sdk/types");
115+
writer.openBlock("const collectBody = (streamBody: any, context: __SerdeContext): Promise<Uint8Array> => {",
116+
"};", () -> {
117+
writer.write("return context.streamCollector(streamBody) || new Uint8Array();");
118+
});
119+
120+
writer.write("");
121+
}
122+
123+
static void generateCollectBodyString(GenerationContext context) {
124+
TypeScriptWriter writer = context.getWriter();
125+
126+
writer.addImport("SerdeContext", "__SerdeContext", "@aws-sdk/types");
127+
writer.openBlock("const collectBodyString = (streamBody: any, context: __SerdeContext): Promise<string> => {",
128+
"};", () -> {
129+
writer.write("return collectBody(streamBody, context).then(body => context.utf8Encoder(body));");
130+
});
131+
132+
writer.write("");
133+
}
134+
111135
/**
112136
* Writes a function used to dispatch to the proper error deserializer
113137
* for each error that the operation can return. The generated function

0 commit comments

Comments
 (0)