From 15da1d015696faf6ca18cf3ebe05d3f87faa889d Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Fri, 28 Aug 2020 22:02:19 -0700 Subject: [PATCH] [ggj][codegen] feat: add LRO to ServiceStubSettings.Builder.initDefaults (#240) * feat: add factory var decl in ServiceStubSettings codegen * fix: prevent duplicate MethodDefinition annotations * feat: add descriptor fields to ServiceStubSettings codegen * feat: add starter Builder to ServiceStubSettings codegen * feat: add settings.builder decls to ServiceStubSettings codegen * feat: add first nested ctors to ServiceStubSettings codegen * feat: add GapicServiceConfig DS and processing * feat: integrate GapicServiceConfig into GapicContext, Parser, Composer * feat: initial param block, RetrySettingsComposer, test * fix!: refactor GapicRetrySettings * fix: recognize 1. or .1 double patterns * feat: support BlockStatement in ClassDef stmts * feat: add params block to ServiceStubSettings codegen * feat: add codes def to ServiceStubSettings codegen * feat: add initDefaults() to ServiceStubSettings codegen * feat: add LRO to ServiceStubSettings.Builder.initDefaults --- .../gapic/composer/RetrySettingsComposer.java | 293 +++++++++++++++--- .../ServiceStubSettingsClassComposer.java | 13 + .../composer/RetrySettingsComposerTest.java | 54 +++- .../ServiceStubSettingsClassComposerTest.java | 30 ++ 4 files changed, 350 insertions(+), 40 deletions(-) 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 2519532fb1..09c4eea906 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 @@ -14,8 +14,12 @@ package com.google.api.generator.gapic.composer; +import com.google.api.gax.grpc.ProtoOperationTransformers; +import com.google.api.gax.longrunning.OperationSnapshot; +import com.google.api.gax.longrunning.OperationTimedPollAlgorithm; import com.google.api.gax.retrying.RetrySettings; import com.google.api.gax.rpc.StatusCode; +import com.google.api.gax.rpc.UnaryCallSettings; import com.google.api.generator.engine.ast.AssignmentExpr; import com.google.api.generator.engine.ast.BlockStatement; import com.google.api.generator.engine.ast.ConcreteReference; @@ -56,10 +60,12 @@ public class RetrySettingsComposer { TypeNode.withReference(ConcreteReference.withClazz(StatusCode.Code.class)); // TODO(miraleung): Determine defaults here. - private static final long LRO_DEFAULT_INITIAL_POLL_DELAY_MILLIS = 9999; - private static final long LRO_DEFAULT_POLL_DELAY_MULTIPLIER = 1; - private static final long LRO_DEFAULT_MAX_POLL_DELAY_MILLIS = 8888; - private static final long LRO_DEFAULT_TOTAL_POLL_TIMEOUT_MILLIS = 7777; + // Default values for LongRunningConfig fields. + private static final long LRO_DEFAULT_INITIAL_POLL_DELAY_MILLIS = 500; + private static final double LRO_DEFAULT_POLL_DELAY_MULTIPLIER = 1.5; + private static final long LRO_DEFAULT_MAX_POLL_DELAY_MILLIS = 5000; + private static final long LRO_DEFAULT_TOTAL_POLL_TIMEOUT_MILLS = 300000; + private static final double LRO_DEFAULT_MAX_RPC_TIMEOUT = 1.0; public static BlockStatement createRetryParamDefinitionsBlock( Service service, @@ -231,6 +237,134 @@ public static Expr createSimpleBuilderSettingsExpr( return builderSettingsExpr; } + public static Expr createLroSettingsBuilderExpr( + Service service, + GapicServiceConfig serviceConfig, + Method method, + VariableExpr builderVarExpr, + VariableExpr retryableCodeDefsVarExpr, + VariableExpr retryParamDefsVarExpr) { + Preconditions.checkState( + method.hasLro(), + String.format( + "Tried to create LRO settings initialization for non-LRO method %s", method.name())); + + String codeName = serviceConfig.getRetryCodeName(service, method); + String retryParamName = serviceConfig.getRetryParamsName(service, method); + String settingsGetterMethodName = + String.format("%sOperationSettings", JavaStyle.toLowerCamelCase(method.name())); + + Function strValExprFn = + s -> ValueExpr.withValue(StringObjectValue.withValue(s)); + + // Argument for setInitialCallSettings. + Expr unaryCallSettingsExpr = + MethodInvocationExpr.builder() + .setStaticReferenceType(STATIC_TYPES.get("UnaryCallSettings")) + .setGenerics( + Arrays.asList( + method.inputType().reference(), + STATIC_TYPES.get("OperationSnapshot").reference())) + .setMethodName("newUnaryCallSettingsBuilder") + .build(); + unaryCallSettingsExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(unaryCallSettingsExpr) + .setMethodName("setRetryableCodes") + .setArguments( + MethodInvocationExpr.builder() + .setExprReferenceExpr(retryableCodeDefsVarExpr) + .setMethodName("get") + .setArguments(strValExprFn.apply(codeName)) + .build()) + .build(); + unaryCallSettingsExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(unaryCallSettingsExpr) + .setMethodName("setRetrySettings") + .setArguments( + MethodInvocationExpr.builder() + .setExprReferenceExpr(retryParamDefsVarExpr) + .setMethodName("get") + .setArguments(strValExprFn.apply(retryParamName)) + .build()) + .build(); + unaryCallSettingsExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(unaryCallSettingsExpr) + .setMethodName("build") + .build(); + + Expr builderSettingsExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(builderVarExpr) + .setMethodName(settingsGetterMethodName) + .build(); + builderSettingsExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(builderSettingsExpr) + .setMethodName("setInitialCallSettings") + .setArguments(unaryCallSettingsExpr) + .build(); + + Function classFieldRefFn = + t -> + VariableExpr.builder() + .setVariable( + Variable.builder() + .setType(TypeNode.withReference(ConcreteReference.withClazz(Class.class))) + .setName("class") + .build()) + .setStaticReferenceType(t) + .build(); + builderSettingsExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(builderSettingsExpr) + .setMethodName("setResponseTransformer") + .setArguments( + MethodInvocationExpr.builder() + .setStaticReferenceType( + TypeNode.withReference( + ConcreteReference.withClazz( + ProtoOperationTransformers.ResponseTransformer.class))) + .setMethodName("create") + .setArguments(classFieldRefFn.apply(method.lro().responseType())) + .build()) + .build(); + builderSettingsExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(builderSettingsExpr) + .setMethodName("setMetadataTransformer") + .setArguments( + MethodInvocationExpr.builder() + .setStaticReferenceType( + TypeNode.withReference( + ConcreteReference.withClazz( + ProtoOperationTransformers.MetadataTransformer.class))) + .setMethodName("create") + .setArguments(classFieldRefFn.apply(method.lro().metadataType())) + .build()) + .build(); + + // TODO(miraleung): Determine fianl LRO settings values here. + Expr lroRetrySettingsExpr = createLroRetrySettingsExpr(); + Expr pollAlgoExpr = + MethodInvocationExpr.builder() + .setStaticReferenceType(STATIC_TYPES.get("OperationTimedPollAlgorithm")) + .setMethodName("create") + .setArguments(lroRetrySettingsExpr) + .build(); + + builderSettingsExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(builderSettingsExpr) + .setMethodName("setPollingAlgorithm") + .setArguments(pollAlgoExpr) + .build(); + + return builderSettingsExpr; + } + private static Expr createRetryCodeDefinitionExpr( String codeName, List retryCodes, VariableExpr definitionsVarExpr) { // Construct something like `definitions.put("code_name", @@ -264,28 +398,6 @@ private static List createRetrySettingsExprs( 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")) @@ -301,16 +413,14 @@ private static List createRetrySettingsExprs( MethodInvocationExpr.builder() .setExprReferenceExpr(settingsBuilderExpr) .setMethodName("setInitialRetryDelay") - .setArguments( - durationMillisMethodFn.apply( - durationToMillisValExprFn.apply(retryPolicy.getInitialBackoff()))) + .setArguments(createDurationOfMillisExpr(toValExpr(retryPolicy.getInitialBackoff()))) .build(); settingsBuilderExpr = MethodInvocationExpr.builder() .setExprReferenceExpr(settingsBuilderExpr) .setMethodName("setRetryDelayMultiplier") - .setArguments(floatToValExprFn.apply(retryPolicy.getBackoffMultiplier())) + .setArguments(toValExpr(retryPolicy.getBackoffMultiplier())) .build(); Preconditions.checkState( @@ -320,9 +430,7 @@ private static List createRetrySettingsExprs( MethodInvocationExpr.builder() .setExprReferenceExpr(settingsBuilderExpr) .setMethodName("setMaxRetryDelay") - .setArguments( - durationMillisMethodFn.apply( - durationToMillisValExprFn.apply(retryPolicy.getMaxBackoff()))) + .setArguments(createDurationOfMillisExpr(toValExpr(retryPolicy.getMaxBackoff()))) .build(); } @@ -331,8 +439,7 @@ private static List createRetrySettingsExprs( MethodInvocationExpr.builder() .setExprReferenceExpr(settingsBuilderExpr) .setMethodName("setInitialRpcTimeout") - .setArguments( - durationMillisMethodFn.apply(durationToMillisValExprFn.apply(settings.timeout()))) + .setArguments(createDurationOfMillisExpr(toValExpr(settings.timeout()))) .build(); } @@ -352,9 +459,7 @@ private static List createRetrySettingsExprs( MethodInvocationExpr.builder() .setExprReferenceExpr(settingsBuilderExpr) .setMethodName(setterMethodName) - .setArguments( - durationMillisMethodFn.apply( - durationToMillisValExprFn.apply(settings.timeout()))) + .setArguments(createDurationOfMillisExpr(toValExpr(settings.timeout()))) .build(); } } @@ -383,10 +488,115 @@ private static List createRetrySettingsExprs( return Arrays.asList(settingsAssignExpr, definitionsPutExpr); } + private static Expr createLroRetrySettingsExpr() { + // TODO(miraleung): Determine fianl LRO settings values here. + Expr lroRetrySettingsExpr = + MethodInvocationExpr.builder() + .setStaticReferenceType(STATIC_TYPES.get("RetrySettings")) + .setMethodName("newBuilder") + .build(); + + lroRetrySettingsExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(lroRetrySettingsExpr) + .setMethodName("setInitialRetryDelay") + .setArguments( + createDurationOfMillisExpr(toValExpr(LRO_DEFAULT_INITIAL_POLL_DELAY_MILLIS))) + .build(); + + lroRetrySettingsExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(lroRetrySettingsExpr) + .setMethodName("setRetryDelayMultiplier") + .setArguments(toValExpr(LRO_DEFAULT_POLL_DELAY_MULTIPLIER)) + .build(); + + lroRetrySettingsExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(lroRetrySettingsExpr) + .setMethodName("setMaxRetryDelay") + .setArguments(createDurationOfMillisExpr(toValExpr(LRO_DEFAULT_MAX_POLL_DELAY_MILLIS))) + .build(); + + Expr zeroDurationExpr = + EnumRefExpr.builder().setType(STATIC_TYPES.get("Duration")).setName("ZERO").build(); + // TODO(miraleung): Find a way to add an "// ignored" comment here. + lroRetrySettingsExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(lroRetrySettingsExpr) + .setMethodName("setInitialRpcTimeout") + .setArguments(zeroDurationExpr) + .build(); + + // TODO(miraleung): Find a way to add an "// ignored" comment here. + lroRetrySettingsExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(lroRetrySettingsExpr) + .setMethodName("setRpcTimeoutMultiplier") + .setArguments(toValExpr(LRO_DEFAULT_MAX_RPC_TIMEOUT)) + .build(); + + // TODO(miraleung): Find a way to add an "// ignored" comment here. + lroRetrySettingsExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(lroRetrySettingsExpr) + .setMethodName("setMaxRpcTimeout") + .setArguments(zeroDurationExpr) + .build(); + + lroRetrySettingsExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(lroRetrySettingsExpr) + .setMethodName("setTotalTimeout") + .setArguments( + createDurationOfMillisExpr(toValExpr(LRO_DEFAULT_TOTAL_POLL_TIMEOUT_MILLS))) + .build(); + + lroRetrySettingsExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(lroRetrySettingsExpr) + .setMethodName("build") + .build(); + + return lroRetrySettingsExpr; + } + private static EnumRefExpr toStatusCodeEnumRefExpr(Code code) { return EnumRefExpr.builder().setType(STATUS_CODE_CODE_TYPE).setName(code.name()).build(); } + private static ValueExpr toValExpr(long longValue) { + return ValueExpr.withValue( + PrimitiveValue.builder() + .setType(TypeNode.LONG) + .setValue(String.format("%dL", longValue)) + .build()); + } + + private static ValueExpr toValExpr(float floatValue) { + return toValExpr((double) floatValue); + } + + private static ValueExpr toValExpr(double val) { + return ValueExpr.withValue( + PrimitiveValue.builder() + .setType(TypeNode.DOUBLE) + .setValue(String.format("%.1f", val)) + .build()); + } + + private static ValueExpr toValExpr(Duration duration) { + return toValExpr(Durations.toMillis(duration)); + } + + private static MethodInvocationExpr createDurationOfMillisExpr(ValueExpr valExpr) { + return MethodInvocationExpr.builder() + .setStaticReferenceType(STATIC_TYPES.get("Duration")) + .setMethodName("ofMillis") + .setArguments(valExpr) + .build(); + } + private static Map createStaticTypes() { List concreteClazzes = Arrays.asList( @@ -394,8 +604,13 @@ private static Map createStaticTypes() { ImmutableMap.class, ImmutableSet.class, Lists.class, + OperationSnapshot.class, + OperationTimedPollAlgorithm.class, + ProtoOperationTransformers.class, RetrySettings.class, - StatusCode.class); + StatusCode.class, + UnaryCallSettings.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 eceb426fe2..a511558462 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 @@ -1193,6 +1193,19 @@ private static MethodDefinition createNestedClassInitDefaultsMethod( NESTED_RETRYABLE_CODE_DEFINITIONS_VAR_EXPR, NESTED_RETRY_PARAM_DEFINITIONS_VAR_EXPR)); } + for (Method method : service.methods()) { + if (!method.hasLro()) { + continue; + } + bodyExprs.add( + RetrySettingsComposer.createLroSettingsBuilderExpr( + service, + serviceConfig, + method, + builderVarExpr, + NESTED_RETRYABLE_CODE_DEFINITIONS_VAR_EXPR, + NESTED_RETRY_PARAM_DEFINITIONS_VAR_EXPR)); + } return MethodDefinition.builder() .setScope(ScopeNode.PRIVATE) 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 3152d2a0ff..d4aae0abb3 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 @@ -218,7 +218,7 @@ public void codesDefinitionsBlock_basic() { } @Test - public void simplerBuilderExpr_basic() { + public void simpleBuilderExpr_basic() { FileDescriptor echoFileDescriptor = EchoOuterClass.getDescriptor(); ServiceDescriptor echoServiceDescriptor = echoFileDescriptor.getServices().get(0); Map messageTypes = Parser.parseMessages(echoFileDescriptor); @@ -298,6 +298,58 @@ public void simplerBuilderExpr_basic() { assertEquals(expected, writerVisitor.write()); } + @Test + public void lroBuilderExpr() { + 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(); + + // LRO method. + Method waitMethod = findMethod(service, "Wait"); + assertThat(waitMethod).isNotNull(); + + VariableExpr builderVarExpr = createBuilderVarExpr(service); + Expr builderExpr = + RetrySettingsComposer.createLroSettingsBuilderExpr( + service, + serviceConfig, + waitMethod, + builderVarExpr, + RETRY_CODES_DEFINITIONS_VAR_EXPR, + RETRY_PARAM_DEFINITIONS_VAR_EXPR); + builderExpr.accept(writerVisitor); + String expected = + createLines( + "builder.waitOperationSettings()" + + ".setInitialCallSettings(UnaryCallSettings.newUnaryCallSettingsBuilder()" + + ".setRetryableCodes(RETRYABLE_CODE_DEFINITIONS.get(\"no_retry_0_codes\"))" + + ".setRetrySettings(RETRY_PARAM_DEFINITIONS.get(\"no_retry_0_params\")).build())" + + ".setResponseTransformer(ProtoOperationTransformers.ResponseTransformer.create(" + + "WaitResponse.class))" + + ".setMetadataTransformer(ProtoOperationTransformers.MetadataTransformer.create(" + + "WaitMetadata.class)).setPollingAlgorithm(OperationTimedPollAlgorithm.create(" + + "RetrySettings.newBuilder().setInitialRetryDelay(Duration.ofMillis(500L))" + + ".setRetryDelayMultiplier(1.5).setMaxRetryDelay(Duration.ofMillis(5000L))" + + ".setInitialRpcTimeout(Duration.ZERO).setRpcTimeoutMultiplier(1.0)" + + ".setMaxRpcTimeout(Duration.ZERO).setTotalTimeout(Duration.ofMillis(300000L))" + + ".build()))"); + assertEquals(expected, writerVisitor.write()); + } + private static Method findMethod(Service service, String methodName) { for (Method m : service.methods()) { if (m.name().equals(methodName)) { 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 661d681349..9c6c76eae8 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 @@ -90,6 +90,9 @@ public void generateServiceClasses() { + "import com.google.api.gax.grpc.GaxGrpcProperties;\n" + "import com.google.api.gax.grpc.GrpcTransportChannel;\n" + "import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider;\n" + + "import com.google.api.gax.grpc.ProtoOperationTransformers;\n" + + "import com.google.api.gax.longrunning.OperationSnapshot;\n" + + "import com.google.api.gax.longrunning.OperationTimedPollAlgorithm;\n" + "import com.google.api.gax.retrying.RetrySettings;\n" + "import com.google.api.gax.rpc.ApiCallContext;\n" + "import com.google.api.gax.rpc.ApiClientHeaderProvider;\n" @@ -446,6 +449,33 @@ public void generateServiceClasses() { + " .blockSettings()\n" + " .setRetryableCodes(RETRYABLE_CODE_DEFINITIONS.get(\"no_retry_0_codes\"))\n" + " .setRetrySettings(RETRY_PARAM_DEFINITIONS.get(\"no_retry_0_params\"));\n" + + " builder\n" + + " .waitOperationSettings()\n" + + " .setInitialCallSettings(\n" + + " UnaryCallSettings.newUnaryCallSettingsBuilder()\n" + + " " + + " .setRetryableCodes(RETRYABLE_CODE_DEFINITIONS.get(\"no_retry_0_codes\"))\n" + + " " + + " .setRetrySettings(RETRY_PARAM_DEFINITIONS.get(\"no_retry_0_params\"))\n" + + " .build())\n" + + " .setResponseTransformer(\n" + + " " + + " ProtoOperationTransformers.ResponseTransformer.create(WaitResponse.class))\n" + + " .setMetadataTransformer(\n" + + " " + + " ProtoOperationTransformers.MetadataTransformer.create(WaitMetadata.class))\n" + + " .setPollingAlgorithm(\n" + + " OperationTimedPollAlgorithm.create(\n" + + " RetrySettings.newBuilder()\n" + + " .setInitialRetryDelay(Duration.ofMillis(500L))\n" + + " .setRetryDelayMultiplier(1.5)\n" + + " .setMaxRetryDelay(Duration.ofMillis(5000L))\n" + + " .setInitialRpcTimeout(Duration.ZERO)\n" + + " .setRpcTimeoutMultiplier(1.0)\n" + + " .setMaxRpcTimeout(Duration.ZERO)\n" + + " .setTotalTimeout(Duration.ofMillis(300000L))\n" + + " .build()));\n" + " return builder;\n" + " }\n" + " }\n"