diff --git a/src/main/java/com/google/api/generator/gapic/composer/BUILD.bazel b/src/main/java/com/google/api/generator/gapic/composer/BUILD.bazel index 21dc25a810..c9be270e28 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/BUILD.bazel +++ b/src/main/java/com/google/api/generator/gapic/composer/BUILD.bazel @@ -13,6 +13,7 @@ java_library( deps = [ "//:longrunning_java_proto", "//:monitored_resource_java_proto", + "//:rpc_java_proto", "//:service_config_java_proto", "//src/main/java/com/google/api/generator/engine/ast", "//src/main/java/com/google/api/generator/gapic:status_java_proto", diff --git a/src/main/java/com/google/api/generator/gapic/composer/RetrySettingsComposer.java b/src/main/java/com/google/api/generator/gapic/composer/RetrySettingsComposer.java index 7d43a096e9..612945c8ca 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/RetrySettingsComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/RetrySettingsComposer.java @@ -15,9 +15,11 @@ package com.google.api.generator.gapic.composer; import com.google.api.gax.retrying.RetrySettings; +import com.google.api.gax.rpc.StatusCode; import com.google.api.generator.engine.ast.AssignmentExpr; import com.google.api.generator.engine.ast.BlockStatement; import com.google.api.generator.engine.ast.ConcreteReference; +import com.google.api.generator.engine.ast.EnumRefExpr; import com.google.api.generator.engine.ast.Expr; import com.google.api.generator.engine.ast.ExprStatement; import com.google.api.generator.engine.ast.MethodInvocationExpr; @@ -33,8 +35,11 @@ import com.google.api.generator.gapic.model.Service; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; import com.google.protobuf.Duration; import com.google.protobuf.util.Durations; +import com.google.rpc.Code; import io.grpc.serviceconfig.MethodConfig.RetryPolicy; import java.util.ArrayList; import java.util.Arrays; @@ -45,6 +50,8 @@ public class RetrySettingsComposer { private static final Map STATIC_TYPES = createStaticTypes(); + private static final TypeNode STATUS_CODE_CODE_TYPE = + TypeNode.withReference(ConcreteReference.withClazz(StatusCode.Code.class)); public static BlockStatement createRetryParamDefinitionsBlock( Service service, @@ -116,6 +123,88 @@ public static BlockStatement createRetryParamDefinitionsBlock( .build(); } + public static BlockStatement createRetryCodesDefinitionsBlock( + Service service, + GapicServiceConfig serviceConfig, + VariableExpr retryCodesDefinitionsClassMemberVarExpr) { + TypeNode definitionsType = + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(ImmutableMap.Builder.class) + .setGenerics(retryCodesDefinitionsClassMemberVarExpr.type().reference().generics()) + .build()); + VariableExpr definitionsVarExpr = + VariableExpr.withVariable( + Variable.builder().setType(definitionsType).setName("definitions").build()); + + List bodyExprs = new ArrayList<>(); + // Create the first expr. + bodyExprs.add( + AssignmentExpr.builder() + .setVariableExpr(definitionsVarExpr.toBuilder().setIsDecl(true).build()) + .setValueExpr( + MethodInvocationExpr.builder() + .setStaticReferenceType(STATIC_TYPES.get("ImmutableMap")) + .setMethodName("builder") + .setReturnType(definitionsVarExpr.type()) + .build()) + .build()); + + for (Map.Entry> codeEntry : + serviceConfig.getAllRetryCodes(service).entrySet()) { + bodyExprs.add( + createRetryCodeDefinitionExpr( + codeEntry.getKey(), codeEntry.getValue(), definitionsVarExpr)); + } + + // Reassign the new codes. + bodyExprs.add( + AssignmentExpr.builder() + .setVariableExpr(retryCodesDefinitionsClassMemberVarExpr) + .setValueExpr( + MethodInvocationExpr.builder() + .setExprReferenceExpr(definitionsVarExpr) + .setMethodName("build") + .setReturnType(retryCodesDefinitionsClassMemberVarExpr.type()) + .build()) + .build()); + + // Put everything together. + return BlockStatement.builder() + .setIsStatic(true) + .setBody( + bodyExprs.stream().map(e -> ExprStatement.withExpr(e)).collect(Collectors.toList())) + .build(); + } + + private static Expr createRetryCodeDefinitionExpr( + String codeName, List retryCodes, VariableExpr definitionsVarExpr) { + // Construct something like `definitions.put("code_name", + // ImmutableSet.copYOf(Lists.newArrayList()));` + MethodInvocationExpr codeListExpr = + MethodInvocationExpr.builder() + .setStaticReferenceType(STATIC_TYPES.get("Lists")) + .setGenerics(Arrays.asList(STATUS_CODE_CODE_TYPE.reference())) + .setMethodName("newArrayList") + .setArguments( + retryCodes.stream() + .map(c -> toStatusCodeEnumRefExpr(c)) + .collect(Collectors.toList())) + .build(); + + MethodInvocationExpr codeSetExpr = + MethodInvocationExpr.builder() + .setStaticReferenceType(STATIC_TYPES.get("ImmutableSet")) + .setMethodName("copyOf") + .setArguments(codeListExpr) + .build(); + return MethodInvocationExpr.builder() + .setExprReferenceExpr(definitionsVarExpr) + .setMethodName("put") + .setArguments(ValueExpr.withValue(StringObjectValue.withValue(codeName)), codeSetExpr) + .build(); + } + private static List createRetrySettingsExprs( String settingsName, GapicRetrySettings settings, @@ -240,9 +329,19 @@ private static List createRetrySettingsExprs( return Arrays.asList(settingsAssignExpr, definitionsPutExpr); } + private static EnumRefExpr toStatusCodeEnumRefExpr(Code code) { + return EnumRefExpr.builder().setType(STATUS_CODE_CODE_TYPE).setName(code.name()).build(); + } + private static Map createStaticTypes() { List concreteClazzes = - Arrays.asList(org.threeten.bp.Duration.class, ImmutableMap.class, RetrySettings.class); + Arrays.asList( + org.threeten.bp.Duration.class, + ImmutableMap.class, + ImmutableSet.class, + Lists.class, + RetrySettings.class, + StatusCode.class); return concreteClazzes.stream() .collect( Collectors.toMap( diff --git a/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java b/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java index 8e35e40ef9..68fbdd1c1b 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java @@ -1121,7 +1121,7 @@ private static List createNestedClassStatements( .map(v -> varDeclFn.apply(v)) .collect(Collectors.toList())); - // Declare the RETRYABLE_CODE_DEFNITIONS field. + // Declare the RETRYABLE_CODE_DEFINITIONS field. Function varStaticDeclFn = v -> v.toBuilder() @@ -1135,8 +1135,13 @@ private static List createNestedClassStatements( List statements = new ArrayList<>(); statements.addAll( exprs.stream().map(e -> exprStatementFn.apply(e)).collect(Collectors.toList())); + + // Declare and set the RETRYABLE_CODE_DEFINITIONS field. statements.add( exprStatementFn.apply((varStaticDeclFn.apply(NESTED_RETRYABLE_CODE_DEFINITIONS_VAR_EXPR)))); + statements.add( + RetrySettingsComposer.createRetryCodesDefinitionsBlock( + service, serviceConfig, NESTED_RETRYABLE_CODE_DEFINITIONS_VAR_EXPR)); // Declare the RETRY_PARAM_DEFINITIONS field. statements.add( diff --git a/src/test/java/com/google/api/generator/gapic/composer/BUILD.bazel b/src/test/java/com/google/api/generator/gapic/composer/BUILD.bazel index eff9a8202a..0dca501d54 100644 --- a/src/test/java/com/google/api/generator/gapic/composer/BUILD.bazel +++ b/src/test/java/com/google/api/generator/gapic/composer/BUILD.bazel @@ -28,6 +28,7 @@ filegroup( ], test_class = "com.google.api.generator.gapic.composer.{0}".format(test_name), deps = [ + "//:rpc_java_proto", "//:service_config_java_proto", "//src/main/java/com/google/api/generator/engine/ast", "//src/main/java/com/google/api/generator/engine/writer", @@ -35,6 +36,7 @@ filegroup( "//src/main/java/com/google/api/generator/gapic/model", "//src/main/java/com/google/api/generator/gapic/protoparser", "//src/test/java/com/google/api/generator/gapic/testdata:showcase_java_proto", + "@com_google_api_gax_java//jar", "@com_google_protobuf//:protobuf_java", "@com_google_truth_truth//jar", "@junit_junit//jar", diff --git a/src/test/java/com/google/api/generator/gapic/composer/RetrySettingsComposerTest.java b/src/test/java/com/google/api/generator/gapic/composer/RetrySettingsComposerTest.java index eafc028566..03fef892ff 100644 --- a/src/test/java/com/google/api/generator/gapic/composer/RetrySettingsComposerTest.java +++ b/src/test/java/com/google/api/generator/gapic/composer/RetrySettingsComposerTest.java @@ -17,6 +17,7 @@ import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; +import com.google.api.gax.rpc.StatusCode; import com.google.api.generator.engine.ast.BlockStatement; import com.google.api.generator.engine.ast.ConcreteReference; import com.google.api.generator.engine.ast.TypeNode; @@ -31,6 +32,7 @@ import com.google.api.generator.gapic.protoparser.Parser; import com.google.api.generator.gapic.protoparser.ServiceConfigParser; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.google.protobuf.Descriptors.FileDescriptor; import com.google.protobuf.Descriptors.ServiceDescriptor; import com.google.showcase.v1beta1.EchoOuterClass; @@ -51,6 +53,8 @@ public class RetrySettingsComposerTest { "src/test/java/com/google/api/generator/gapic/testdata/"; private static final VariableExpr RETRY_PARAM_DEFINITIONS_VAR_EXPR = createRetryParamDefinitionsVarExpr(); + private static final VariableExpr RETRY_CODES_DEFINITIONS_VAR_EXPR = + createRetryableCodesDefinitionsVarExpr(); private JavaWriterVisitor writerVisitor; @@ -135,6 +139,101 @@ public void paramDefinitionsBlock_basic() { assertEquals(expected, writerVisitor.write()); } + @Test + public void codesDefinitionsBlock_noConfigsFound() { + FileDescriptor echoFileDescriptor = EchoOuterClass.getDescriptor(); + ServiceDescriptor echoServiceDescriptor = echoFileDescriptor.getServices().get(0); + Map messageTypes = Parser.parseMessages(echoFileDescriptor); + Map resourceNames = Parser.parseResourceNames(echoFileDescriptor); + Set outputResourceNames = new HashSet<>(); + List services = + Parser.parseService(echoFileDescriptor, messageTypes, resourceNames, outputResourceNames); + assertEquals(1, services.size()); + + Service service = services.get(0); + + String jsonFilename = "retrying_grpc_service_config.json"; + Path jsonPath = Paths.get(JSON_DIRECTORY, jsonFilename); + Optional serviceConfigOpt = ServiceConfigParser.parse(jsonPath.toString()); + assertTrue(serviceConfigOpt.isPresent()); + GapicServiceConfig serviceConfig = serviceConfigOpt.get(); + + BlockStatement paramDefinitionsBlock = + RetrySettingsComposer.createRetryCodesDefinitionsBlock( + service, serviceConfig, RETRY_CODES_DEFINITIONS_VAR_EXPR); + + paramDefinitionsBlock.accept(writerVisitor); + String expected = + createLines( + "static {\n", + "ImmutableMap.Builder> definitions =" + + " ImmutableMap.builder();\n", + "definitions.put(\"no_retry_codes\"," + + " ImmutableSet.copyOf(Lists.newArrayList()));\n", + "RETRYABLE_CODE_DEFINITIONS = definitions.build();\n", + "}\n"); + assertEquals(expected, writerVisitor.write()); + } + + @Test + public void codesDefinitionsBlock_basic() { + FileDescriptor echoFileDescriptor = EchoOuterClass.getDescriptor(); + ServiceDescriptor echoServiceDescriptor = echoFileDescriptor.getServices().get(0); + Map messageTypes = Parser.parseMessages(echoFileDescriptor); + Map resourceNames = Parser.parseResourceNames(echoFileDescriptor); + Set outputResourceNames = new HashSet<>(); + List services = + Parser.parseService(echoFileDescriptor, messageTypes, resourceNames, outputResourceNames); + assertEquals(1, services.size()); + + Service service = services.get(0); + + String jsonFilename = "showcase_grpc_service_config.json"; + Path jsonPath = Paths.get(JSON_DIRECTORY, jsonFilename); + Optional serviceConfigOpt = ServiceConfigParser.parse(jsonPath.toString()); + assertTrue(serviceConfigOpt.isPresent()); + GapicServiceConfig serviceConfig = serviceConfigOpt.get(); + + BlockStatement paramDefinitionsBlock = + RetrySettingsComposer.createRetryCodesDefinitionsBlock( + service, serviceConfig, RETRY_CODES_DEFINITIONS_VAR_EXPR); + + paramDefinitionsBlock.accept(writerVisitor); + String expected = + createLines( + "static {\n", + "ImmutableMap.Builder> definitions =" + + " ImmutableMap.builder();\n", + "definitions.put(\"retry_policy_1_codes\"," + + " ImmutableSet.copyOf(Lists.newArrayList(StatusCode.Code.UNAVAILABLE," + + " StatusCode.Code.UNKNOWN)));\n", + "definitions.put(\"no_retry_0_codes\"," + + " ImmutableSet.copyOf(Lists.newArrayList()));\n", + "RETRYABLE_CODE_DEFINITIONS = definitions.build();\n", + "}\n"); + assertEquals(expected, writerVisitor.write()); + } + + private static VariableExpr createRetryableCodesDefinitionsVarExpr() { + TypeNode immutableSetType = + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(ImmutableSet.class) + .setGenerics(Arrays.asList(ConcreteReference.withClazz(StatusCode.Code.class))) + .build()); + TypeNode varType = + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(ImmutableMap.class) + .setGenerics( + Arrays.asList(TypeNode.STRING, immutableSetType).stream() + .map(t -> t.reference()) + .collect(Collectors.toList())) + .build()); + return VariableExpr.withVariable( + Variable.builder().setType(varType).setName("RETRYABLE_CODE_DEFINITIONS").build()); + } + private static VariableExpr createRetryParamDefinitionsVarExpr() { TypeNode retrySettingsType = TypeNode.withReference( diff --git a/src/test/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposerTest.java b/src/test/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposerTest.java index ced1b897c7..bb73d72816 100644 --- a/src/test/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposerTest.java +++ b/src/test/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposerTest.java @@ -109,6 +109,7 @@ public void generateServiceClasses() { + "import com.google.common.collect.ImmutableList;\n" + "import com.google.common.collect.ImmutableMap;\n" + "import com.google.common.collect.ImmutableSet;\n" + + "import com.google.common.collect.Lists;\n" + "import com.google.longrunning.Operation;\n" + "import com.google.showcase.v1beta1.BlockRequest;\n" + "import com.google.showcase.v1beta1.BlockResponse;\n" @@ -352,6 +353,21 @@ public void generateServiceClasses() { + " blockSettings;\n" + " private static final ImmutableMap>\n" + " RETRYABLE_CODE_DEFINITIONS;\n" + + "\n" + + " static {\n" + + " ImmutableMap.Builder> definitions =\n" + + " ImmutableMap.builder();\n" + + " definitions.put(\n" + + " \"retry_policy_1_codes\",\n" + + " ImmutableSet.copyOf(\n" + + " Lists.newArrayList(\n" + + " StatusCode.Code.UNAVAILABLE, StatusCode.Code.UNKNOWN)));\n" + + " definitions.put(\n" + + " \"no_retry_0_codes\"," + + " ImmutableSet.copyOf(Lists.newArrayList()));\n" + + " RETRYABLE_CODE_DEFINITIONS = definitions.build();\n" + + " }\n" + + "\n" + " private static final ImmutableMap" + " RETRY_PARAM_DEFINITIONS;\n" + "\n"