in codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/Waiters.java [487:648]
private void generateRetryable(
Model model,
SymbolProvider symbolProvider,
GoWriter writer,
ServiceShape serviceShape,
OperationShape operationShape,
String waiterName,
Waiter waiter
) {
StructureShape inputShape = model.expectShape(
operationShape.getInput().get(), StructureShape.class
);
StructureShape outputShape = model.expectShape(
operationShape.getOutput().get(), StructureShape.class
);
Symbol inputSymbol = symbolProvider.toSymbol(inputShape);
Symbol outputSymbol = symbolProvider.toSymbol(outputShape);
writer.write("");
writer.openBlock("func $L(ctx context.Context, input $P, output $P, err error) (bool, error) {",
"}", generateRetryableName(waiterName), inputSymbol, outputSymbol, () -> {
waiter.getAcceptors().forEach(acceptor -> {
writer.write("");
// scope each acceptor to avoid name collisions
Matcher matcher = acceptor.getMatcher();
switch (matcher.getMemberName()) {
case "output":
writer.addUseImports(SmithyGoDependency.GO_JMESPATH);
writer.addUseImports(SmithyGoDependency.FMT);
Matcher.OutputMember outputMember = (Matcher.OutputMember) matcher;
String path = outputMember.getValue().getPath();
String expectedValue = outputMember.getValue().getExpected();
PathComparator comparator = outputMember.getValue().getComparator();
writer.openBlock("if err == nil {", "}", () -> {
writer.write("pathValue, err := jmespath.Search($S, output)", path);
writer.openBlock("if err != nil {", "}", () -> {
writer.write(
"return false, "
+ "fmt.Errorf(\"error evaluating waiter state: %w\", err)");
}).write("");
writer.write("expectedValue := $S", expectedValue);
if (comparator == PathComparator.BOOLEAN_EQUALS) {
writeWaiterComparator(writer, acceptor, comparator, null, "pathValue",
"expectedValue");
} else {
String[] pathMembers = path.split("\\.");
Shape targetShape = outputShape;
for (int i = 0; i < pathMembers.length; i++) {
MemberShape member = getComparedMember(model, targetShape, pathMembers[i]);
if (member == null) {
targetShape = null;
break;
}
targetShape = model.expectShape(member.getTarget());
}
if (targetShape == null) {
writeWaiterComparator(writer, acceptor, comparator, null, "pathValue",
"expectedValue");
} else {
Symbol targetSymbol = symbolProvider.toSymbol(targetShape);
writeWaiterComparator(writer, acceptor, comparator, targetSymbol,
"pathValue",
"expectedValue");
}
}
});
break;
case "inputOutput":
writer.addUseImports(SmithyGoDependency.GO_JMESPATH);
writer.addUseImports(SmithyGoDependency.FMT);
Matcher.InputOutputMember ioMember = (Matcher.InputOutputMember) matcher;
path = ioMember.getValue().getPath();
expectedValue = ioMember.getValue().getExpected();
comparator = ioMember.getValue().getComparator();
writer.openBlock("if err == nil {", "}", () -> {
writer.openBlock("pathValue, err := jmespath.Search($S, &struct{",
"})", path, () -> {
writer.write("Input $P \n Output $P \n }{", inputSymbol,
outputSymbol);
writer.write("Input: input, \n Output: output, \n");
});
writer.openBlock("if err != nil {", "}", () -> {
writer.write(
"return false, "
+ "fmt.Errorf(\"error evaluating waiter state: %w\", err)");
});
writer.write("");
writer.write("expectedValue := $S", expectedValue);
writeWaiterComparator(writer, acceptor, comparator, outputSymbol, "pathValue",
"expectedValue");
});
break;
case "success":
Matcher.SuccessMember successMember = (Matcher.SuccessMember) matcher;
writer.openBlock("if err == nil {", "}",
() -> {
writeMatchedAcceptorReturn(writer, acceptor);
});
break;
case "errorType":
Matcher.ErrorTypeMember errorTypeMember = (Matcher.ErrorTypeMember) matcher;
String errorType = errorTypeMember.getValue();
writer.openBlock("if err != nil {", "}", () -> {
// identify if this is a modeled error shape
Optional<ShapeId> errorShapeId = operationShape.getErrors().stream().filter(
shapeId -> {
return shapeId.getName(serviceShape).equalsIgnoreCase(errorType);
}).findFirst();
// if modeled error shape
if (errorShapeId.isPresent()) {
Shape errorShape = model.expectShape(errorShapeId.get());
Symbol modeledErrorSymbol = symbolProvider.toSymbol(errorShape);
writer.addUseImports(SmithyGoDependency.ERRORS);
writer.write("var errorType *$T", modeledErrorSymbol);
writer.openBlock("if errors.As(err, &errorType) {", "}", () -> {
writeMatchedAcceptorReturn(writer, acceptor);
});
} else {
// fall back to un-modeled error shape matching
writer.addUseImports(SmithyGoDependency.SMITHY);
writer.addUseImports(SmithyGoDependency.ERRORS);
// assert unmodeled error to smithy's API error
writer.write("var apiErr smithy.APIError");
writer.write("ok := errors.As(err, &apiErr)");
writer.openBlock("if !ok {", "}", () -> {
writer.write("return false, "
+ "fmt.Errorf(\"expected err to be of type smithy.APIError, "
+ "got %w\", err)");
});
writer.write("");
writer.openBlock("if $S == apiErr.ErrorCode() {", "}",
errorType, () -> {
writeMatchedAcceptorReturn(writer, acceptor);
});
}
});
break;
default:
throw new CodegenException(
String.format("unknown waiter state : %v", matcher.getMemberName())
);
}
});
writer.write("");
writer.write("return true, nil");
});
}