in codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/HttpBindingProtocolGenerator.java [235:367]
private void generateOperationSerializerMiddleware(GenerationContext context, OperationShape operation) {
SymbolProvider symbolProvider = context.getSymbolProvider();
Model model = context.getModel();
ServiceShape service = context.getService();
Shape inputShape = model.expectShape(operation.getInput()
.orElseThrow(() -> new CodegenException("expect input shape for operation: " + operation.getId())));
Symbol inputSymbol = symbolProvider.toSymbol(inputShape);
ApplicationProtocol applicationProtocol = getApplicationProtocol();
Symbol requestType = applicationProtocol.getRequestType();
HttpTrait httpTrait = operation.expectTrait(HttpTrait.class);
GoStackStepMiddlewareGenerator middleware = GoStackStepMiddlewareGenerator.createSerializeStepMiddleware(
ProtocolGenerator.getSerializeMiddlewareName(operation.getId(), service, getProtocolName()),
ProtocolUtils.OPERATION_SERIALIZER_MIDDLEWARE_ID);
middleware.writeMiddleware(context.getWriter().get(), (generator, writer) -> {
writer.addUseImports(SmithyGoDependency.FMT);
writer.addUseImports(SmithyGoDependency.SMITHY);
writer.addUseImports(SmithyGoDependency.SMITHY_HTTP_BINDING);
writer.write(goTemplate("""
_, span := $T(ctx, "OperationSerializer")
endTimer := startMetricTimer(ctx, "client.call.serialization_duration")
defer endTimer()
defer span.End()
""", SMITHY_TRACING.func("StartSpan")));
// cast input request to smithy transport type, check for failures
writer.write("request, ok := in.Request.($P)", requestType);
writer.openBlock("if !ok {", "}", () -> {
writer.write("return out, metadata, "
+ "&smithy.SerializationError{Err: fmt.Errorf(\"unknown transport type %T\", in.Request)}"
);
});
writer.write("");
// cast input parameters type to the input type of the operation
writer.write("input, ok := in.Parameters.($P)", inputSymbol);
writer.write("_ = input");
writer.openBlock("if !ok {", "}", () -> {
writer.write("return out, metadata, "
+ "&smithy.SerializationError{Err: fmt.Errorf(\"unknown input parameters type %T\","
+ " in.Parameters)}");
});
writer.write("");
writer.write("opPath, opQuery := httpbinding.SplitURI($S)", httpTrait.getUri());
writer.write("request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath)");
writer.write("request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery)");
writer.write("request.Method = $S", httpTrait.getMethod());
writer.write(
"""
var restEncoder $P
if request.URL.RawPath == "" {
restEncoder, err = $T(request.URL.Path, request.URL.RawQuery, request.Header)
} else {
request.URL.RawPath = $T(request.URL.RawPath, opPath)
restEncoder, err = $T(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header)
}
""",
SymbolUtils.createPointableSymbolBuilder(
"Encoder", SmithyGoDependency.SMITHY_HTTP_BINDING).build(),
SymbolUtils.createValueSymbolBuilder(
"NewEncoder", SmithyGoDependency.SMITHY_HTTP_BINDING).build(),
SymbolUtils.createValueSymbolBuilder(
"JoinPath", SmithyGoDependency.SMITHY_HTTP_TRANSPORT).build(),
SymbolUtils.createValueSymbolBuilder(
"NewEncoderWithRawPath", SmithyGoDependency.SMITHY_HTTP_BINDING).build()
);
writer.openBlock("if err != nil {", "}", () -> {
writer.write("return out, metadata, &smithy.SerializationError{Err: err}");
});
writer.write("");
// we only generate an operations http bindings function if there are bindings
if (isOperationWithRestRequestBindings(model, operation)) {
String serFunctionName = ProtocolGenerator.getOperationHttpBindingsSerFunctionName(
inputShape, service, getProtocolName());
writer.openBlock("if err := $L(input, restEncoder); err != nil {", "}", serFunctionName, () -> {
writer.write("return out, metadata, &smithy.SerializationError{Err: err}");
});
writer.write("");
}
// Don't consider serializing the body if the input shape is a stubbed synthetic clone, without an
// archetype.
if (!CodegenUtils.isStubSynthetic(ProtocolUtils.expectInput(model, operation))) {
Optional<EventStreamInfo> eventStreamInfo = EventStreamIndex.of(model).getInputInfo(operation);
// document bindings vs payload bindings vs event streams
HttpBindingIndex httpBindingIndex = HttpBindingIndex.of(model);
boolean hasDocumentBindings = httpBindingIndex
.getRequestBindings(operation, HttpBinding.Location.DOCUMENT)
.stream().anyMatch(httpBinding -> eventStreamInfo.map(streamInfo ->
!streamInfo.getEventStreamMember().equals(httpBinding.getMember())).orElse(true));
Optional<HttpBinding> payloadBinding = httpBindingIndex.getRequestBindings(operation,
HttpBinding.Location.PAYLOAD).stream()
.filter(httpBinding -> eventStreamInfo.map(streamInfo ->
!streamInfo.getEventStreamMember().equals(httpBinding.getMember())).orElse(true))
.findFirst();
if (eventStreamInfo.isPresent() && (hasDocumentBindings || payloadBinding.isPresent())) {
throw new CodegenException("HTTP Binding Protocol unexpected document or payload bindings with "
+ "input event stream");
}
if (eventStreamInfo.isPresent()) {
writeOperationSerializerMiddlewareEventStreamSetup(context, eventStreamInfo.get());
} else if (hasDocumentBindings) {
// delegate the setup and usage of the document serializer function for the protocol
writeMiddlewareDocumentSerializerDelegator(context, operation, generator);
} else if (payloadBinding.isPresent()) {
// delegate the setup and usage of the payload serializer function for the protocol
MemberShape memberShape = payloadBinding.get().getMember();
writeMiddlewarePayloadSerializerDelegator(context, memberShape);
}
writer.write("");
}
// Serialize HTTP request with payload, if set.
writer.openBlock("if request.Request, err = restEncoder.Encode(request.Request); err != nil {", "}", () -> {
writer.write("return out, metadata, &smithy.SerializationError{Err: err}");
});
writer.write("in.Request = request");
writer.write("");
writer.write("endTimer()");
writer.write("span.End()");
writer.write("return next.$L(ctx, in)", generator.getHandleMethodName());
});
}