Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Awsjson10 Deserializer Implementation w/o Errors #533

Merged
merged 3 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,15 @@
package software.amazon.smithy.go.codegen.protocol.aws;

import static software.amazon.smithy.go.codegen.ApplicationProtocol.createDefaultHttpApplicationProtocol;
import static software.amazon.smithy.go.codegen.GoWriter.goTemplate;
import static software.amazon.smithy.go.codegen.serde.SerdeUtil.getShapesToSerde;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import software.amazon.smithy.aws.traits.protocols.AwsJson1_0Trait;
import software.amazon.smithy.go.codegen.ApplicationProtocol;
import software.amazon.smithy.go.codegen.GoWriter;
import software.amazon.smithy.go.codegen.SmithyGoDependency;
import software.amazon.smithy.go.codegen.integration.ProtocolGenerator;
import software.amazon.smithy.go.codegen.server.protocol.JsonDeserializerGenerator;
import software.amazon.smithy.go.codegen.server.protocol.JsonSerializerGenerator;
import software.amazon.smithy.model.shapes.OperationShape;
import software.amazon.smithy.model.shapes.Shape;
Expand Down Expand Up @@ -72,11 +70,24 @@ public void generateSharedSerializers(GenerationContext context, GoWriter writer
@Override
public void generateResponseDeserializers(GenerationContext context) {
var writer = context.getWriter().get();
var model = context.getModel();
var ops = model.getOperationShapes();
var ops = context.getModel().getOperationShapes();
for (var op : ops) {
responseDeserializerCode(op, writer);
var middle = new DeserializeMiddleware(context, op, writer);
writer.write(middle.generate());
writer.write("\n");
}
generateSharedDeserializers(context, writer, ops);
}

private void generateSharedDeserializers(GenerationContext context, GoWriter writer, Set<OperationShape> ops) {
Set<Shape> shared = new HashSet<>();
for (var op : ops) {
Set<Shape> shapes = getShapesToSerde(context.getModel(), context.getModel().expectShape(
op.getOutputShape()));
shared.addAll(shapes);
}
var generator = new JsonDeserializerGenerator(context.getModel(), context.getSymbolProvider());
writer.write(generator.generate(shared));
}

@Override
Expand All @@ -98,46 +109,4 @@ public void generateProtocolDocumentUnmarshalerMarshalDocument(GenerationContext
public void generateProtocolDocumentUnmarshalerUnmarshalDocument(GenerationContext context) {
// TODO
}

private void responseDeserializerCode(OperationShape op, GoWriter writer) {
var opName = "awsAwsjson10_deserializeOp" + op.toShapeId().getName();

/* Struct Definition */
var struct = goTemplate("""
type $L struct{
}
""", opName
);
writer.write(struct);


//ID Function
var idFunction = goTemplate("""
func (op *$L) ID() string {
return "OperationDeserializer"
}
""", opName
);
writer.write(idFunction);

//Handle Serialize Function
var handleFunction = goTemplate(
"""
func (op *$structName:L) HandleDeserialize (ctx $context:T, in $input:T, next $handler:T) (
out $output:T, metadata $metadata:T, err error) {
return out, metadata, err
}
""", Map.of(
"context", SmithyGoDependency.CONTEXT.interfaceSymbol("Context"),
"input", SmithyGoDependency.SMITHY_MIDDLEWARE.struct("DeserializeInput"),
"handler", SmithyGoDependency.SMITHY_MIDDLEWARE.struct("DeserializeHandler"),
"output", SmithyGoDependency.SMITHY_MIDDLEWARE.struct("DeserializeOutput"),
"metadata", SmithyGoDependency.SMITHY_MIDDLEWARE.struct("Metadata"),
"structName", opName
));
writer.write(handleFunction);
/* Operation End */
writer.write("\n");
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package software.amazon.smithy.go.codegen.protocol.aws;

import static software.amazon.smithy.go.codegen.GoWriter.goTemplate;
import static software.amazon.smithy.go.codegen.SmithyGoDependency.SMITHY_HTTP_TRANSPORT;
import static software.amazon.smithy.go.codegen.protocol.ProtocolUtil.hasEventStream;
import static software.amazon.smithy.go.codegen.server.protocol.JsonDeserializerGenerator.getDeserializerName;

import software.amazon.smithy.go.codegen.GoStdlibTypes;
import software.amazon.smithy.go.codegen.GoWriter;
import software.amazon.smithy.go.codegen.SmithyGoDependency;
import software.amazon.smithy.go.codegen.integration.ProtocolGenerator;
import software.amazon.smithy.model.shapes.OperationShape;
import software.amazon.smithy.model.shapes.StructureShape;
import software.amazon.smithy.utils.MapUtils;

public class DeserializeMiddleware {

protected final ProtocolGenerator.GenerationContext ctx;
protected final OperationShape operation;
protected final GoWriter writer;

protected final StructureShape input;
protected final StructureShape output;

private String deserialName;

public DeserializeMiddleware(
ProtocolGenerator.GenerationContext ctx, OperationShape operation, GoWriter writer
) {
this.ctx = ctx;
this.operation = operation;
this.writer = writer;

this.input = ctx.getModel().expectShape(operation.getInputShape(), StructureShape.class);
this.output = ctx.getModel().expectShape(operation.getOutputShape(), StructureShape.class);

deserialName = getMiddlewareName(operation);
}

public static String getMiddlewareName(OperationShape operation) {
return "awsAwsjson10_deserializeOp" + operation.toShapeId().getName();
}

public GoWriter.Writable generate() {
return goTemplate("""

type $opName:L struct{
}

func (op *$opName:L) ID() string {
return "OperationDeserializer"
}

$handleSerialize:W

""",
MapUtils.of(
"opName", deserialName,
"handleSerialize", generateHandleDeserialize()
));
}

private GoWriter.Writable generateHandleDeserialize() {
return goTemplate(
"""

func (op *$opName:L) HandleDeserialize (ctx $context:T, in $input:T, next $handler:T) (
nathanhit marked this conversation as resolved.
Show resolved Hide resolved
out $output:T, metadata $metadata:T, err error) {

$body:W

return out, metadata, nil
}

""", MapUtils.of(
"context", SmithyGoDependency.CONTEXT.interfaceSymbol("Context"),
"input", SmithyGoDependency.SMITHY_MIDDLEWARE.struct("DeserializeInput"),
"handler", SmithyGoDependency.SMITHY_MIDDLEWARE.struct("DeserializeHandler"),
"output", SmithyGoDependency.SMITHY_MIDDLEWARE.struct("DeserializeOutput"),
"metadata", SmithyGoDependency.SMITHY_MIDDLEWARE.struct("Metadata"),
"opName", deserialName,
"body", generateHandleDeserializeBody())
);
}

private GoWriter.Writable generateHandleDeserializeBody() {
return goTemplate("""
$errors:W

$response:W
""",
MapUtils.of(
"errors", handleResponseChecks(),
"response", handleResponse()
));
}


private GoWriter.Writable handleResponseChecks() {
return goTemplate("""

out, metadata, err = next.HandleDeserialize(ctx, in)
if err != nil {
return out, metadata, err
}

resp, ok := out.RawResponse.($response:P)
if !ok {
return out, metadata, $errorf:T("unexpected transport type %T", out.RawResponse)
}

if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return out, metadata, &$deserError:T{}
}

""",
MapUtils.of(
"response", SMITHY_HTTP_TRANSPORT.pointableSymbol("Response"),
"errorf", GoStdlibTypes.Fmt.Errorf,
"deserError", SmithyGoDependency.SMITHY.struct("DeserializationError")
));
}

private GoWriter.Writable handleResponse() {
if (output.members().isEmpty()) {
return discardDeserialize();
} else if (hasEventStream(ctx.getModel(), output)) {
return deserializeEventStream();
}
return handlePayload();
}

private GoWriter.Writable handlePayload() {
return goTemplate("""
payload, err := $readAll:T(resp.Body)
if err != nil {
return out, metadata, err
}

if len(payload) == 0 {
out.Result = &$output:T{}
return out, metadata, nil
}

decoder := $decoder:T(resp.Body)
var jv map[string]interface{}
err = decoder.Decode(&jv)
if err!= nil {
return out, metadata, err
}

output, err := $deserialize:L(jv)
if err != nil {
return out, metadata, err
}

out.Result = output
""",
MapUtils.of(
"readAll", GoStdlibTypes.Io.ReadAll,
"output", ctx.getSymbolProvider()
.toSymbol(ctx.getModel().expectShape(operation.getOutputShape())),
"decoder", GoStdlibTypes.Encoding.Json.NewDecoder,
"deserialize", getDeserializerName(output)
));
}

private GoWriter.Writable discardDeserialize() {
return goTemplate("""
if _, err = $copy:T($discard:T, resp.Body); err != nil {
return out, metadata, $errorf:T("discard response body: %w", err)
}

out.Result = &$result:T{}
""",
MapUtils.of(
"copy", GoStdlibTypes.Io.Copy,
"discard", GoStdlibTypes.Io.IoUtil.Discard,
"errorf", GoStdlibTypes.Fmt.Errorf,
"result", ctx.getSymbolProvider().toSymbol(output)
));
}

// Basically a no-op. Event stream deserializer middleware, implemented elsewhere, will handle the wire-up here,
// including handling the initial-response message to deserialize any non-stream members to output.
// Taken straight from CBOR implementation
private GoWriter.Writable deserializeEventStream() {
return goTemplate("out.Result = &$T{}", ctx.getSymbolProvider().toSymbol(output));
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,15 @@ public SerializeMiddleware(

this.input = ctx.getModel().expectShape(operation.getInputShape(), StructureShape.class);
this.output = ctx.getModel().expectShape(operation.getOutputShape(), StructureShape.class);

serialName = getMiddlewareName(operation);
}

public static String getMiddlewareName(OperationShape operation) {
return "awsAwsjson10_serializeOp" + operation.toShapeId().getName();
}

public GoWriter.Writable generate() {
serialName = getMiddlewareName(operation);

return goTemplate("""

type $opName:L struct{
Expand Down
Loading
Loading