Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[draft][1/2]Implement service client class header comment method sample code #528

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,11 @@ class ServiceClientCommentComposer {
+ " operation returned by another API method call.");

static List<CommentStatement> createClassHeaderComments(
Service service, TypeNode clientType, TypeNode settingsType) {
String settingsName = JavaStyle.toLowerCamelCase(getSettingsName(service.name()));
String clientName = JavaStyle.toLowerCamelCase(getClientClassName(service.name()));
Service service, Map<String, TypeNode> types, Map<String, ResourceName> 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 =
Expand All @@ -124,7 +126,9 @@ static List<CommentStatement> 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(
Expand Down Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, ResourceName> 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<String, TypeNode> 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<MethodArgument> 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.
Expand Down Expand Up @@ -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<Reference> getGenericsForCallable(
Method method, Map<String, TypeNode> 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<String, TypeNode> createDynamicTypes(Service service) {
Map<String, TypeNode> 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<String, TypeNode> createConcreteTypes() {
List<Class> 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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:
*
* <pre>{@code
* try (EchoClient echoClient = EchoClient.create()) {
* ResourceName parent = FoobarName.ofProjectFoobarName("[PROJECT]", "[FOOBAR]");
* EchoResponse response = echoClient.Echo(parent);
* }
* }</pre>
*
* <p>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().
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
*
* <pre>{@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);
* }
* }</pre>
*
* <p>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().
*
Expand Down
7 changes: 7 additions & 0 deletions test/integration/goldens/asset/AssetServiceClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@
* <p>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:
*
* <pre>{@code
* try (AssetServiceClient assetServiceClient = AssetServiceClient.create()) {
* BatchGetAssetsHistoryRequest request = BatchGetAssetsHistoryRequest.newBuilder().build();
* BatchGetAssetsHistoryResponse response = assetServiceClient.BatchGetAssetsHistory(request);
* }
* }</pre>
*
* <p>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().
*
Expand Down
7 changes: 7 additions & 0 deletions test/integration/goldens/logging/ConfigServiceV2Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@
* <p>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:
*
* <pre>{@code
* try (ConfigServiceV2Client configServiceV2Client = ConfigServiceV2Client.create()) {
* GetBucketRequest request = GetBucketRequest.newBuilder().build();
* LogBucket response = configServiceV2Client.GetBucket(request);
* }
* }</pre>
*
* <p>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().
Expand Down
7 changes: 7 additions & 0 deletions test/integration/goldens/logging/LoggingServiceV2Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@
* <p>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:
*
* <pre>{@code
* try (LoggingServiceV2Client loggingServiceV2Client = LoggingServiceV2Client.create()) {
* LogName log_name = LogName.ofProjectLogName("[PROJECT]", "[LOG]");
* Empty response = loggingServiceV2Client.DeleteLog(log_name);
* }
* }</pre>
*
* <p>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().
Expand Down
7 changes: 7 additions & 0 deletions test/integration/goldens/logging/MetricsServiceV2Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@
* <p>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:
*
* <pre>{@code
* try (MetricsServiceV2Client metricsServiceV2Client = MetricsServiceV2Client.create()) {
* LogMetricName metric_name = LogMetricName.of("[PROJECT]", "[METRIC]");
* LogMetric response = metricsServiceV2Client.GetLogMetric(metric_name);
* }
* }</pre>
*
* <p>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().
Expand Down
7 changes: 7 additions & 0 deletions test/integration/goldens/redis/CloudRedisClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@
* <p>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:
*
* <pre>{@code
* try (CloudRedisClient cloudRedisClient = CloudRedisClient.create()) {
* InstanceName name = InstanceName.of("[PROJECT]", "[LOCATION]", "[INSTANCE]");
* Instance response = cloudRedisClient.GetInstance(name);
* }
* }</pre>
*
* <p>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().
*
Expand Down