From 62854af97ab2c2d7c1baec8b359fefb33e752498 Mon Sep 17 00:00:00 2001 From: summerji Date: Fri, 4 Dec 2020 18:17:32 -0800 Subject: [PATCH] rebase previous PR and address the comments --- .../composer/ServiceClientClassComposer.java | 12 +- .../ServiceClientSampleCodeComposer.java | 128 ++++++++++++- .../ServiceClientSampleCodeComposerTest.java | 181 ++++++++++++++++++ .../gapic/composer/goldens/EchoClient.golden | 10 + .../goldens/logging/ConfigClient.java | 9 +- .../goldens/logging/LoggingClient.java | 2 +- .../goldens/logging/MetricsClient.java | 2 +- .../goldens/redis/CloudRedisClient.java | 2 +- 8 files changed, 333 insertions(+), 13 deletions(-) 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 be2017ad5e..a4c13cc7b3 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 @@ -480,11 +480,11 @@ private static List createServiceMethods( Map types, Map resourceNames) { List javaMethods = new ArrayList<>(); - String clientName = getClientClassName(service); for (Method method : service.methods()) { if (method.stream().equals(Stream.NONE)) { javaMethods.addAll( - createMethodVariants(method, messageTypes, types, clientName, resourceNames)); + createMethodVariants( + method, getClientClassName(service), messageTypes, types, resourceNames)); javaMethods.add(createMethodDefaultMethod(method, types)); } if (method.hasLro()) { @@ -566,13 +566,13 @@ private static List createMethodVariants( .build(); Optional methodSampleCode = Optional.empty(); - if (!method.isPaged() && !method.hasLro()) { - // TODO(summerji): Remove the condition check once finished the implementation on paged - // sample code and lro sample code. + if (!method.hasLro()) { + // TODO(summerji): Remove the condition check once finished the implementation on lro sample + // code. methodSampleCode = Optional.of( ServiceClientSampleCodeComposer.composeRpcMethodHeaderSampleCode( - method, signature, types.get(clientName), resourceNames)); + method, signature, types.get(clientName), resourceNames, messageTypes)); } MethodDefinition.Builder methodVariantBuilder = MethodDefinition.builder() 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 33ef275e09..1c987e7618 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 @@ -16,16 +16,22 @@ import com.google.api.gax.core.FixedCredentialsProvider; import com.google.api.generator.engine.ast.AssignmentExpr; +import com.google.api.generator.engine.ast.CommentStatement; import com.google.api.generator.engine.ast.ConcreteReference; import com.google.api.generator.engine.ast.Expr; import com.google.api.generator.engine.ast.ExprStatement; +import com.google.api.generator.engine.ast.ForStatement; +import com.google.api.generator.engine.ast.LineComment; import com.google.api.generator.engine.ast.MethodInvocationExpr; +import com.google.api.generator.engine.ast.Statement; import com.google.api.generator.engine.ast.TryCatchStatement; import com.google.api.generator.engine.ast.TypeNode; import com.google.api.generator.engine.ast.VaporReference; import com.google.api.generator.engine.ast.Variable; import com.google.api.generator.engine.ast.VariableExpr; import com.google.api.generator.gapic.composer.samplecode.SampleCodeWriter; +import com.google.api.generator.gapic.model.Field; +import com.google.api.generator.gapic.model.Message; import com.google.api.generator.gapic.model.Method; import com.google.api.generator.gapic.model.MethodArgument; import com.google.api.generator.gapic.model.ResourceName; @@ -185,8 +191,24 @@ public static String composeRpcMethodHeaderSampleCode( Method method, List arguments, TypeNode clientType, - Map resourceNames) { + Map resourceNames, + Map messageTypes) { // TODO(summerji): Add other types RPC methods' sample code. + if (method.isPaged()) { + // Find the repeated field. + Message methodOutputMessage = messageTypes.get(method.outputType().reference().simpleName()); + Field repeatedPagedResultsField = methodOutputMessage.findAndUnwrapFirstRepeatedField(); + Preconditions.checkNotNull( + repeatedPagedResultsField, + String.format( + "No repeated field found on message %s for method %s", + methodOutputMessage.name(), method.name())); + + TypeNode repeatedResponseType = repeatedPagedResultsField.type(); + return SampleCodeWriter.write( + composeUnaryPagedRpcMethodSampleCode( + method, arguments, clientType, resourceNames, repeatedResponseType)); + } return SampleCodeWriter.write( composeUnaryRpcMethodSampleCode(method, arguments, clientType, resourceNames)); } @@ -289,6 +311,106 @@ public static TryCatchStatement composeUnaryRpcMethodSampleCode( .build(); } + public static TryCatchStatement composeUnaryPagedRpcMethodSampleCode( + Method method, + List arguments, + TypeNode clientType, + Map resourceNames, + TypeNode repeatedResponseType) { + VariableExpr clientVarExpr = + VariableExpr.withVariable( + Variable.builder() + .setName(JavaStyle.toLowerCamelCase(clientType.reference().name())) + .setType(clientType) + .build()); + // List of rpc method arguments' variable expressions. + List rpcMethodArgVarExprs = + arguments.stream() + .map( + arg -> + VariableExpr.withVariable( + Variable.builder() + .setName(JavaStyle.toLowerCamelCase(arg.name())) + .setType(arg.type()) + .build())) + .collect(Collectors.toList()); + // List of rpc method arguments' default value expression. + List resourceNameList = + resourceNames.values().stream().collect(Collectors.toList()); + List rpcMethodArgDefaultValueExprs = + arguments.stream() + .map( + arg -> + !isStringTypedResourceName(arg, resourceNames) + ? DefaultValueComposer.createDefaultValue(arg, resourceNames) + : MethodInvocationExpr.builder() + .setExprReferenceExpr( + DefaultValueComposer.createDefaultValue( + resourceNames.get( + arg.field().resourceReference().resourceTypeString()), + resourceNameList, + arg.field().name())) + .setMethodName("toString") + .setReturnType(TypeNode.STRING) + .build()) + .collect(Collectors.toList()); + + List bodyExprs = new ArrayList<>(); + Preconditions.checkState( + rpcMethodArgVarExprs.size() == rpcMethodArgDefaultValueExprs.size(), + "The method arguments' the number of variable expressions should equal to the number of default value expressions."); + bodyExprs.addAll( + IntStream.range(0, rpcMethodArgVarExprs.size()) + .mapToObj( + i -> + AssignmentExpr.builder() + .setVariableExpr( + ((VariableExpr) rpcMethodArgVarExprs.get(i)) + .toBuilder() + .setIsDecl(true) + .build()) + .setValueExpr(rpcMethodArgDefaultValueExprs.get(i)) + .build()) + .collect(Collectors.toList())); + // For loop paged response item on iterateAll method. + // e.g. for (LogEntry element : loggingServiceV2Client.ListLogs(parent).iterateAll()) { + // //doThingsWith(element); + // } + MethodInvocationExpr clientMethodExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(clientVarExpr) + .setMethodName(JavaStyle.toLowerCamelCase(method.name())) + .setArguments(rpcMethodArgVarExprs) + .build(); + Expr clientMethodIteratorAllExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(clientMethodExpr) + .setMethodName("iterateAll") + .setReturnType(repeatedResponseType) + .build(); + ForStatement loopIteratorStatement = + ForStatement.builder() + .setLocalVariableExpr( + VariableExpr.builder() + .setVariable( + Variable.builder().setName("element").setType(repeatedResponseType).build()) + .setIsDecl(true) + .build()) + .setCollectionExpr(clientMethodIteratorAllExpr) + .setBody(Arrays.asList(createLineCommentStatement("doThingsWith(element);"))) + .build(); + + List bodyStatements = + bodyExprs.stream().map(e -> ExprStatement.withExpr(e)).collect(Collectors.toList()); + bodyStatements.add(loopIteratorStatement); + + return TryCatchStatement.builder() + .setTryResourceExpr(assignClientVariableWithCreateMethodExpr(clientVarExpr)) + .setTryBody(bodyStatements) + .setIsSampleCode(true) + .build(); + } + // ==================================Helpers===================================================// // Assign client variable expr with create client. @@ -317,4 +439,8 @@ private static boolean isProtoEmptyType(TypeNode type) { return type.reference().pakkage().equals("com.google.protobuf") && type.reference().name().equals("Empty"); } + + private static CommentStatement createLineCommentStatement(String content) { + return CommentStatement.withComment(LineComment.withComment(content)); + } } 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 edb908d563..39f3381473 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 @@ -15,6 +15,7 @@ package com.google.api.generator.gapic.composer; import static junit.framework.Assert.assertEquals; +import static org.junit.Assert.assertThrows; import com.google.api.generator.engine.ast.ConcreteReference; import com.google.api.generator.engine.ast.Reference; @@ -22,6 +23,7 @@ import com.google.api.generator.engine.ast.VaporReference; import com.google.api.generator.gapic.composer.samplecode.SampleCodeWriter; import com.google.api.generator.gapic.model.Field; +import com.google.api.generator.gapic.model.Message; import com.google.api.generator.gapic.model.Method; import com.google.api.generator.gapic.model.MethodArgument; import com.google.api.generator.gapic.model.ResourceName; @@ -46,13 +48,85 @@ public class ServiceClientSampleCodeComposerTest { FileDescriptor echoFileDescriptor; Map resourceNames; + Map messageTypes; @Before public void setUp() { echoFileDescriptor = EchoOuterClass.getDescriptor(); resourceNames = Parser.parseResourceNames(echoFileDescriptor); + messageTypes = Parser.parseMessages(echoFileDescriptor); } + @Test + public void invalidComposeRpcMethodHeaderSampleCode_noMatchedRepeatedResponseTypeInPagedMethod() { + TypeNode inputType = + TypeNode.withReference( + VaporReference.builder().setName("EchoRequest").setPakkage(PACKAGE_NAME).build()); + TypeNode outputType = + TypeNode.withReference( + VaporReference.builder().setName("PagedResponse").setPakkage(PACKAGE_NAME).build()); + List methodArguments = Collections.emptyList(); + Method method = + Method.builder() + .setName("simplePagedMethod") + .setMethodSignatures(Arrays.asList(methodArguments)) + .setInputType(inputType) + .setOutputType(outputType) + .setIsPaged(true) + .build(); + assertThrows( + NullPointerException.class, + () -> + ServiceClientSampleCodeComposer.composeRpcMethodHeaderSampleCode( + method, methodArguments, clientType, resourceNames, messageTypes)); + } + + @Test + public void invalidComposeRpcMethodHeaderSampleCode_noRepeatedResponseTypeInPagedMethod() { + TypeNode inputType = + TypeNode.withReference( + VaporReference.builder().setName("EchoRequest").setPakkage(PACKAGE_NAME).build()); + TypeNode outputType = + TypeNode.withReference( + VaporReference.builder().setName("PagedResponse").setPakkage(PACKAGE_NAME).build()); + List methodArguments = Collections.emptyList(); + Method method = + Method.builder() + .setName("simplePagedMethod") + .setMethodSignatures(Arrays.asList(methodArguments)) + .setInputType(inputType) + .setOutputType(outputType) + .setIsPaged(true) + .build(); + Field responseField = + Field.builder() + .setName("response") + .setType( + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(List.class) + .setGenerics(ConcreteReference.withClazz(String.class)) + .build())) + .setIsMessage(true) + .setIsRepeated(false) + .build(); + Field nextPageToken = + Field.builder().setName("next_page_token").setType(TypeNode.STRING).build(); + Message noRepeatedFiledMessage = + Message.builder() + .setName("PagedResponse") + .setType(outputType) + .setFields(Arrays.asList(responseField, nextPageToken)) + .build(); + messageTypes.put("PagedResponse", noRepeatedFiledMessage); + assertThrows( + NullPointerException.class, + () -> + ServiceClientSampleCodeComposer.composeRpcMethodHeaderSampleCode( + method, methodArguments, clientType, resourceNames, messageTypes)); + } + + // ==========================================Unary RPC Method Sample Code=======================// @Test public void composeUnaryRpcMethodSampleCode_resourceNameMethodArgument() { TypeNode inputType = @@ -488,4 +562,111 @@ public void composeUnaryRpcMethodSampleCode_methodReturnVoid() { "}"); Assert.assertEquals(results, expected); } + + // ===================================Unary Paged RPC Method Sample Code ======================// + @Test + public void validComposeUnaryPagedRpcMethodSampleCode_multipleMethodArguments() { + TypeNode inputType = + TypeNode.withReference( + VaporReference.builder() + .setName("ListContentRequest") + .setPakkage(PACKAGE_NAME) + .build()); + TypeNode outputType = + TypeNode.withReference( + VaporReference.builder() + .setName("ListContentResponse") + .setPakkage(PACKAGE_NAME) + .build()); + TypeNode resourceNameType = + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(List.class) + .setGenerics(ConcreteReference.withClazz(String.class)) + .build()); + List arguments = + Arrays.asList( + MethodArgument.builder() + .setName("resourceName") + .setType(resourceNameType) + .setField( + Field.builder() + .setName("resourceName") + .setType(resourceNameType) + .setIsRepeated(true) + .build()) + .build(), + MethodArgument.builder() + .setName("filter") + .setType(TypeNode.STRING) + .setField(Field.builder().setName("filter").setType(TypeNode.STRING).build()) + .build()); + Method method = + Method.builder() + .setName("ListContent") + .setMethodSignatures(Arrays.asList(arguments)) + .setInputType(inputType) + .setOutputType(outputType) + .setIsPaged(true) + .build(); + TypeNode repeatedResponseType = + TypeNode.withReference( + VaporReference.builder().setName("Content").setPakkage(PACKAGE_NAME).build()); + + String results = + SampleCodeWriter.write( + ServiceClientSampleCodeComposer.composeUnaryPagedRpcMethodSampleCode( + method, arguments, clientType, resourceNames, repeatedResponseType)); + String expected = + LineFormatter.lines( + "try (EchoClient echoClient = EchoClient.create()) {\n", + " List resourceName = new ArrayList<>();\n", + " String filter = \"filter-1274492040\";\n", + " for (Content element : echoClient.listContent(resourceName, filter).iterateAll()) {\n", + " // doThingsWith(element);\n", + " }\n", + "}"); + Assert.assertEquals(results, expected); + } + + @Test + public void validComposeUnaryPagedRpcMethodSampleCode_noMethodArguments() { + TypeNode inputType = + TypeNode.withReference( + VaporReference.builder() + .setName("ListContentRequest") + .setPakkage(PACKAGE_NAME) + .build()); + TypeNode outputType = + TypeNode.withReference( + VaporReference.builder() + .setName("ListContentResponse") + .setPakkage(PACKAGE_NAME) + .build()); + List arguments = Collections.emptyList(); + Method method = + Method.builder() + .setName("ListContent") + .setMethodSignatures(Arrays.asList(arguments)) + .setInputType(inputType) + .setOutputType(outputType) + .setIsPaged(true) + .build(); + TypeNode repeatedResponseType = + TypeNode.withReference( + VaporReference.builder().setName("Content").setPakkage(PACKAGE_NAME).build()); + + String results = + SampleCodeWriter.write( + ServiceClientSampleCodeComposer.composeUnaryPagedRpcMethodSampleCode( + method, arguments, clientType, resourceNames, repeatedResponseType)); + String expected = + LineFormatter.lines( + "try (EchoClient echoClient = EchoClient.create()) {\n", + " for (Content element : echoClient.listContent().iterateAll()) {\n", + " // doThingsWith(element);\n", + " }\n", + "}"); + Assert.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 cedfec7ba7..7adc8fa3af 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 @@ -364,6 +364,16 @@ public class EchoClient implements BackgroundResource { // AUTO-GENERATED DOCUMENTATION AND METHOD. /** + * Sample code: + * + *
{@code
+   * try (EchoClient echoClient = EchoClient.create()) {
+   *   for (EchoResponse element : echoClient.simplePagedExpand().iterateAll()) {
+   *     // doThingsWith(element);
+   *   }
+   * }
+   * }
+ * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ diff --git a/test/integration/goldens/logging/ConfigClient.java b/test/integration/goldens/logging/ConfigClient.java index 2701af8fed..2134ba14e4 100644 --- a/test/integration/goldens/logging/ConfigClient.java +++ b/test/integration/goldens/logging/ConfigClient.java @@ -310,7 +310,9 @@ public final ListBucketsPagedResponse listBuckets(OrganizationLocationName paren * *
{@code
    * try (ConfigClient configClient = ConfigClient.create()) {
-   *   String parent = "parent-995424086";
+   *   String parent =
+   *       LogBucketName.ofProjectLocationBucketName("[PROJECT]", "[LOCATION]", "[BUCKET]")
+   *           .toString();
    *   for (LogBucket element : configClient.listBuckets(parent).iterateAll()) {
    *     // doThingsWith(element);
    *   }
@@ -543,7 +545,7 @@ public final ListSinksPagedResponse listSinks(ProjectName parent) {
    *
    * 
{@code
    * try (ConfigClient configClient = ConfigClient.create()) {
-   *   String parent = "parent-995424086";
+   *   String parent = LogSinkName.ofProjectSinkName("[PROJECT]", "[SINK]").toString();
    *   for (LogSink element : configClient.listSinks(parent).iterateAll()) {
    *     // doThingsWith(element);
    *   }
@@ -1263,7 +1265,8 @@ public final ListExclusionsPagedResponse listExclusions(ProjectName parent) {
    *
    * 
{@code
    * try (ConfigClient configClient = ConfigClient.create()) {
-   *   String parent = "parent-995424086";
+   *   String parent =
+   *       LogExclusionName.ofProjectExclusionName("[PROJECT]", "[EXCLUSION]").toString();
    *   for (LogExclusion element : configClient.listExclusions(parent).iterateAll()) {
    *     // doThingsWith(element);
    *   }
diff --git a/test/integration/goldens/logging/LoggingClient.java b/test/integration/goldens/logging/LoggingClient.java
index 41f0e2e5e4..fc29e9aafe 100644
--- a/test/integration/goldens/logging/LoggingClient.java
+++ b/test/integration/goldens/logging/LoggingClient.java
@@ -669,7 +669,7 @@ public final ListLogsPagedResponse listLogs(ProjectName parent) {
    *
    * 
{@code
    * try (LoggingClient loggingClient = LoggingClient.create()) {
-   *   String parent = "parent-995424086";
+   *   String parent = LogName.ofProjectLogName("[PROJECT]", "[LOG]").toString();
    *   for (String element : loggingClient.listLogs(parent).iterateAll()) {
    *     // doThingsWith(element);
    *   }
diff --git a/test/integration/goldens/logging/MetricsClient.java b/test/integration/goldens/logging/MetricsClient.java
index e3a50c3478..55de0c6d40 100644
--- a/test/integration/goldens/logging/MetricsClient.java
+++ b/test/integration/goldens/logging/MetricsClient.java
@@ -184,7 +184,7 @@ public final ListLogMetricsPagedResponse listLogMetrics(ProjectName parent) {
    *
    * 
{@code
    * try (MetricsClient metricsClient = MetricsClient.create()) {
-   *   String parent = "parent-995424086";
+   *   String parent = ProjectName.of("[PROJECT]").toString();
    *   for (LogMetric element : metricsClient.listLogMetrics(parent).iterateAll()) {
    *     // doThingsWith(element);
    *   }
diff --git a/test/integration/goldens/redis/CloudRedisClient.java b/test/integration/goldens/redis/CloudRedisClient.java
index 010dc5e60b..45f8e4518f 100644
--- a/test/integration/goldens/redis/CloudRedisClient.java
+++ b/test/integration/goldens/redis/CloudRedisClient.java
@@ -231,7 +231,7 @@ public final ListInstancesPagedResponse listInstances(LocationName parent) {
    *
    * 
{@code
    * try (CloudRedisClient cloudRedisClient = CloudRedisClient.create()) {
-   *   String parent = "parent-995424086";
+   *   String parent = LocationName.of("[PROJECT]", "[LOCATION]").toString();
    *   for (Instance element : cloudRedisClient.listInstances(parent).iterateAll()) {
    *     // doThingsWith(element);
    *   }