in codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/HttpBindingProtocolGenerator.java [369:482]
protected abstract void writeOperationSerializerMiddlewareEventStreamSetup(
GenerationContext context,
EventStreamInfo eventStreamInfo
);
// Generates operation deserializer middleware that delegates to appropriate deserializers for the error,
// output shapes for the operation.
private void generateOperationDeserializerMiddleware(GenerationContext context, OperationShape operation) {
SymbolProvider symbolProvider = context.getSymbolProvider();
Model model = context.getModel();
ServiceShape service = context.getService();
ApplicationProtocol applicationProtocol = getApplicationProtocol();
Symbol responseType = applicationProtocol.getResponseType();
GoWriter goWriter = context.getWriter().get();
GoStackStepMiddlewareGenerator middleware = GoStackStepMiddlewareGenerator.createDeserializeStepMiddleware(
ProtocolGenerator.getDeserializeMiddlewareName(operation.getId(), service, getProtocolName()),
ProtocolUtils.OPERATION_DESERIALIZER_MIDDLEWARE_ID);
String errorFunctionName = ProtocolGenerator.getOperationErrorDeserFunctionName(
operation, service, context.getProtocolName());
middleware.writeMiddleware(goWriter, (generator, writer) -> {
writer.addUseImports(SmithyGoDependency.FMT);
writer.write("out, metadata, err = next.$L(ctx, in)", generator.getHandleMethodName());
writer.write("if err != nil { return out, metadata, err }");
writer.write("");
writer.write(goTemplate("""
_, span := $T(ctx, "OperationDeserializer")
endTimer := startMetricTimer(ctx, "client.call.deserialization_duration")
defer endTimer()
defer span.End()
""", SMITHY_TRACING.func("StartSpan")));
writer.write("response, ok := out.RawResponse.($P)", responseType);
writer.openBlock("if !ok {", "}", () -> {
writer.addUseImports(SmithyGoDependency.SMITHY);
writer.write(String.format("return out, metadata, &smithy.DeserializationError{Err: %s}",
"fmt.Errorf(\"unknown transport type %T\", out.RawResponse)"));
});
writer.write("");
writer.openBlock("if response.StatusCode < 200 || response.StatusCode >= 300 {", "}", () -> {
writer.write("return out, metadata, $L(response, &metadata)", errorFunctionName);
});
Shape outputShape = model.expectShape(operation.getOutput()
.orElseThrow(() -> new CodegenException("expect output shape for operation: " + operation.getId()))
);
Symbol outputSymbol = symbolProvider.toSymbol(outputShape);
// initialize out.Result as output structure shape
writer.write("output := &$T{}", outputSymbol);
writer.write("out.Result = output");
writer.write("");
// Output shape HTTP binding middleware generation
if (isShapeWithRestResponseBindings(model, operation)) {
String deserFuncName = ProtocolGenerator.getOperationHttpBindingsDeserFunctionName(
outputShape, service, getProtocolName());
writer.write("err= $L(output, response)", deserFuncName);
writer.openBlock("if err != nil {", "}", () -> {
writer.addUseImports(SmithyGoDependency.SMITHY);
writer.write(String.format("return out, metadata, &smithy.DeserializationError{Err: %s}",
"fmt.Errorf(\"failed to decode response with invalid Http bindings, %w\", err)"));
});
writer.write("");
}
Optional<EventStreamInfo> streamInfoOptional = EventStreamIndex.of(model).getOutputInfo(operation);
// Discard without deserializing the response if the input shape is a stubbed synthetic clone
// without an archetype.
if (CodegenUtils.isStubSynthetic(ProtocolUtils.expectOutput(model, operation))
&& streamInfoOptional.isEmpty()) {
writer.addUseImports(SmithyGoDependency.IOUTIL);
writer.openBlock("if _, err = io.Copy(ioutil.Discard, response.Body); err != nil {", "}", () -> {
writer.openBlock("return out, metadata, &smithy.DeserializationError{", "}", () -> {
writer.write("Err: fmt.Errorf(\"failed to discard response body, %w\", err),");
});
});
} else {
boolean hasBodyBinding = HttpBindingIndex.of(model).getResponseBindings(operation).values().stream()
.filter(httpBinding -> httpBinding.getLocation() == HttpBinding.Location.DOCUMENT
|| httpBinding.getLocation() == HttpBinding.Location.PAYLOAD)
.anyMatch(httpBinding -> streamInfoOptional.map(esi -> !esi.getEventStreamMember()
.equals(httpBinding.getMember())).orElse(true));
if (hasBodyBinding && streamInfoOptional.isPresent()) {
throw new CodegenException("HTTP Binding Protocol unexpected document or payload bindings with "
+ "output event stream");
}
if (hasBodyBinding) {
// Output Shape Document Binding middleware generation
writeMiddlewareDocumentDeserializerDelegator(context, operation, generator);
}
}
writer.write("");
writer.write("span.End()");
writer.write("return out, metadata, err");
});
goWriter.write("");
Set<StructureShape> errorShapes = HttpProtocolGeneratorUtils.generateErrorDispatcher(
context, operation, responseType, this::writeErrorMessageCodeDeserializer,
this::getOperationErrors);
deserializingErrorShapes.addAll(errorShapes);
deserializeDocumentBindingShapes.addAll(errorShapes);
}