Skip to content

Commit

Permalink
Add ErrorCodeOverride to error structures
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
eddy-aws authored and Steven Yuan committed Jan 4, 2023
1 parent 67a719d commit 21eaa6c
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ final class StructureGenerator implements Runnable {
"ErrorMessage", "string",
"ErrorFault", "string"
);
private static final Set<String> ERROR_MESSAGE_MEMBER_NAMES = SetUtils.of("ErrorMessage", "Message");
private static final Set<String> ERROR_MEMBER_NAMES = SetUtils.of("ErrorMessage", "Message", "ErrorCodeOverride");

private final Model model;
private final SymbolProvider symbolProvider;
Expand Down Expand Up @@ -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));
}
}
Expand All @@ -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()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ final class SymbolVisitor implements SymbolProvider, ShapeVisitor<Symbol> {
.put("ErrorFault", "ErrorFault_")
.put("Unwrap", "Unwrap_")
.put("Error", "Error_")
.put("ErrorCodeOverride", "ErrorCodeOverride_")
.build();

errorMemberEscaper = ReservedWordSymbolProvider.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<StructureShape> generateErrorDispatcher(
public static Set<StructureShape> generateErrorDispatcher(
GenerationContext context,
OperationShape operation,
Symbol responseType,
Consumer<GenerationContext> errorMessageCodeGenerator,
BiFunction<GenerationContext, OperationShape, Map<String, ShapeId>> 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<StructureShape> generateErrorDispatcher(
GenerationContext context,
OperationShape operation,
Symbol responseType,
Consumer<GenerationContext> errorMessageCodeGenerator,
BiFunction<GenerationContext, OperationShape, Map<String, ShapeId>> operationErrorsToShapes,
DefaultBlockWriter defaultBlockWriter
) {
GoWriter writer = context.getWriter().get();
ServiceShape service = context.getService();
Expand Down Expand Up @@ -111,13 +144,7 @@ static Set<StructureShape> 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("");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -368,13 +368,18 @@ private void generateOperationDeserializer(GenerationContext context, OperationS
});
writer.write("");

Set<StructureShape> errorShapes = HttpProtocolGeneratorUtils.generateErrorDispatcher(
context, operation, responseType, this::writeErrorMessageCodeDeserializer,
this::getOperationErrors);
Set<StructureShape> errorShapes = generateErrorShapes(context, operation, responseType);
deserializingErrorShapes.addAll(errorShapes);
deserializingDocumentShapes.addAll(errorShapes);
}

protected Set<StructureShape> 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.
*
Expand Down

0 comments on commit 21eaa6c

Please sign in to comment.