Skip to content

Commit 0dbd1d3

Browse files
committed
fix when response payload is not structured string
1 parent 0fa51ff commit 0dbd1d3

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
@@ -119,6 +119,8 @@ public void generateSharedComponents(GenerationContext context) {
119119
generateDocumentBodyShapeSerializers(context, serializingDocumentShapes);
120120
generateDocumentBodyShapeDeserializers(context, deserializingDocumentShapes);
121121
HttpProtocolGeneratorUtils.generateMetadataDeserializer(context, getApplicationProtocol().getResponseType());
122+
HttpProtocolGeneratorUtils.generateCollectBody(context);
123+
HttpProtocolGeneratorUtils.generateCollectBodyString(context);
122124
}
123125

124126
/**
@@ -689,43 +691,44 @@ private List<HttpBinding> readResponseBody(
689691
List<HttpBinding> documentBindings = bindingIndex.getResponseBindings(operationOrError, Location.DOCUMENT);
690692
documentBindings.sort(Comparator.comparing(HttpBinding::getMemberName));
691693
List<HttpBinding> payloadBindings = bindingIndex.getResponseBindings(operationOrError, Location.PAYLOAD);
694+
OperationIndex operationIndex = context.getModel().getKnowledge(OperationIndex.class);
695+
StructureShape operationOutputOrError = operationOrError.asStructureShape()
696+
.orElseGet(() -> operationIndex.getOutput(operationOrError).orElse(null));
697+
boolean hasStreamingComponent = Optional.ofNullable(operationOutputOrError)
698+
.map(structure -> structure.getAllMembers().values().stream()
699+
.anyMatch(memberShape -> memberShape.hasTrait(StreamingTrait.class)))
700+
.orElse(false);
692701

693702
if (!documentBindings.isEmpty()) {
694-
readReponseBodyData(context, operationOrError);
703+
writer.write("const data: any = await parseBody(output.body, context);");
695704
deserializeOutputDocument(context, operationOrError, documentBindings);
696705
return documentBindings;
697706
}
698707
if (!payloadBindings.isEmpty()) {
699-
readReponseBodyData(context, operationOrError);
700708
// There can only be one payload binding.
701709
HttpBinding binding = payloadBindings.get(0);
702710
Shape target = context.getModel().expectShape(binding.getMember().getTarget());
711+
if (hasStreamingComponent) {
712+
// If payload is streaming, return raw low-level stream directly.
713+
writer.write("const data: any = output.body;");
714+
} else if (target instanceof BlobShape) {
715+
// If payload is blob, only need to collect stream to binary data(Uint8Array).
716+
writer.write("const data: any = await collectBody(output.body, context);");
717+
} else if (target instanceof CollectionShape || target instanceof StructureShape) {
718+
// If body is Collection or Structure, they we need to parse the string into JavaScript object.
719+
writer.write("const data: any = await parseBody(output.body, context);");
720+
} else {
721+
// If payload is other scalar types(not Collection or Structure), because payload will be values in
722+
// string instead of valid JSON or XML. So we need to collect body and encode binary to string.
723+
writer.write("const data: any = await collectBodyString(output.body, context);");
724+
}
703725
writer.write("contents.$L = $L;", binding.getMemberName(), getOutputValue(context,
704726
Location.PAYLOAD, "data", binding.getMember(), target));
705727
return payloadBindings;
706728
}
707729
return ListUtils.of();
708730
}
709731

710-
private void readReponseBodyData(GenerationContext context, Shape operationOrError) {
711-
TypeScriptWriter writer = context.getWriter();
712-
// Prepare response body for deserializing.
713-
OperationIndex operationIndex = context.getModel().getKnowledge(OperationIndex.class);
714-
StructureShape operationOutputOrError = operationOrError.asStructureShape()
715-
.orElseGet(() -> operationIndex.getOutput(operationOrError).orElse(null));
716-
boolean hasStreamingComponent = Optional.ofNullable(operationOutputOrError)
717-
.map(structure -> structure.getAllMembers().values().stream()
718-
.anyMatch(memberShape -> memberShape.hasTrait(StreamingTrait.class)))
719-
.orElse(false);
720-
if (hasStreamingComponent) {
721-
// For operations with streaming output or errors with streaming body we keep the body intact.
722-
writer.write("const data: any = output.body;");
723-
} else {
724-
// Otherwise, we collect the response body to structured object with parseBody().
725-
writer.write("const data: any = await parseBody(output.body, context);");
726-
}
727-
}
728-
729732
/**
730733
* Given context and a source of data, generate an output value provider for the
731734
* 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)