Skip to content

Update Error handling through async #54

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

Merged
merged 1 commit into from
Dec 13, 2019
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -592,11 +592,10 @@ private void generateErrorDeserializer(GenerationContext context, StructureShape
String errorDeserMethodName = ProtocolGenerator.getDeserFunctionName(errorSymbol,
context.getProtocolName()) + "Response";

writer.openBlock("const $L = (\n"
writer.openBlock("const $L = async (\n"
+ " output: any,\n"
+ " context: __SerdeContext\n"
+ "): $T => {", "};", errorDeserMethodName, errorSymbol, () -> {

+ "): Promise<$T> => {", "};", errorDeserMethodName, errorSymbol, () -> {
writer.openBlock("const contents: $T = {", "};", errorSymbol, () -> {
writer.write("__type: $S,", error.getId().getName());
writer.write("$$fault: $S,", error.getTrait(ErrorTrait.class).get().getValue());
Expand Down Expand Up @@ -677,6 +676,25 @@ private List<HttpBinding> readResponseBody(
documentBindings.sort(Comparator.comparing(HttpBinding::getMemberName));
List<HttpBinding> payloadBindings = bindingIndex.getResponseBindings(operationOrError, Location.PAYLOAD);

if (!documentBindings.isEmpty()) {
readReponseBodyData(context, operationOrError);
deserializeOutputDocument(context, operationOrError, documentBindings);
return documentBindings;
}
if (!payloadBindings.isEmpty()) {
readReponseBodyData(context, operationOrError);
// There can only be one payload binding.
HttpBinding binding = payloadBindings.get(0);
Shape target = context.getModel().expectShape(binding.getMember().getTarget());
writer.write("contents.$L = $L;", binding.getMemberName(), getOutputValue(context,
Location.PAYLOAD, "data", binding.getMember(), target));
return payloadBindings;
}
return ListUtils.of();
}

private void readReponseBodyData(GenerationContext context, Shape operationOrError) {
TypeScriptWriter writer = context.getWriter();
// Prepare response body for deserializing.
OperationIndex operationIndex = context.getModel().getKnowledge(OperationIndex.class);
StructureShape operationOutputOrError = operationOrError.asStructureShape()
Expand All @@ -692,20 +710,6 @@ private List<HttpBinding> readResponseBody(
// Otherwise, we collect the response body to structured object with parseBody().
writer.write("const data: any = await parseBody(output.body, context);");
}

if (!documentBindings.isEmpty()) {
deserializeOutputDocument(context, operationOrError, documentBindings);
return documentBindings;
}
if (!payloadBindings.isEmpty()) {
// There can only be one payload binding.
HttpBinding binding = payloadBindings.get(0);
Shape target = context.getModel().expectShape(binding.getMember().getTarget());
writer.write("contents.$L = $L;", binding.getMemberName(), getOutputValue(context,
Location.PAYLOAD, "data", binding.getMember(), target));
return payloadBindings;
}
return ListUtils.of();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.shapes.StructureShape;
import software.amazon.smithy.model.traits.TimestampFormatTrait.Format;
import software.amazon.smithy.typescript.codegen.TypeScriptDependency;
import software.amazon.smithy.typescript.codegen.TypeScriptWriter;
import software.amazon.smithy.typescript.codegen.integration.ProtocolGenerator.GenerationContext;

Expand Down Expand Up @@ -146,7 +147,13 @@ static Set<StructureShape> generateErrorDispatcher(
writer.write("body: data,");
});
}
writer.write("let response: any;");

// Error responses must be at least SmithyException and MetadataBearer implementations.
writer.addImport("SmithyException", "__SmithyException",
TypeScriptDependency.AWS_SMITHY_CLIENT.packageName);
writer.addImport("MetadataBearer", "__MetadataBearer",
TypeScriptDependency.AWS_SDK_TYPES.packageName);
writer.write("let response: __SmithyException & __MetadataBearer;");
writer.write("let errorCode: String;");
errorCodeGenerator.accept(context);
writer.openBlock("switch (errorCode) {", "}", () -> {
Expand All @@ -160,7 +167,7 @@ static Set<StructureShape> generateErrorDispatcher(
context.getProtocolName()) + "Response";
writer.openBlock("case $S:\ncase $S:", " break;", errorId.getName(), errorId.toString(), () -> {
// Dispatch to the error deserialization function.
writer.write("response = $L(parsedOutput, context);", errorDeserMethodName);
writer.write("response = await $L(parsedOutput, context);", errorDeserMethodName);
});
});

Expand All @@ -169,8 +176,8 @@ static Set<StructureShape> generateErrorDispatcher(
.write("errorCode = errorCode || \"UnknownError\";")
.openBlock("response = {", "};", () -> {
writer.write("__type: `$L#$${errorCode}`,", operation.getId().getNamespace());
writer.write("$$name: errorCode,");
writer.write("$$fault: \"client\",");
writer.write("$$metadata: deserializeMetadata(output),");
}).dedent();
});
writer.write("return Promise.reject(Object.assign(new Error(response.__type), response));");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,6 @@ private void generateOperationDeserializer(GenerationContext context, OperationS
});

// Start deserializing the response.
writer.write("const data: any = await parseBody(output.body, context)");
writer.write("let contents: any = {};");
readResponseBody(context, operation);

// Build the response with typing and metadata.
Expand Down Expand Up @@ -270,10 +268,10 @@ private void generateErrorDeserializer(GenerationContext context, StructureShape
// Add the error shape to the list to generate functions for, since we'll use that.
deserializingDocumentShapes.add(error);

writer.openBlock("const $L = (\n"
writer.openBlock("const $L = async (\n"
+ " output: any,\n"
+ " context: __SerdeContext\n"
+ "): $T => {", "};", errorDeserMethodName, errorSymbol, () -> {
+ "): Promise<$T> => {", "};", errorDeserMethodName, errorSymbol, () -> {
// First deserialize the body properly.
writer.write("const deserialized: any = $L(output.body, context);",
ProtocolGenerator.getDeserFunctionName(errorSymbol, context.getProtocolName()));
Expand All @@ -294,6 +292,11 @@ private void generateErrorDeserializer(GenerationContext context, StructureShape

private void readResponseBody(GenerationContext context, OperationShape operation) {
operation.getOutput().ifPresent(outputId -> {
// We only need to load the body and prepare a contents object if there is a response.
TypeScriptWriter writer = context.getWriter();
writer.write("const data: any = await parseBody(output.body, context)");
writer.write("let contents: any = {};");

// If there's an output present, we know it's a structure.
StructureShape outputShape = context.getModel().expectShape(outputId).asStructureShape().get();

Expand Down