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 67746dec2a..272aeb6fb1 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 @@ -129,7 +129,8 @@ public GapicClass generate( ClassDefinition classDef = ClassDefinition.builder() - .setHeaderCommentStatements(createClassHeaderComments(service, typeStore)) + .setHeaderCommentStatements( + createClassHeaderComments(service, typeStore, resourceNames, messageTypes)) .setPackageString(pakkage) .setAnnotations(createClassAnnotations(pakkage, typeStore)) .setScope(ScopeNode.PUBLIC) @@ -161,9 +162,15 @@ private static List createClassImplements(TypeStore typeStore) { } private static List createClassHeaderComments( - Service service, TypeStore typeStore) { + Service service, + TypeStore typeStore, + Map resourceNames, + Map messageTypes) { TypeNode clientType = typeStore.get(ClassNames.getServiceClientClassName(service)); TypeNode settingsType = typeStore.get(ClassNames.getServiceSettingsClassName(service)); + String classMethodSampleCode = + ServiceClientSampleCodeComposer.composeClassHeaderMethodSampleCode( + service, clientType, resourceNames, messageTypes); String credentialsSampleCode = ServiceClientSampleCodeComposer.composeClassHeaderCredentialsSampleCode( clientType, settingsType); @@ -171,7 +178,7 @@ private static List createClassHeaderComments( ServiceClientSampleCodeComposer.composeClassHeaderEndpointSampleCode( clientType, settingsType); return ServiceClientCommentComposer.createClassHeaderComments( - service, credentialsSampleCode, endpointSampleCode); + service, classMethodSampleCode, credentialsSampleCode, endpointSampleCode); } private static List createClassMethods( diff --git a/src/main/java/com/google/api/generator/gapic/composer/comment/ServiceClientCommentComposer.java b/src/main/java/com/google/api/generator/gapic/composer/comment/ServiceClientCommentComposer.java index 7275ef7627..004082568a 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/comment/ServiceClientCommentComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/comment/ServiceClientCommentComposer.java @@ -110,7 +110,10 @@ public class ServiceClientCommentComposer { + " operation returned by another API method call."); public static List createClassHeaderComments( - Service service, String credentialsSampleCode, String endpointSampleCode) { + Service service, + String classMethodSampleCode, + String credentialsSampleCode, + String endpointSampleCode) { JavaDocComment.Builder classHeaderJavadocBuilder = JavaDocComment.builder(); if (service.hasDescription()) { classHeaderJavadocBuilder = @@ -122,7 +125,7 @@ public static List createClassHeaderComments( // Service introduction. classHeaderJavadocBuilder.addParagraph(SERVICE_DESCRIPTION_INTRO_STRING); - // TODO(summerji): Add sample code here. + classHeaderJavadocBuilder.addSampleCode(classMethodSampleCode); // API surface description. classHeaderJavadocBuilder.addParagraph( diff --git a/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientSampleCodeComposer.java b/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientSampleCodeComposer.java index 920bb7d4cb..1045275b5a 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientSampleCodeComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientSampleCodeComposer.java @@ -50,6 +50,7 @@ import com.google.api.generator.gapic.model.Method.Stream; import com.google.api.generator.gapic.model.MethodArgument; import com.google.api.generator.gapic.model.ResourceName; +import com.google.api.generator.gapic.model.Service; import com.google.api.generator.gapic.utils.JavaStyle; import com.google.common.base.Preconditions; import com.google.common.base.Strings; @@ -64,6 +65,30 @@ public class ServiceClientSampleCodeComposer { + public static String composeClassHeaderMethodSampleCode( + Service service, + TypeNode clientType, + Map resourceNames, + Map messageTypes) { + // Use the first pure unary RPC method's sample code as showcase, if no such method exists, use + // the first method in the service's methods list. + Method method = + service.methods().stream() + .filter(m -> m.stream() == Stream.NONE && !m.hasLro() && !m.isPaged()) + .findFirst() + .orElse(service.methods().get(0)); + if (method.stream() == Stream.NONE) { + if (method.methodSignatures().isEmpty()) { + return composeRpcDefaultMethodHeaderSampleCode( + method, clientType, resourceNames, messageTypes); + } + return composeRpcMethodHeaderSampleCode( + method, clientType, method.methodSignatures().get(0), resourceNames, messageTypes); + } + return composeStreamCallableMethodHeaderSampleCode( + method, clientType, resourceNames, messageTypes); + } + public static String composeClassHeaderCredentialsSampleCode( TypeNode clientType, TypeNode settingsType) { // Initialize clientSettings with builder() method. 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 d20b9f9139..0779b4d092 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 @@ -34,6 +34,12 @@ import javax.annotation.Generated; * This class provides the ability to make remote calls to the backing service through method calls * that map to API methods. Sample code to get started: * + *
{@code
+ * try (EchoClient echoClient = EchoClient.create()) {
+ *   EchoResponse response = echoClient.echo();
+ * }
+ * }
+ * *

Note: close() needs to be called on the EchoClient object to clean up resources such as * threads. In the example above, try-with-resources is used, which automatically calls close(). * diff --git a/src/test/java/com/google/api/generator/gapic/composer/goldens/IdentityClient.golden b/src/test/java/com/google/api/generator/gapic/composer/goldens/IdentityClient.golden index 78f8912a73..16c35c6b11 100644 --- a/src/test/java/com/google/api/generator/gapic/composer/goldens/IdentityClient.golden +++ b/src/test/java/com/google/api/generator/gapic/composer/goldens/IdentityClient.golden @@ -24,6 +24,15 @@ import javax.annotation.Generated; * This class provides the ability to make remote calls to the backing service through method calls * that map to API methods. Sample code to get started: * + *

{@code
+ * try (IdentityClient identityClient = IdentityClient.create()) {
+ *   String parent = UserName.of("[USER]").toString();
+ *   String displayName = "displayName1714148973";
+ *   String email = "email96619420";
+ *   User response = identityClient.createUser(parent, displayName, email);
+ * }
+ * }
+ * *

Note: close() needs to be called on the IdentityClient object to clean up resources such as * threads. In the example above, try-with-resources is used, which automatically calls close(). * diff --git a/src/test/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientSampleCodeComposerTest.java b/src/test/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientSampleCodeComposerTest.java index ec7fdf0e4b..f42e251d89 100644 --- a/src/test/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientSampleCodeComposerTest.java +++ b/src/test/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientSampleCodeComposerTest.java @@ -30,14 +30,19 @@ import com.google.api.generator.gapic.model.MethodArgument; import com.google.api.generator.gapic.model.ResourceName; import com.google.api.generator.gapic.model.ResourceReference; +import com.google.api.generator.gapic.model.Service; import com.google.api.generator.gapic.protoparser.Parser; import com.google.api.generator.testutils.LineFormatter; import com.google.protobuf.Descriptors.FileDescriptor; +import com.google.protobuf.Descriptors.ServiceDescriptor; import com.google.showcase.v1beta1.EchoOuterClass; import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.Set; import org.junit.Test; public class ServiceClientSampleCodeComposerTest { @@ -46,6 +51,218 @@ public class ServiceClientSampleCodeComposerTest { private static final String PROTO_PACKAGE_NAME = "com.google.protobuf"; // =============================== Class Header Sample Code ===============================// + @Test + public void composeClassHeaderMethodSampleCode_unaryRpc() { + FileDescriptor echoFileDescriptor = EchoOuterClass.getDescriptor(); + Map resourceNames = Parser.parseResourceNames(echoFileDescriptor); + Map messageTypes = Parser.parseMessages(echoFileDescriptor); + ServiceDescriptor echoService = echoFileDescriptor.getServices().get(0); + Set outputResourceNames = new HashSet<>(); + List services = + Parser.parseService( + echoFileDescriptor, messageTypes, resourceNames, Optional.empty(), outputResourceNames); + Service echoProtoService = services.get(0); + TypeNode clientType = + TypeNode.withReference( + VaporReference.builder() + .setName("EchoClient") + .setPakkage(SHOWCASE_PACKAGE_NAME) + .build()); + String results = ServiceClientSampleCodeComposer.composeClassHeaderMethodSampleCode(echoProtoService, clientType, resourceNames, messageTypes); + String expected = + LineFormatter.lines( + "try (EchoClient echoClient = EchoClient.create()) {\n", + " EchoResponse response = echoClient.echo();\n", + "}"); + assertEquals(expected, results); + } + + @Test + public void composeClassHeaderMethodSampleCode_firstMethodIsNotUnaryRpc() { + FileDescriptor echoFileDescriptor = EchoOuterClass.getDescriptor(); + Map resourceNames = Parser.parseResourceNames(echoFileDescriptor); + Map messageTypes = Parser.parseMessages(echoFileDescriptor); + 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); + TypeNode ttlTypeNode = + TypeNode.withReference( + VaporReference.builder().setName("Duration").setPakkage(PROTO_PACKAGE_NAME).build()); + MethodArgument ttl = + MethodArgument.builder() + .setName("ttl") + .setType(ttlTypeNode) + .setField( + Field.builder() + .setName("ttl") + .setType(ttlTypeNode) + .setIsMessage(true) + .setIsContainedInOneof(true) + .build()) + .build(); + Method method = + Method.builder() + .setName("Wait") + .setInputType(inputType) + .setOutputType(outputType) + .setLro(lro) + .setMethodSignatures(Arrays.asList(Arrays.asList(ttl))) + .build(); + Service service = Service.builder() + .setName("Echo") + .setDefaultHost("localhost:7469") + .setOauthScopes(Arrays.asList("https://www.googleapis.com/auth/cloud-platform")) + .setPakkage(SHOWCASE_PACKAGE_NAME) + .setProtoPakkage(SHOWCASE_PACKAGE_NAME) + .setOriginalJavaPackage(SHOWCASE_PACKAGE_NAME) + .setOverriddenName("Echo") + .setMethods(Arrays.asList(method)) + .build(); + TypeNode clientType = + TypeNode.withReference( + VaporReference.builder() + .setName("EchoClient") + .setPakkage(SHOWCASE_PACKAGE_NAME) + .build()); + String results = + ServiceClientSampleCodeComposer.composeClassHeaderMethodSampleCode( + service, clientType, resourceNames, messageTypes); + String expected = + LineFormatter.lines( + "try (EchoClient echoClient = EchoClient.create()) {\n", + " Duration ttl = Duration.newBuilder().build();\n", + " WaitResponse response = echoClient.waitAsync(ttl).get();\n", + "}"); + assertEquals(results, expected); + } + + @Test + public void composeClassHeaderMethodSampleCode_firstMethodHasNoSignatures() { + FileDescriptor echoFileDescriptor = EchoOuterClass.getDescriptor(); + Map resourceNames = Parser.parseResourceNames(echoFileDescriptor); + Map messageTypes = Parser.parseMessages(echoFileDescriptor); + TypeNode inputType = + TypeNode.withReference( + VaporReference.builder() + .setName("EchoRequest") + .setPakkage(SHOWCASE_PACKAGE_NAME) + .build()); + TypeNode outputType = + TypeNode.withReference( + VaporReference.builder().setName("EchoResponse").setPakkage(SHOWCASE_PACKAGE_NAME).build()); + Method method = + Method.builder() + .setName("Echo") + .setInputType(inputType) + .setOutputType(outputType) + .setMethodSignatures(Collections.emptyList()) + .build(); + Service service = Service.builder() + .setName("Echo") + .setDefaultHost("localhost:7469") + .setOauthScopes(Arrays.asList("https://www.googleapis.com/auth/cloud-platform")) + .setPakkage(SHOWCASE_PACKAGE_NAME) + .setProtoPakkage(SHOWCASE_PACKAGE_NAME) + .setOriginalJavaPackage(SHOWCASE_PACKAGE_NAME) + .setOverriddenName("Echo") + .setMethods(Arrays.asList(method)) + .build(); + TypeNode clientType = + TypeNode.withReference( + VaporReference.builder() + .setName("EchoClient") + .setPakkage(SHOWCASE_PACKAGE_NAME) + .build()); + String results = + ServiceClientSampleCodeComposer.composeClassHeaderMethodSampleCode( + service, clientType, resourceNames, messageTypes); + String expected = + LineFormatter.lines( + "try (EchoClient echoClient = EchoClient.create()) {\n", + " EchoRequest request =\n", + " EchoRequest.newBuilder()\n", + " .setName(FoobarName.ofProjectFoobarName(\"[PROJECT]\", \"[FOOBAR]\").toString())\n", + " .setParent(FoobarName.ofProjectFoobarName(\"[PROJECT]\", \"[FOOBAR]\").toString())\n", + " .setFoobar(Foobar.newBuilder().build())\n", + " .build();\n", + " EchoResponse response = echoClient.echo(request);\n", + "}"); + assertEquals(results, expected); + } + + @Test + public void composeClassHeaderMethodSampleCode_firstMethodIsStream() { + FileDescriptor echoFileDescriptor = EchoOuterClass.getDescriptor(); + Map resourceNames = Parser.parseResourceNames(echoFileDescriptor); + Map messageTypes = Parser.parseMessages(echoFileDescriptor); + TypeNode inputType = + TypeNode.withReference( + VaporReference.builder() + .setName("ExpandRequest") + .setPakkage(SHOWCASE_PACKAGE_NAME) + .build()); + TypeNode outputType = + TypeNode.withReference( + VaporReference.builder() + .setName("EchoResponse") + .setPakkage(SHOWCASE_PACKAGE_NAME) + .build()); + Method method = + Method.builder() + .setName("Expand") + .setInputType(inputType) + .setOutputType(outputType) + .setStream(Stream.SERVER) + .build(); + Service service = Service.builder() + .setName("Echo") + .setDefaultHost("localhost:7469") + .setOauthScopes(Arrays.asList("https://www.googleapis.com/auth/cloud-platform")) + .setPakkage(SHOWCASE_PACKAGE_NAME) + .setProtoPakkage(SHOWCASE_PACKAGE_NAME) + .setOriginalJavaPackage(SHOWCASE_PACKAGE_NAME) + .setOverriddenName("Echo") + .setMethods(Arrays.asList(method)) + .build(); + TypeNode clientType = + TypeNode.withReference( + VaporReference.builder() + .setName("EchoClient") + .setPakkage(SHOWCASE_PACKAGE_NAME) + .build()); + String results = ServiceClientSampleCodeComposer.composeClassHeaderMethodSampleCode(service, clientType, resourceNames, messageTypes); + String expected = + LineFormatter.lines( + "try (EchoClient echoClient = EchoClient.create()) {\n", + " ExpandRequest request =\n", + " ExpandRequest.newBuilder().setContent(\"content951530617\").setInfo(\"info3237038\").build();\n", + " ServerStream stream = echoClient.expandCallable().call(request);\n", + " for (EchoResponse response : stream) {\n", + " // Do something when a response is received.\n", + " }\n", + "}"); + assertEquals(results, expected); + } + @Test public void composeClassHeaderCredentialsSampleCode() { TypeNode clientType = diff --git a/test/integration/goldens/asset/AssetServiceClient.java b/test/integration/goldens/asset/AssetServiceClient.java index c0b14f0bb5..81f1dcb617 100644 --- a/test/integration/goldens/asset/AssetServiceClient.java +++ b/test/integration/goldens/asset/AssetServiceClient.java @@ -46,6 +46,18 @@ *

This class provides the ability to make remote calls to the backing service through method * calls that map to API methods. Sample code to get started: * + *

{@code
+ * try (AssetServiceClient assetServiceClient = AssetServiceClient.create()) {
+ *   BatchGetAssetsHistoryRequest request =
+ *       BatchGetAssetsHistoryRequest.newBuilder()
+ *           .setParent(FeedName.ofProjectFeedName("[PROJECT]", "[FEED]").toString())
+ *           .addAllAssetNames(new ArrayList())
+ *           .setReadTimeWindow(TimeWindow.newBuilder().build())
+ *           .build();
+ *   BatchGetAssetsHistoryResponse response = assetServiceClient.batchGetAssetsHistory(request);
+ * }
+ * }
+ * *

Note: close() needs to be called on the AssetServiceClient object to clean up resources such * as threads. In the example above, try-with-resources is used, which automatically calls close(). * diff --git a/test/integration/goldens/credentials/IamCredentialsClient.java b/test/integration/goldens/credentials/IamCredentialsClient.java index 6dd22974d7..dd6d14576c 100644 --- a/test/integration/goldens/credentials/IamCredentialsClient.java +++ b/test/integration/goldens/credentials/IamCredentialsClient.java @@ -42,6 +42,17 @@ *

This class provides the ability to make remote calls to the backing service through method * calls that map to API methods. Sample code to get started: * + *

{@code
+ * try (IamCredentialsClient iamCredentialsClient = IamCredentialsClient.create()) {
+ *   ServiceAccountName name = ServiceAccountName.of("[PROJECT]", "[SERVICE_ACCOUNT]");
+ *   List delegates = new ArrayList<>();
+ *   List scope = new ArrayList<>();
+ *   Duration lifetime = Duration.newBuilder().build();
+ *   GenerateAccessTokenResponse response =
+ *       iamCredentialsClient.generateAccessToken(name, delegates, scope, lifetime);
+ * }
+ * }
+ * *

Note: close() needs to be called on the IamCredentialsClient object to clean up resources such * as threads. In the example above, try-with-resources is used, which automatically calls close(). * diff --git a/test/integration/goldens/library/LibraryServiceClient.java b/test/integration/goldens/library/LibraryServiceClient.java index 142e52a744..4b5b2ee6bc 100644 --- a/test/integration/goldens/library/LibraryServiceClient.java +++ b/test/integration/goldens/library/LibraryServiceClient.java @@ -66,6 +66,13 @@ *

This class provides the ability to make remote calls to the backing service through method * calls that map to API methods. Sample code to get started: * + *

{@code
+ * try (LibraryServiceClient libraryServiceClient = LibraryServiceClient.create()) {
+ *   Shelf shelf = Shelf.newBuilder().build();
+ *   Shelf response = libraryServiceClient.createShelf(shelf);
+ * }
+ * }
+ * *

Note: close() needs to be called on the LibraryServiceClient object to clean up resources such * as threads. In the example above, try-with-resources is used, which automatically calls close(). * diff --git a/test/integration/goldens/logging/ConfigClient.java b/test/integration/goldens/logging/ConfigClient.java index 387fe881e5..ec7a1fb4e7 100644 --- a/test/integration/goldens/logging/ConfigClient.java +++ b/test/integration/goldens/logging/ConfigClient.java @@ -75,6 +75,18 @@ *

This class provides the ability to make remote calls to the backing service through method * calls that map to API methods. Sample code to get started: * + *

{@code
+ * try (ConfigClient configClient = ConfigClient.create()) {
+ *   GetBucketRequest request =
+ *       GetBucketRequest.newBuilder()
+ *           .setName(
+ *               LogBucketName.ofProjectLocationBucketName("[PROJECT]", "[LOCATION]", "[BUCKET]")
+ *                   .toString())
+ *           .build();
+ *   LogBucket response = configClient.getBucket(request);
+ * }
+ * }
+ * *

Note: close() needs to be called on the ConfigClient object to clean up resources such as * threads. In the example above, try-with-resources is used, which automatically calls close(). * diff --git a/test/integration/goldens/logging/LoggingClient.java b/test/integration/goldens/logging/LoggingClient.java index b03977b96e..393a9faffd 100644 --- a/test/integration/goldens/logging/LoggingClient.java +++ b/test/integration/goldens/logging/LoggingClient.java @@ -60,6 +60,13 @@ *

This class provides the ability to make remote calls to the backing service through method * calls that map to API methods. Sample code to get started: * + *

{@code
+ * try (LoggingClient loggingClient = LoggingClient.create()) {
+ *   LogName logName = LogName.ofProjectLogName("[PROJECT]", "[LOG]");
+ *   loggingClient.deleteLog(logName);
+ * }
+ * }
+ * *

Note: close() needs to be called on the LoggingClient object to clean up resources such as * threads. In the example above, try-with-resources is used, which automatically calls close(). * diff --git a/test/integration/goldens/logging/MetricsClient.java b/test/integration/goldens/logging/MetricsClient.java index de3e6ab2e2..61747b266c 100644 --- a/test/integration/goldens/logging/MetricsClient.java +++ b/test/integration/goldens/logging/MetricsClient.java @@ -51,6 +51,13 @@ *

This class provides the ability to make remote calls to the backing service through method * calls that map to API methods. Sample code to get started: * + *

{@code
+ * try (MetricsClient metricsClient = MetricsClient.create()) {
+ *   LogMetricName metricName = LogMetricName.of("[PROJECT]", "[METRIC]");
+ *   LogMetric response = metricsClient.getLogMetric(metricName);
+ * }
+ * }
+ * *

Note: close() needs to be called on the MetricsClient object to clean up resources such as * threads. In the example above, try-with-resources is used, which automatically calls close(). * diff --git a/test/integration/goldens/redis/CloudRedisClient.java b/test/integration/goldens/redis/CloudRedisClient.java index 65e9aca9bb..4411ff29db 100644 --- a/test/integration/goldens/redis/CloudRedisClient.java +++ b/test/integration/goldens/redis/CloudRedisClient.java @@ -67,6 +67,13 @@ *

This class provides the ability to make remote calls to the backing service through method * calls that map to API methods. Sample code to get started: * + *

{@code
+ * try (CloudRedisClient cloudRedisClient = CloudRedisClient.create()) {
+ *   InstanceName name = InstanceName.of("[PROJECT]", "[LOCATION]", "[INSTANCE]");
+ *   Instance response = cloudRedisClient.getInstance(name);
+ * }
+ * }
+ * *

Note: close() needs to be called on the CloudRedisClient object to clean up resources such as * threads. In the example above, try-with-resources is used, which automatically calls close(). *