diff --git a/src/main/java/com/google/api/generator/gapic/composer/ServiceClientClassComposer.java b/src/main/java/com/google/api/generator/gapic/composer/ServiceClientClassComposer.java index ccd8cafaf2..1634f121b7 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/ServiceClientClassComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/ServiceClientClassComposer.java @@ -500,7 +500,8 @@ private static List createServiceMethods( resourceNames)); } if (method.hasLro()) { - javaMethods.add(createLroCallableMethod(service, method, types)); + javaMethods.add( + createLroCallableMethod(service, method, types, messageTypes, resourceNames)); } if (method.isPaged()) { javaMethods.add(createPagedCallableMethod(service, method, types)); @@ -680,25 +681,49 @@ private static MethodDefinition createMethodDefaultMethod( } private static MethodDefinition createLroCallableMethod( - Service service, Method method, Map types) { - return createCallableMethod(service, method, types, CallableMethodKind.LRO); + Service service, + Method method, + Map types, + Map messageTypes, + Map resourceNames) { + return createCallableMethod( + service, method, CallableMethodKind.LRO, types, messageTypes, resourceNames); } private static MethodDefinition createCallableMethod( Service service, Method method, Map types) { - return createCallableMethod(service, method, types, CallableMethodKind.REGULAR); + // TODO(summerji): Implement sample code for callable methods which include stream methods and + // unary methods, + // and pass in actual map of Messages and ResourceNames + return createCallableMethod( + service, + method, + CallableMethodKind.REGULAR, + types, + Collections.emptyMap(), + Collections.emptyMap()); } private static MethodDefinition createPagedCallableMethod( Service service, Method method, Map types) { - return createCallableMethod(service, method, types, CallableMethodKind.PAGED); + // TODO(summerji): Implement sample code for paged callable method and pass in actual map of + // Messages and ResourceNames + return createCallableMethod( + service, + method, + CallableMethodKind.PAGED, + types, + Collections.emptyMap(), + Collections.emptyMap()); } private static MethodDefinition createCallableMethod( Service service, Method method, + CallableMethodKind callableMethodKind, Map types, - CallableMethodKind callableMethodKind) { + Map messageTypes, + Map resourceNames) { TypeNode rawCallableReturnType = null; if (callableMethodKind.equals(CallableMethodKind.LRO)) { rawCallableReturnType = types.get("OperationCallable"); @@ -740,9 +765,22 @@ private static MethodDefinition createCallableMethod( .setReturnType(returnType) .build(); + Optional sampleCode = Optional.empty(); + // TODO (summerji): Implement sample code for CallableMethodKind.PAGED and + // CallableMethodKind.REGULAR + if (callableMethodKind.equals(CallableMethodKind.LRO)) { + sampleCode = + Optional.of( + ServiceClientSampleCodeComposer.composeLroCallableMethodHeaderSampleCode( + method, + types.get(ClassNames.getServiceClientClassName(service)), + resourceNames, + messageTypes)); + } + return MethodDefinition.builder() .setHeaderCommentStatements( - ServiceClientCommentComposer.createRpcCallableMethodHeaderComment(method)) + ServiceClientCommentComposer.createRpcCallableMethodHeaderComment(method, sampleCode)) .setScope(ScopeNode.PUBLIC) .setIsFinal(true) .setName(methodName) diff --git a/src/main/java/com/google/api/generator/gapic/composer/ServiceClientCommentComposer.java b/src/main/java/com/google/api/generator/gapic/composer/ServiceClientCommentComposer.java index c3b9158d9b..c5ee9a98d6 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/ServiceClientCommentComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/ServiceClientCommentComposer.java @@ -215,7 +215,8 @@ static CommentStatement createMethodSettingsArgComment(String serviceName) { return toSimpleComment(String.format(CREATE_METHOD_SETTINGS_ARG_PATTERN, serviceName)); } - static List createRpcCallableMethodHeaderComment(Method method) { + static List createRpcCallableMethodHeaderComment( + Method method, Optional sampleCode) { JavaDocComment.Builder methodJavadocBuilder = JavaDocComment.builder(); if (method.hasDescription()) { @@ -224,7 +225,9 @@ static List createRpcCallableMethodHeaderComment(Method method } methodJavadocBuilder.addParagraph(METHOD_DESCRIPTION_SAMPLE_CODE_SUMMARY_STRING); - // TODO(summerji): Add sample code here. + if (sampleCode.isPresent()) { + methodJavadocBuilder.addSampleCode(sampleCode.get()); + } return Arrays.asList( CommentComposer.AUTO_GENERATED_METHOD_COMMENT, diff --git a/src/main/java/com/google/api/generator/gapic/composer/ServiceClientSampleCodeComposer.java b/src/main/java/com/google/api/generator/gapic/composer/ServiceClientSampleCodeComposer.java index b60c329ab5..d26a1f129a 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/ServiceClientSampleCodeComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/ServiceClientSampleCodeComposer.java @@ -15,6 +15,7 @@ package com.google.api.generator.gapic.composer; import com.google.api.gax.core.FixedCredentialsProvider; +import com.google.api.gax.longrunning.OperationFuture; import com.google.api.generator.engine.ast.AssignmentExpr; import com.google.api.generator.engine.ast.CommentStatement; import com.google.api.generator.engine.ast.ConcreteReference; @@ -289,6 +290,114 @@ public static String composeRpcDefaultMethodHeaderSampleCode( .build()); } + public static String composeLroCallableMethodHeaderSampleCode( + Method method, + TypeNode clientType, + Map resourceNames, + Map messageTypes) { + VariableExpr clientVarExpr = + VariableExpr.withVariable( + Variable.builder() + .setName(JavaStyle.toLowerCamelCase(clientType.reference().name())) + .setType(clientType) + .build()); + // Assign method's request variable with the default value. + VariableExpr requestVarExpr = + VariableExpr.withVariable( + Variable.builder().setName("request").setType(method.inputType()).build()); + Message requestMessage = messageTypes.get(method.inputType().reference().simpleName()); + Preconditions.checkNotNull(requestMessage); + Expr requestBuilderExpr = + DefaultValueComposer.createSimpleMessageBuilderExpr( + requestMessage, resourceNames, messageTypes); + AssignmentExpr requestAssignmentExpr = + AssignmentExpr.builder() + .setVariableExpr(requestVarExpr.toBuilder().setIsDecl(true).build()) + .setValueExpr(requestBuilderExpr) + .build(); + + List bodyExprs = new ArrayList<>(); + bodyExprs.add(requestAssignmentExpr); + + // Create OperationFuture variable expr with invoking client's LRO callable method. + // e.g. OperationFuture future = + // echoClient.waitOperationCallable().futureCall(request); + TypeNode operationFutureType = + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(OperationFuture.class) + .setGenerics( + method.lro().responseType().reference(), + method.lro().metadataType().reference()) + .build()); + VariableExpr operationFutureVarExpr = + VariableExpr.withVariable( + Variable.builder().setName("future").setType(operationFutureType).build()); + MethodInvocationExpr rpcMethodInvocationExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(clientVarExpr) + .setMethodName( + String.format("%sOperationCallable", JavaStyle.toLowerCamelCase(method.name()))) + .build(); + rpcMethodInvocationExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(rpcMethodInvocationExpr) + .setMethodName("futureCall") + .setArguments(requestVarExpr) + .setReturnType(operationFutureType) + .build(); + bodyExprs.add( + AssignmentExpr.builder() + .setVariableExpr(operationFutureVarExpr.toBuilder().setIsDecl(true).build()) + .setValueExpr(rpcMethodInvocationExpr) + .build()); + + List bodyStatements = + bodyExprs.stream().map(e -> ExprStatement.withExpr(e)).collect(Collectors.toList()); + bodyExprs.clear(); + // Add a line comment + bodyStatements.add(CommentStatement.withComment(LineComment.withComment("Do something."))); + + // Assign response variable with invoking client's LRO method. + // e.g. if return void, future.get(); or, + // e.g. if return other type, WaitResponse response = future.get(); + MethodInvocationExpr futureGetMethodExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(operationFutureVarExpr) + .setMethodName("get") + .setReturnType(method.lro().responseType()) + .build(); + boolean returnVoid = isProtoEmptyType(method.lro().responseType()); + if (returnVoid) { + bodyExprs.add(futureGetMethodExpr); + } else { + VariableExpr responseVarExpr = + VariableExpr.builder() + .setVariable( + Variable.builder() + .setName("response") + .setType(method.lro().responseType()) + .build()) + .setIsDecl(true) + .build(); + bodyExprs.add( + AssignmentExpr.builder() + .setVariableExpr(responseVarExpr) + .setValueExpr(futureGetMethodExpr) + .build()); + } + bodyStatements.addAll( + bodyExprs.stream().map(e -> ExprStatement.withExpr(e)).collect(Collectors.toList())); + bodyExprs.clear(); + + return SampleCodeWriter.write( + TryCatchStatement.builder() + .setTryResourceExpr(assignClientVariableWithCreateMethodExpr(clientVarExpr)) + .setTryBody(bodyStatements) + .setIsSampleCode(true) + .build()); + } + private static List composeUnaryRpcMethodSampleCodeBodyStatements( Method method, VariableExpr clientVarExpr, @@ -385,7 +494,7 @@ private static List composeUnaryLroRpcMethodSampleCodeBodyStatements( VariableExpr clientVarExpr, List rpcMethodArgVarExprs, List bodyExprs) { - // Assign response variable with invoking client's lro method. + // Assign response variable with invoking client's LRO method. // e.g. if return void, echoClient.waitAsync(ttl).get(); or, // e.g. if return other type, WaitResponse response = echoClient.waitAsync(ttl).get(); Expr invokeLroGetMethodExpr = diff --git a/src/test/java/com/google/api/generator/gapic/composer/ServiceClientSampleCodeComposerTest.java b/src/test/java/com/google/api/generator/gapic/composer/ServiceClientSampleCodeComposerTest.java index 3e5d82a8b0..441bea4c65 100644 --- a/src/test/java/com/google/api/generator/gapic/composer/ServiceClientSampleCodeComposerTest.java +++ b/src/test/java/com/google/api/generator/gapic/composer/ServiceClientSampleCodeComposerTest.java @@ -1397,4 +1397,112 @@ public void validComposeRpcDefaultMethodHeaderSampleCode_pureUnaryReturnResponse "}"); assertEquals(results, expected); } + + // ================================LRO Callable Method Sample Code ====================// + @Test + public void validComposeLroCallableMethodHeaderSampleCode_withReturnResponse() { + FileDescriptor echoFileDescriptor = EchoOuterClass.getDescriptor(); + Map resourceNames = Parser.parseResourceNames(echoFileDescriptor); + Map messageTypes = Parser.parseMessages(echoFileDescriptor); + TypeNode clientType = + TypeNode.withReference( + VaporReference.builder() + .setName("EchoClient") + .setPakkage(SHOWCASE_PACKAGE_NAME) + .build()); + TypeNode inputType = + TypeNode.withReference( + VaporReference.builder() + .setName("WaitRequest") + .setPakkage(SHOWCASE_PACKAGE_NAME) + .build()); + TypeNode outputType = + TypeNode.withReference( + VaporReference.builder().setName("Operation").setPakkage(LRO_PACKAGE_NAME).build()); + TypeNode responseType = + TypeNode.withReference( + VaporReference.builder() + .setName("WaitResponse") + .setPakkage(SHOWCASE_PACKAGE_NAME) + .build()); + TypeNode metadataType = + TypeNode.withReference( + VaporReference.builder() + .setName("WaitMetadata") + .setPakkage(SHOWCASE_PACKAGE_NAME) + .build()); + LongrunningOperation lro = LongrunningOperation.withTypes(responseType, metadataType); + Method method = + Method.builder() + .setName("Wait") + .setInputType(inputType) + .setOutputType(outputType) + .setLro(lro) + .build(); + String results = + ServiceClientSampleCodeComposer.composeLroCallableMethodHeaderSampleCode( + method, clientType, resourceNames, messageTypes); + String expected = + LineFormatter.lines( + "try (EchoClient echoClient = EchoClient.create()) {\n", + " WaitRequest request = WaitRequest.newBuilder().build();\n", + " OperationFuture future =\n", + " echoClient.waitOperationCallable().futureCall(request);\n", + " // Do something.\n", + " WaitResponse response = future.get();\n", + "}"); + assertEquals(results, expected); + } + + @Test + public void validComposeLroCallableMethodHeaderSampleCode_withReturnVoid() { + FileDescriptor echoFileDescriptor = EchoOuterClass.getDescriptor(); + Map resourceNames = Parser.parseResourceNames(echoFileDescriptor); + Map messageTypes = Parser.parseMessages(echoFileDescriptor); + TypeNode clientType = + TypeNode.withReference( + VaporReference.builder() + .setName("EchoClient") + .setPakkage(SHOWCASE_PACKAGE_NAME) + .build()); + TypeNode inputType = + TypeNode.withReference( + VaporReference.builder() + .setName("WaitRequest") + .setPakkage(SHOWCASE_PACKAGE_NAME) + .build()); + TypeNode outputType = + TypeNode.withReference( + VaporReference.builder().setName("Operation").setPakkage(LRO_PACKAGE_NAME).build()); + TypeNode responseType = + TypeNode.withReference( + VaporReference.builder().setName("Empty").setPakkage(PROTO_PACKAGE_NAME).build()); + TypeNode metadataType = + TypeNode.withReference( + VaporReference.builder() + .setName("WaitMetadata") + .setPakkage(SHOWCASE_PACKAGE_NAME) + .build()); + LongrunningOperation lro = LongrunningOperation.withTypes(responseType, metadataType); + Method method = + Method.builder() + .setName("Wait") + .setInputType(inputType) + .setOutputType(outputType) + .setLro(lro) + .build(); + String results = + ServiceClientSampleCodeComposer.composeLroCallableMethodHeaderSampleCode( + method, clientType, resourceNames, messageTypes); + String expected = + LineFormatter.lines( + "try (EchoClient echoClient = EchoClient.create()) {\n", + " WaitRequest request = WaitRequest.newBuilder().build();\n", + " OperationFuture future =\n", + " echoClient.waitOperationCallable().futureCall(request);\n", + " // Do something.\n", + " future.get();\n", + "}"); + assertEquals(results, expected); + } } diff --git a/src/test/java/com/google/api/generator/gapic/composer/goldens/EchoClient.golden b/src/test/java/com/google/api/generator/gapic/composer/goldens/EchoClient.golden index 15d93be4eb..b9585ec489 100644 --- a/src/test/java/com/google/api/generator/gapic/composer/goldens/EchoClient.golden +++ b/src/test/java/com/google/api/generator/gapic/composer/goldens/EchoClient.golden @@ -504,7 +504,19 @@ public class EchoClient implements BackgroundResource { } // AUTO-GENERATED DOCUMENTATION AND METHOD. - /** Sample code: */ + /** + * Sample code: + * + *
{@code
+   * try (EchoClient echoClient = EchoClient.create()) {
+   *   WaitRequest request = WaitRequest.newBuilder().build();
+   *   OperationFuture future =
+   *       echoClient.waitOperationCallable().futureCall(request);
+   *   // Do something.
+   *   WaitResponse response = future.get();
+   * }
+   * }
+ */ public final OperationCallable waitOperationCallable() { return stub.waitOperationCallable(); } diff --git a/test/integration/goldens/asset/AssetServiceClient.java b/test/integration/goldens/asset/AssetServiceClient.java index 24702ea4f4..6a39beb127 100644 --- a/test/integration/goldens/asset/AssetServiceClient.java +++ b/test/integration/goldens/asset/AssetServiceClient.java @@ -202,6 +202,22 @@ public final OperationFuture exportAs * export operation usually finishes within 5 minutes. * *

Sample code: + * + *

{@code
+   * try (AssetServiceClient assetServiceClient = AssetServiceClient.create()) {
+   *   ExportAssetsRequest request =
+   *       ExportAssetsRequest.newBuilder()
+   *           .setParent(FeedName.ofProjectFeedName("[PROJECT]", "[FEED]").toString())
+   *           .setReadTime(Timestamp.newBuilder().build())
+   *           .addAllAssetTypes(new ArrayList())
+   *           .setOutputConfig(OutputConfig.newBuilder().build())
+   *           .build();
+   *   OperationFuture future =
+   *       assetServiceClient.exportAssetsOperationCallable().futureCall(request);
+   *   // Do something.
+   *   ExportAssetsResponse response = future.get();
+   * }
+   * }
*/ public final OperationCallable exportAssetsOperationCallable() { diff --git a/test/integration/goldens/redis/CloudRedisClient.java b/test/integration/goldens/redis/CloudRedisClient.java index 72ebd6d532..bf6aba5541 100644 --- a/test/integration/goldens/redis/CloudRedisClient.java +++ b/test/integration/goldens/redis/CloudRedisClient.java @@ -561,6 +561,21 @@ public final OperationFuture createInstanceAsync( * call DeleteOperation. * *

Sample code: + * + *

{@code
+   * try (CloudRedisClient cloudRedisClient = CloudRedisClient.create()) {
+   *   CreateInstanceRequest request =
+   *       CreateInstanceRequest.newBuilder()
+   *           .setParent(LocationName.of("[PROJECT]", "[LOCATION]").toString())
+   *           .setInstanceId("instanceId902024336")
+   *           .setInstance(Instance.newBuilder().build())
+   *           .build();
+   *   OperationFuture future =
+   *       cloudRedisClient.createInstanceOperationCallable().futureCall(request);
+   *   // Do something.
+   *   Instance response = future.get();
+   * }
+   * }
*/ public final OperationCallable createInstanceOperationCallable() { @@ -658,6 +673,20 @@ public final OperationFuture updateInstanceAsync( * DeleteOperation. * *

Sample code: + * + *

{@code
+   * try (CloudRedisClient cloudRedisClient = CloudRedisClient.create()) {
+   *   UpdateInstanceRequest request =
+   *       UpdateInstanceRequest.newBuilder()
+   *           .setUpdateMask(FieldMask.newBuilder().build())
+   *           .setInstance(Instance.newBuilder().build())
+   *           .build();
+   *   OperationFuture future =
+   *       cloudRedisClient.updateInstanceOperationCallable().futureCall(request);
+   *   // Do something.
+   *   Instance response = future.get();
+   * }
+   * }
*/ public final OperationCallable updateInstanceOperationCallable() { @@ -765,6 +794,20 @@ public final OperationFuture upgradeInstanceAsync( * Upgrades Redis instance to the newer Redis version specified in the request. * *

Sample code: + * + *

{@code
+   * try (CloudRedisClient cloudRedisClient = CloudRedisClient.create()) {
+   *   UpgradeInstanceRequest request =
+   *       UpgradeInstanceRequest.newBuilder()
+   *           .setName(InstanceName.of("[PROJECT]", "[LOCATION]", "[INSTANCE]").toString())
+   *           .setRedisVersion("redisVersion-1972584739")
+   *           .build();
+   *   OperationFuture future =
+   *       cloudRedisClient.upgradeInstanceOperationCallable().futureCall(request);
+   *   // Do something.
+   *   Instance response = future.get();
+   * }
+   * }
*/ public final OperationCallable upgradeInstanceOperationCallable() { @@ -856,6 +899,20 @@ public final OperationFuture importInstanceAsync( * call DeleteOperation. * *

Sample code: + * + *

{@code
+   * try (CloudRedisClient cloudRedisClient = CloudRedisClient.create()) {
+   *   ImportInstanceRequest request =
+   *       ImportInstanceRequest.newBuilder()
+   *           .setName("name3373707")
+   *           .setInputConfig(InputConfig.newBuilder().build())
+   *           .build();
+   *   OperationFuture future =
+   *       cloudRedisClient.importInstanceOperationCallable().futureCall(request);
+   *   // Do something.
+   *   Instance response = future.get();
+   * }
+   * }
*/ public final OperationCallable importInstanceOperationCallable() { @@ -950,6 +1007,20 @@ public final OperationFuture exportInstanceAsync( * call DeleteOperation. * *

Sample code: + * + *

{@code
+   * try (CloudRedisClient cloudRedisClient = CloudRedisClient.create()) {
+   *   ExportInstanceRequest request =
+   *       ExportInstanceRequest.newBuilder()
+   *           .setName("name3373707")
+   *           .setOutputConfig(OutputConfig.newBuilder().build())
+   *           .build();
+   *   OperationFuture future =
+   *       cloudRedisClient.exportInstanceOperationCallable().futureCall(request);
+   *   // Do something.
+   *   Instance response = future.get();
+   * }
+   * }
*/ public final OperationCallable exportInstanceOperationCallable() { @@ -1068,6 +1139,19 @@ public final OperationFuture failoverInstanceAsync( * Cloud Memorystore for Redis instance. * *

Sample code: + * + *

{@code
+   * try (CloudRedisClient cloudRedisClient = CloudRedisClient.create()) {
+   *   FailoverInstanceRequest request =
+   *       FailoverInstanceRequest.newBuilder()
+   *           .setName(InstanceName.of("[PROJECT]", "[LOCATION]", "[INSTANCE]").toString())
+   *           .build();
+   *   OperationFuture future =
+   *       cloudRedisClient.failoverInstanceOperationCallable().futureCall(request);
+   *   // Do something.
+   *   Instance response = future.get();
+   * }
+   * }
*/ public final OperationCallable failoverInstanceOperationCallable() { @@ -1161,6 +1245,19 @@ public final OperationFuture deleteInstanceAsync( * Deletes a specific Redis instance. Instance stops serving and data is deleted. * *

Sample code: + * + *

{@code
+   * try (CloudRedisClient cloudRedisClient = CloudRedisClient.create()) {
+   *   DeleteInstanceRequest request =
+   *       DeleteInstanceRequest.newBuilder()
+   *           .setName(InstanceName.of("[PROJECT]", "[LOCATION]", "[INSTANCE]").toString())
+   *           .build();
+   *   OperationFuture future =
+   *       cloudRedisClient.deleteInstanceOperationCallable().futureCall(request);
+   *   // Do something.
+   *   future.get();
+   * }
+   * }
*/ public final OperationCallable deleteInstanceOperationCallable() {