From 21eaa6cab3962934751b3c1d2c3e6eaf4b10a914 Mon Sep 17 00:00:00 2001 From: Eddy Ilangovan Date: Tue, 6 Dec 2022 23:16:44 -0800 Subject: [PATCH] Add `ErrorCodeOverride` to error structures Adding the `ErrorCodeOverride` field allows protocols to override the default `ErrorCode()` implementation (which is the name of the error) with a custom string. If a smithy model uses the `ErrorCodeOverride` field in an error structure, it will be renamed to `ErrorCodeOverride_`. Also, the writer for the switch-statement default block is now pluggable, which means the default block can be overwritten with a custom writer. --- .../smithy/go/codegen/StructureGenerator.java | 13 ++++-- .../smithy/go/codegen/SymbolVisitor.java | 1 + .../HttpProtocolGeneratorUtils.java | 43 +++++++++++++++---- .../integration/HttpRpcProtocolGenerator.java | 11 +++-- 4 files changed, 53 insertions(+), 15 deletions(-) diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java index e8843f2f0..24927977e 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java @@ -38,7 +38,7 @@ final class StructureGenerator implements Runnable { "ErrorMessage", "string", "ErrorFault", "string" ); - private static final Set ERROR_MESSAGE_MEMBER_NAMES = SetUtils.of("ErrorMessage", "Message"); + private static final Set ERROR_MEMBER_NAMES = SetUtils.of("ErrorMessage", "Message", "ErrorCodeOverride"); private final Model model; private final SymbolProvider symbolProvider; @@ -144,12 +144,12 @@ private void renderErrorStructure() { // The message is the only part of the standard APIError interface that isn't known ahead of time. // Message is a pointer mostly for the sake of consistency. writer.write("Message *string").write(""); - + writer.write("ErrorCodeOverride *string").write(""); for (MemberShape member : shape.getAllMembers().values()) { String memberName = symbolProvider.toMemberName(member); // error messages are represented under Message for consistency - if (!ERROR_MESSAGE_MEMBER_NAMES.contains(memberName)) { + if (!ERROR_MEMBER_NAMES.contains(memberName)) { writer.write("$L $P", memberName, symbolProvider.toSymbol(member)); } } @@ -174,7 +174,12 @@ private void renderErrorStructure() { String errorCode = protocolGenerator == null ? shape.getId().getName(service) : protocolGenerator.getErrorCode(service, shape); - writer.write("func (e *$L) ErrorCode() string { return $S }", structureSymbol.getName(), errorCode); + writer.openBlock("func (e *$L) ErrorCode() string {", "}", structureSymbol.getName(), () -> { + writer.openBlock("if e.ErrorCodeOverride == nil {", "}", () -> { + writer.write("return $S", errorCode); + }); + writer.write("return *e.ErrorCodeOverride"); + }); String fault = "smithy.FaultUnknown"; if (errorTrait.isClientError()) { diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SymbolVisitor.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SymbolVisitor.java index 55ce3134f..e60fd0267 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SymbolVisitor.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SymbolVisitor.java @@ -122,6 +122,7 @@ final class SymbolVisitor implements SymbolProvider, ShapeVisitor { .put("ErrorFault", "ErrorFault_") .put("Unwrap", "Unwrap_") .put("Error", "Error_") + .put("ErrorCodeOverride", "ErrorCodeOverride_") .build(); errorMemberEscaper = ReservedWordSymbolProvider.builder() diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/HttpProtocolGeneratorUtils.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/HttpProtocolGeneratorUtils.java index e002ef450..808f8ce25 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/HttpProtocolGeneratorUtils.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/HttpProtocolGeneratorUtils.java @@ -59,12 +59,45 @@ private HttpProtocolGeneratorUtils() { * and {@code errorMessage} variables from the http response. * @return A set of all error structure shapes for the operation that were dispatched to. */ - static Set generateErrorDispatcher( + public static Set generateErrorDispatcher( GenerationContext context, OperationShape operation, Symbol responseType, Consumer errorMessageCodeGenerator, BiFunction> operationErrorsToShapes + ) { + return generateErrorDispatcher( + context, operation, responseType, + errorMessageCodeGenerator, operationErrorsToShapes, writer -> defaultBlock(writer)); + } + + private static void defaultBlock(GoWriter writer) { + writer.openBlock("default:", "", () -> { + writer.openBlock("genericError := &smithy.GenericAPIError{", "}", () -> { + writer.write("Code: errorCode,"); + writer.write("Message: errorMessage,"); + }); + writer.write("return genericError"); + }); + } + + @FunctionalInterface + public interface DefaultBlockWriter { + void write(GoWriter writer); + } + + /** + * Generates a function that handles error deserialization by getting the error code then + * dispatching to the error-specific deserializer. Provies an option to write custom default + * error block. + */ + public static Set generateErrorDispatcher( + GenerationContext context, + OperationShape operation, + Symbol responseType, + Consumer errorMessageCodeGenerator, + BiFunction> operationErrorsToShapes, + DefaultBlockWriter defaultBlockWriter ) { GoWriter writer = context.getWriter().get(); ServiceShape service = context.getService(); @@ -111,13 +144,7 @@ static Set generateErrorDispatcher( // Create a generic error writer.addUseImports(SmithyGoDependency.SMITHY); - writer.openBlock("default:", "", () -> { - writer.openBlock("genericError := &smithy.GenericAPIError{", "}", () -> { - writer.write("Code: errorCode,"); - writer.write("Message: errorMessage,"); - }); - writer.write("return genericError"); - }); + defaultBlockWriter.write(writer); }); }).write(""); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/HttpRpcProtocolGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/HttpRpcProtocolGenerator.java index f690a6924..da9ec636e 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/HttpRpcProtocolGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/HttpRpcProtocolGenerator.java @@ -368,13 +368,18 @@ private void generateOperationDeserializer(GenerationContext context, OperationS }); writer.write(""); - Set errorShapes = HttpProtocolGeneratorUtils.generateErrorDispatcher( - context, operation, responseType, this::writeErrorMessageCodeDeserializer, - this::getOperationErrors); + Set errorShapes = generateErrorShapes(context, operation, responseType); deserializingErrorShapes.addAll(errorShapes); deserializingDocumentShapes.addAll(errorShapes); } + protected Set generateErrorShapes( + GenerationContext context, OperationShape operation, Symbol responseType) { + return HttpProtocolGeneratorUtils.generateErrorDispatcher( + context, operation, responseType, this::writeErrorMessageCodeDeserializer, + this::getOperationErrors); + } + /** * Generate the document deserializer logic for the deserializer middleware body. *