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 d1847bc155..21dc25a810 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 @@ -23,6 +23,8 @@ java_library( "@com_google_api_gax_java//jar", "@com_google_code_findbugs_jsr305//jar", "@com_google_guava_guava//jar", + "@com_google_protobuf//:protobuf_java", + "@com_google_protobuf//:protobuf_java_util", "@com_google_protobuf//java/core", "@io_grpc_java//api", "@io_grpc_java//protobuf", 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 35cffa36ba..7d43a096e9 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 @@ -22,17 +22,25 @@ import com.google.api.generator.engine.ast.ExprStatement; import com.google.api.generator.engine.ast.MethodInvocationExpr; import com.google.api.generator.engine.ast.NullObjectValue; +import com.google.api.generator.engine.ast.PrimitiveValue; +import com.google.api.generator.engine.ast.StringObjectValue; import com.google.api.generator.engine.ast.TypeNode; import com.google.api.generator.engine.ast.ValueExpr; import com.google.api.generator.engine.ast.Variable; import com.google.api.generator.engine.ast.VariableExpr; +import com.google.api.generator.gapic.model.GapicRetrySettings; import com.google.api.generator.gapic.model.GapicServiceConfig; import com.google.api.generator.gapic.model.Service; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; +import com.google.protobuf.Duration; +import com.google.protobuf.util.Durations; +import io.grpc.serviceconfig.MethodConfig.RetryPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.function.Function; import java.util.stream.Collectors; public class RetrySettingsComposer { @@ -78,7 +86,15 @@ public static BlockStatement createRetryParamDefinitionsBlock( .build()); // Build the settings object for each config. - // TODO(miraleung): Fill this out. + for (Map.Entry settingsEntry : + serviceConfig.getAllGapicRetrySettings(service).entrySet()) { + bodyExprs.addAll( + createRetrySettingsExprs( + settingsEntry.getKey(), + settingsEntry.getValue(), + settingsVarExpr, + definitionsVarExpr)); + } // Reassign the new settings. bodyExprs.add( @@ -100,6 +116,130 @@ public static BlockStatement createRetryParamDefinitionsBlock( .build(); } + private static List createRetrySettingsExprs( + String settingsName, + GapicRetrySettings settings, + VariableExpr settingsVarExpr, + VariableExpr definitionsVarExpr) { + Function durationToMillisValExprFn = + d -> + ValueExpr.withValue( + PrimitiveValue.builder() + .setType(TypeNode.LONG) + .setValue(String.format("%dL", Durations.toMillis(d))) + .build()); + Function floatToValExprFn = + f -> + ValueExpr.withValue( + PrimitiveValue.builder() + .setType(TypeNode.DOUBLE) + .setValue(String.format("%.1f", f)) + .build()); + Function durationMillisMethodFn = + v -> + MethodInvocationExpr.builder() + .setStaticReferenceType(STATIC_TYPES.get("Duration")) + .setMethodName("ofMillis") + .setArguments(v) + .build(); + + Expr settingsBuilderExpr = + MethodInvocationExpr.builder() + .setStaticReferenceType(STATIC_TYPES.get("RetrySettings")) + .setMethodName("newBuilder") + .build(); + + RetryPolicy retryPolicy = settings.retryPolicy(); + if (settings.kind().equals(GapicRetrySettings.Kind.FULL)) { + Preconditions.checkState( + retryPolicy.hasInitialBackoff(), + String.format("initialBackoff not found for setting %s", settingsName)); + settingsBuilderExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(settingsBuilderExpr) + .setMethodName("setInitialRetryDelay") + .setArguments( + durationMillisMethodFn.apply( + durationToMillisValExprFn.apply(retryPolicy.getInitialBackoff()))) + .build(); + + settingsBuilderExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(settingsBuilderExpr) + .setMethodName("setRetryDelayMultiplier") + .setArguments(floatToValExprFn.apply(retryPolicy.getBackoffMultiplier())) + .build(); + + Preconditions.checkState( + retryPolicy.hasMaxBackoff(), + String.format("maxBackoff not found for setting %s", settingsName)); + settingsBuilderExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(settingsBuilderExpr) + .setMethodName("setMaxRetryDelay") + .setArguments( + durationMillisMethodFn.apply( + durationToMillisValExprFn.apply(retryPolicy.getMaxBackoff()))) + .build(); + } + + if (!settings.kind().equals(GapicRetrySettings.Kind.NONE)) { + settingsBuilderExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(settingsBuilderExpr) + .setMethodName("setInitialRpcTimeout") + .setArguments( + durationMillisMethodFn.apply(durationToMillisValExprFn.apply(settings.timeout()))) + .build(); + } + + // This will always be done, no matter the type of the retry settings object. + settingsBuilderExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(settingsBuilderExpr) + .setMethodName("setRpcTimeoutMultiplier") + .setArguments( + ValueExpr.withValue( + PrimitiveValue.builder().setType(TypeNode.DOUBLE).setValue("1.0").build())) + .build(); + + if (!settings.kind().equals(GapicRetrySettings.Kind.NONE)) { + for (String setterMethodName : Arrays.asList("setMaxRpcTimeout", "setTotalTimeout")) { + settingsBuilderExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(settingsBuilderExpr) + .setMethodName(setterMethodName) + .setArguments( + durationMillisMethodFn.apply( + durationToMillisValExprFn.apply(settings.timeout()))) + .build(); + } + } + + settingsBuilderExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(settingsBuilderExpr) + .setMethodName("build") + .setReturnType(settingsVarExpr.type()) + .build(); + + Expr settingsAssignExpr = + AssignmentExpr.builder() + .setVariableExpr(settingsVarExpr) + .setValueExpr(settingsBuilderExpr) + .build(); + + Expr definitionsPutExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(definitionsVarExpr) + .setMethodName("put") + .setArguments( + ValueExpr.withValue(StringObjectValue.withValue(settingsName)), settingsVarExpr) + .build(); + + return Arrays.asList(settingsAssignExpr, definitionsPutExpr); + } + private static Map createStaticTypes() { List concreteClazzes = Arrays.asList(org.threeten.bp.Duration.class, ImmutableMap.class, RetrySettings.class); 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 8799ff254d..8e35e40ef9 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 @@ -113,13 +113,11 @@ public class ServiceStubSettingsClassComposer { private static final String PAGED_RESPONSE_FACTORY_PATTERN = "%s_PAGE_STR_FACT"; private static final String PAGED_RESPONSE_TYPE_NAME_PATTERN = "%sPagedResponse"; private static final String NESTED_BUILDER_CLASS_NAME = "Builder"; - private static final String NESTED_UNARY_METHOD_SETTINGS_BUILDERS_VAR_NAME = "unaryMethodSettingsBuilders"; private static final String NESTED_RETRYABLE_CODE_DEFINITIONS_VAR_NAME = "RETRYABLE_CODE_DEFINITIONS"; private static final String NESTED_RETRY_PARAM_DEFINITIONS_VAR_NAME = "RETRY_PARAM_DEFINITIONS"; - private static final String STUB_PATTERN = "%sStub"; private static final String SETTINGS_LITERAL = "Settings"; @@ -165,7 +163,8 @@ public GapicClass generate( createClassStatements( service, serviceConfig, methodSettingsMemberVarExprs, messageTypes, types)) .setMethods(createClassMethods(service, methodSettingsMemberVarExprs, types)) - .setNestedClasses(Arrays.asList(createNestedBuilderClass(service, types))) + .setNestedClasses( + Arrays.asList(createNestedBuilderClass(service, serviceConfig, types))) .build(); return GapicClass.create(GapicClass.Kind.STUB, classDef); } @@ -285,7 +284,6 @@ private static List createPagingStaticAssignExprs( // TODO(miraleung): Add a test case for several such statements. List descExprs = new ArrayList<>(); List factoryExprs = new ArrayList<>(); - for (Method method : service.methods()) { if (!method.isPaged()) { continue; @@ -309,7 +307,6 @@ private static List createPagingStaticAssignExprs( Preconditions.checkState( field != null, String.format("Null field found for message %s", pagedResponseMessage.name())); - if (field.isRepeated()) { // Field is currently a List-type. Preconditions.checkState( @@ -337,7 +334,6 @@ private static List createPagingStaticAssignExprs( .build()); String pageStrDescVarName = String.format(PAGE_STR_DESC_PATTERN, JavaStyle.toUpperSnakeCase(method.name())); - VariableExpr pagedListDescVarExpr = VariableExpr.withVariable( Variable.builder().setType(pagedListDescType).setName(pageStrDescVarName).build()); @@ -1074,7 +1070,7 @@ private static MethodDefinition createClassConstructor( } private static ClassDefinition createNestedBuilderClass( - Service service, Map types) { + Service service, GapicServiceConfig serviceConfig, Map types) { String thisClassName = getThisClassName(service.name()); TypeNode outerThisClassType = types.get(thisClassName); @@ -1102,22 +1098,25 @@ private static ClassDefinition createNestedBuilderClass( .setIsStatic(true) .setName(className) .setExtendsType(extendsType) - .setStatements(createNestedClassStatements(nestedMethodSettingsMemberVarExprs)) + .setStatements( + createNestedClassStatements(service, serviceConfig, nestedMethodSettingsMemberVarExprs)) .setMethods(createNestedClassMethods(nestedMethodSettingsMemberVarExprs, types)) .build(); } private static List createNestedClassStatements( + Service service, + GapicServiceConfig serviceConfig, Map nestedMethodSettingsMemberVarExprs) { - List varDeclExprs = new ArrayList<>(); + List exprs = new ArrayList<>(); // Declare unaryMethodSettingsBuilders. Function varDeclFn = v -> v.toBuilder().setIsDecl(true).setScope(ScopeNode.PRIVATE).setIsFinal(true).build(); - varDeclExprs.add(varDeclFn.apply(NESTED_UNARY_METHOD_SETTINGS_BUILDERS_VAR_EXPR)); + exprs.add(varDeclFn.apply(NESTED_UNARY_METHOD_SETTINGS_BUILDERS_VAR_EXPR)); // Declare all the settings fields. - varDeclExprs.addAll( + exprs.addAll( nestedMethodSettingsMemberVarExprs.values().stream() .map(v -> varDeclFn.apply(v)) .collect(Collectors.toList())); @@ -1131,12 +1130,23 @@ private static List createNestedClassStatements( .setIsStatic(true) .setIsFinal(true) .build(); - varDeclExprs.add(varStaticDeclFn.apply(NESTED_RETRYABLE_CODE_DEFINITIONS_VAR_EXPR)); + Function exprStatementFn = e -> ExprStatement.withExpr(e); + + List statements = new ArrayList<>(); + statements.addAll( + exprs.stream().map(e -> exprStatementFn.apply(e)).collect(Collectors.toList())); + statements.add( + exprStatementFn.apply((varStaticDeclFn.apply(NESTED_RETRYABLE_CODE_DEFINITIONS_VAR_EXPR)))); // Declare the RETRY_PARAM_DEFINITIONS field. - varDeclExprs.add(varStaticDeclFn.apply(NESTED_RETRY_PARAM_DEFINITIONS_VAR_EXPR)); + statements.add( + exprStatementFn.apply(varStaticDeclFn.apply(NESTED_RETRY_PARAM_DEFINITIONS_VAR_EXPR))); + + statements.add( + RetrySettingsComposer.createRetryParamDefinitionsBlock( + service, serviceConfig, NESTED_RETRY_PARAM_DEFINITIONS_VAR_EXPR)); - return varDeclExprs.stream().map(e -> ExprStatement.withExpr(e)).collect(Collectors.toList()); + return statements; } private static List createNestedClassMethods( 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 2cc9f65206..eafc028566 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 @@ -88,6 +88,8 @@ public void paramDefinitionsBlock_noConfigsFound() { "static {\n", "ImmutableMap.Builder definitions = ImmutableMap.builder();\n", "RetrySettings settings = null;\n", + "settings = RetrySettings.newBuilder().setRpcTimeoutMultiplier(1.0).build();\n", + "definitions.put(\"no_retry_params\", settings);\n", "RETRY_PARAM_DEFINITIONS = definitions.build();\n", "}\n"); assertEquals(expected, writerVisitor.write()); @@ -95,7 +97,42 @@ public void paramDefinitionsBlock_noConfigsFound() { @Test public void paramDefinitionsBlock_basic() { - // TODO(miraleung): Fill this out. + 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.createRetryParamDefinitionsBlock( + service, serviceConfig, RETRY_PARAM_DEFINITIONS_VAR_EXPR); + + paramDefinitionsBlock.accept(writerVisitor); + String expected = + createLines( + "static {\n", + "ImmutableMap.Builder definitions = ImmutableMap.builder();\n", + "RetrySettings settings = null;\n", + "settings =" + + " RetrySettings.newBuilder().setInitialRetryDelay(Duration.ofMillis(100L)).setRetryDelayMultiplier(2.0).setMaxRetryDelay(Duration.ofMillis(3000L)).setInitialRpcTimeout(Duration.ofMillis(10000L)).setRpcTimeoutMultiplier(1.0).setMaxRpcTimeout(Duration.ofMillis(10000L)).setTotalTimeout(Duration.ofMillis(10000L)).build();\n", + "definitions.put(\"retry_policy_1_params\", settings);\n", + "settings =" + + " RetrySettings.newBuilder().setInitialRpcTimeout(Duration.ofMillis(5000L)).setRpcTimeoutMultiplier(1.0).setMaxRpcTimeout(Duration.ofMillis(5000L)).setTotalTimeout(Duration.ofMillis(5000L)).build();\n", + "definitions.put(\"no_retry_0_params\", settings);\n", + "RETRY_PARAM_DEFINITIONS = definitions.build();\n", + "}\n"); + assertEquals(expected, writerVisitor.write()); } private static VariableExpr createRetryParamDefinitionsVarExpr() { 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 42851e5797..ced1b897c7 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 @@ -124,6 +124,7 @@ public void generateServiceClasses() { + "import java.util.List;\n" + "import java.util.Objects;\n" + "import javax.annotation.Generated;\n" + + "import org.threeten.bp.Duration;\n" + "\n" + "@BetaApi\n" + "@Generated(\"by gapic-generator-java\")\n" @@ -354,6 +355,32 @@ public void generateServiceClasses() { + " private static final ImmutableMap" + " RETRY_PARAM_DEFINITIONS;\n" + "\n" + + " static {\n" + + " ImmutableMap.Builder definitions =" + + " ImmutableMap.builder();\n" + + " RetrySettings settings = null;\n" + + " settings =\n" + + " RetrySettings.newBuilder()\n" + + " .setInitialRetryDelay(Duration.ofMillis(100L))\n" + + " .setRetryDelayMultiplier(2.0)\n" + + " .setMaxRetryDelay(Duration.ofMillis(3000L))\n" + + " .setInitialRpcTimeout(Duration.ofMillis(10000L))\n" + + " .setRpcTimeoutMultiplier(1.0)\n" + + " .setMaxRpcTimeout(Duration.ofMillis(10000L))\n" + + " .setTotalTimeout(Duration.ofMillis(10000L))\n" + + " .build();\n" + + " definitions.put(\"retry_policy_1_params\", settings);\n" + + " settings =\n" + + " RetrySettings.newBuilder()\n" + + " .setInitialRpcTimeout(Duration.ofMillis(5000L))\n" + + " .setRpcTimeoutMultiplier(1.0)\n" + + " .setMaxRpcTimeout(Duration.ofMillis(5000L))\n" + + " .setTotalTimeout(Duration.ofMillis(5000L))\n" + + " .build();\n" + + " definitions.put(\"no_retry_0_params\", settings);\n" + + " RETRY_PARAM_DEFINITIONS = definitions.build();\n" + + " }\n" + + "\n" + " protected Builder() {\n" + " this(((ClientContext) null));\n" + " }\n"