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 db3fdbebcf..1a0e3b8698 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 @@ -122,9 +122,7 @@ public GapicClass generate( ClassDefinition.builder() .setHeaderCommentStatements( ServiceClientCommentComposer.createClassHeaderComments( - service, - types.get(getClientClassName(service.name())), - types.get(getSettingsName(service.name())))) + service, types, resourceNames)) .setPackageString(pakkage) .setAnnotations(createClassAnnotations(types)) .setScope(ScopeNode.PUBLIC) 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 299e8ff78c..1dcd51e66c 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 @@ -110,9 +110,11 @@ class ServiceClientCommentComposer { + " operation returned by another API method call."); static List createClassHeaderComments( - Service service, TypeNode clientType, TypeNode settingsType) { - String settingsName = JavaStyle.toLowerCamelCase(getSettingsName(service.name())); - String clientName = JavaStyle.toLowerCamelCase(getClientClassName(service.name())); + Service service, Map types, Map resourcesNames) { + String clientName = getClientClassName(service.name()); + String settingsName = getSettingsName(service.name()); + TypeNode clientType = types.get(clientName); + TypeNode settingsType = types.get(settingsName); JavaDocComment.Builder classHeaderJavadocBuilder = JavaDocComment.builder(); if (service.hasDescription()) { classHeaderJavadocBuilder = @@ -124,7 +126,9 @@ static List createClassHeaderComments( // Service introduction. classHeaderJavadocBuilder.addParagraph(SERVICE_DESCRIPTION_INTRO_STRING); - // TODO(summerji): Add sample code here. + classHeaderJavadocBuilder.addSampleCode( + ServiceClientSampleCodeComposer.composeClassHeaderMethodSampleCode( + service, resourcesNames)); // API surface description. classHeaderJavadocBuilder.addParagraph( @@ -280,11 +284,11 @@ private static JavaDocComment.Builder processProtobufComment( return commentBuilder; } - private static String getSettingsName(String serviceName) { - return String.format(SETTINGS_NAME_PATTERN, serviceName); + private static String getClientClassName(String serviceName) { + return String.format("%sClient", serviceName); } - private static String getClientClassName(String serviceName) { - return String.format(CLASS_NAME_PATTERN, serviceName); + private static String getSettingsName(String serviceName) { + return String.format("%sSettings", serviceName); } } 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 d6fe1f1ad0..4f792063ba 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,33 +15,92 @@ package com.google.api.generator.gapic.composer; import com.google.api.gax.core.FixedCredentialsProvider; +import com.google.api.gax.rpc.BidiStreamingCallable; +import com.google.api.gax.rpc.ClientStreamingCallable; +import com.google.api.gax.rpc.OperationCallable; +import com.google.api.gax.rpc.ServerStreamingCallable; +import com.google.api.gax.rpc.UnaryCallable; import com.google.api.generator.engine.ast.AssignmentExpr; 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.MethodInvocationExpr; +import com.google.api.generator.engine.ast.Reference; import com.google.api.generator.engine.ast.Statement; 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.VaporReference; import com.google.api.generator.engine.ast.Variable; import com.google.api.generator.engine.ast.VariableExpr; import com.google.api.generator.engine.writer.JavaWriterVisitor; import com.google.api.generator.gapic.composer.samplecode.SampleCodeJavaFormatter; import com.google.api.generator.gapic.composer.samplecode.SampleCodeWriter; import com.google.api.generator.gapic.model.Method; +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 java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class ServiceClientSampleCodeComposer { + + private static final String PAGED_RESPONSE_TYPE_NAME_PATTERN = "%sPagedResponse"; // TODO(summerji): Add unit tests for ServiceClientSampleCodeComposer. // TODO(summerji): Refactor signatures as sample code context. + public static String composeClassHeaderMethodSampleCode( + Service service, Map resourceNames) { + // 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. + Map types = createDynamicTypes(service); + TypeNode clientType = types.get(getClientName(service.name())); + Method method = + service.methods().stream() + .filter(m -> m.stream() == Stream.NONE && !m.hasLro() && !m.isPaged()) + .findFirst() + .orElse(service.methods().get(0)); + // If variant method signatures exists, use the first one. + List arguments = + method.methodSignatures().isEmpty() + ? Collections.emptyList() + : method.methodSignatures().get(0); + if (method.stream() == Stream.NONE) { + return SampleCodeWriter.write( + SampleCodeHelperComposer.composeRpcMethodSampleCode( + method, arguments, clientType, resourceNames)); + } + + TypeNode rawCallableReturnType = null; + if (method.hasLro()) { + rawCallableReturnType = types.get("OperationCallable"); + } else if (method.stream() == Stream.CLIENT) { + rawCallableReturnType = types.get("ClientStreamingCallable"); + } else if (method.stream() == Stream.SERVER) { + rawCallableReturnType = types.get("ServerStreamingCallable"); + } else if (method.stream() == Stream.BIDI) { + rawCallableReturnType = types.get("BidiStreamingCallable"); + } else { + rawCallableReturnType = types.get("UnaryCallable"); + } + + // Set generics. + TypeNode returnType = + TypeNode.withReference( + rawCallableReturnType + .reference() + .copyAndSetGenerics(getGenericsForCallable(method, types))); + return SampleCodeWriter.write( + SampleCodeHelperComposer.composeRpcCallableMethodSampleCode( + method, clientType, returnType, resourceNames)); + } + public static String composeClassHeaderCredentialsSampleCode( TypeNode clientType, TypeNode settingsType) { // Initialize clientSettings with builder() method. @@ -186,4 +245,67 @@ private static VariableExpr createVariableExpr(String variableName, TypeNode typ return VariableExpr.withVariable( Variable.builder().setName(variableName).setType(type).build()); } + + private static List getGenericsForCallable( + Method method, Map types) { + if (method.hasLro()) { + return Arrays.asList( + method.inputType().reference(), + method.lro().responseType().reference(), + method.lro().metadataType().reference()); + } + if (method.isPaged()) { + return Arrays.asList( + method.inputType().reference(), + types.get(String.format(PAGED_RESPONSE_TYPE_NAME_PATTERN, method.name())).reference()); + } + return Arrays.asList(method.inputType().reference(), method.outputType().reference()); + } + + private static Map createDynamicTypes(Service service) { + Map dynamicTypes = new HashMap<>(); + dynamicTypes.putAll(createConcreteTypes()); + dynamicTypes.put( + getClientName(service.name()), + TypeNode.withReference( + VaporReference.builder() + .setName(getClientName(service.name())) + .setPakkage(service.pakkage()) + .build())); + // Pagination types. + dynamicTypes.putAll( + service.methods().stream() + .filter(m -> m.isPaged()) + .collect( + Collectors.toMap( + m -> String.format(PAGED_RESPONSE_TYPE_NAME_PATTERN, m.name()), + m -> + TypeNode.withReference( + VaporReference.builder() + .setName(String.format(PAGED_RESPONSE_TYPE_NAME_PATTERN, m.name())) + .setPakkage(service.pakkage()) + .setEnclosingClassNames(getClientName(service.name())) + .setIsStaticImport(true) + .build())))); + return dynamicTypes; + } + + private static Map createConcreteTypes() { + List concreteClazzes = + Arrays.asList( + BidiStreamingCallable.class, + ClientStreamingCallable.class, + ServerStreamingCallable.class, + OperationCallable.class, + UnaryCallable.class); + return concreteClazzes.stream() + .collect( + Collectors.toMap( + c -> c.getSimpleName(), + c -> TypeNode.withReference(ConcreteReference.withClazz(c)))); + } + + private static String getClientName(String serviceName) { + return String.format("%sClient", serviceName); + } } 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 0149f76652..a090c77ab7 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 @@ -35,6 +35,13 @@ 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()) {
+ *   ResourceName parent = FoobarName.ofProjectFoobarName("[PROJECT]", "[FOOBAR]");
+ *   EchoResponse response = echoClient.Echo(parent);
+ * }
+ * }
+ * *

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 3cad790404..928c45dc2f 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 @@ -25,6 +25,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 = "parent-995424086";
+ *   String display_name = "display_name1615086568";
+ *   String email = "email96619420";
+ *   User response = identityClient.CreateUser(parent, display_name, 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/test/integration/goldens/asset/AssetServiceClient.java b/test/integration/goldens/asset/AssetServiceClient.java index bff5c6105f..a0f63562e5 100644 --- a/test/integration/goldens/asset/AssetServiceClient.java +++ b/test/integration/goldens/asset/AssetServiceClient.java @@ -47,6 +47,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 (AssetServiceClient assetServiceClient = AssetServiceClient.create()) {
+ *   BatchGetAssetsHistoryRequest request = BatchGetAssetsHistoryRequest.newBuilder().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/logging/ConfigServiceV2Client.java b/test/integration/goldens/logging/ConfigServiceV2Client.java index 23da46e48e..de08145a0b 100644 --- a/test/integration/goldens/logging/ConfigServiceV2Client.java +++ b/test/integration/goldens/logging/ConfigServiceV2Client.java @@ -44,6 +44,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 (ConfigServiceV2Client configServiceV2Client = ConfigServiceV2Client.create()) {
+ *   GetBucketRequest request = GetBucketRequest.newBuilder().build();
+ *   LogBucket response = configServiceV2Client.GetBucket(request);
+ * }
+ * }
+ * *

Note: close() needs to be called on the configServiceV2Client 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/LoggingServiceV2Client.java b/test/integration/goldens/logging/LoggingServiceV2Client.java index 6e7fd562e5..c326354830 100644 --- a/test/integration/goldens/logging/LoggingServiceV2Client.java +++ b/test/integration/goldens/logging/LoggingServiceV2Client.java @@ -46,6 +46,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 (LoggingServiceV2Client loggingServiceV2Client = LoggingServiceV2Client.create()) {
+ *   LogName log_name = LogName.ofProjectLogName("[PROJECT]", "[LOG]");
+ *   Empty response = loggingServiceV2Client.DeleteLog(log_name);
+ * }
+ * }
+ * *

Note: close() needs to be called on the loggingServiceV2Client 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/MetricsServiceV2Client.java b/test/integration/goldens/logging/MetricsServiceV2Client.java index 030e4bb8d8..aef171784b 100644 --- a/test/integration/goldens/logging/MetricsServiceV2Client.java +++ b/test/integration/goldens/logging/MetricsServiceV2Client.java @@ -43,6 +43,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 (MetricsServiceV2Client metricsServiceV2Client = MetricsServiceV2Client.create()) {
+ *   LogMetricName metric_name = LogMetricName.of("[PROJECT]", "[METRIC]");
+ *   LogMetric response = metricsServiceV2Client.GetLogMetric(metric_name);
+ * }
+ * }
+ * *

Note: close() needs to be called on the metricsServiceV2Client 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 294ac2e43e..993f4d138b 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(). *