Skip to content

Commit

Permalink
feat: Add add autogenerated javadoc sample for selecting REST transpo…
Browse files Browse the repository at this point in the history
…rt over gRPC (#983)

This is for grpc+rest clients.

Also add proper grpc+rest unit tests which were missing.
  • Loading branch information
vam-google committed Apr 21, 2022
1 parent 9e863f8 commit 051713d
Show file tree
Hide file tree
Showing 25 changed files with 4,577 additions and 6 deletions.
Expand Up @@ -55,6 +55,8 @@ public class ServiceClientCommentComposer {
"To customize credentials:";
private static final String SERVICE_DESCRIPTION_ENDPOINT_SUMMARY_STRING =
"To customize the endpoint:";
private static final String SERVICE_DESCRIPTION_TRANSPORT_SUMMARY_STRING =
"To use %s transport (instead of %s) for sending an receiving requests over the wire:";

private static final String SERVICE_DESCRIPTION_SAMPLE_REFERENCE_STRING =
"Please refer to the GitHub repository's samples for more quickstart code snippets.";
Expand Down Expand Up @@ -112,7 +114,10 @@ public static List<CommentStatement> createClassHeaderComments(
Service service,
String classMethodSampleCode,
String credentialsSampleCode,
String endpointSampleCode) {
String endpointSampleCode,
String transportSampleCode,
String primaryTransport,
String secondaryTransport) {
JavaDocComment.Builder classHeaderJavadocBuilder = JavaDocComment.builder();
if (service.hasDescription()) {
classHeaderJavadocBuilder =
Expand Down Expand Up @@ -146,6 +151,12 @@ public static List<CommentStatement> createClassHeaderComments(
classHeaderJavadocBuilder.addSampleCode(credentialsSampleCode);
classHeaderJavadocBuilder.addParagraph(SERVICE_DESCRIPTION_ENDPOINT_SUMMARY_STRING);
classHeaderJavadocBuilder.addSampleCode(endpointSampleCode);
if (transportSampleCode != null) {
classHeaderJavadocBuilder.addParagraph(
String.format(
SERVICE_DESCRIPTION_TRANSPORT_SUMMARY_STRING, secondaryTransport, primaryTransport));
classHeaderJavadocBuilder.addSampleCode(transportSampleCode);
}

classHeaderJavadocBuilder.addParagraph(SERVICE_DESCRIPTION_SAMPLE_REFERENCE_STRING);

Expand Down
Expand Up @@ -188,7 +188,7 @@ private static List<TypeNode> createClassImplements(TypeStore typeStore) {
return Arrays.asList(typeStore.get("BackgroundResource"));
}

private static List<CommentStatement> createClassHeaderComments(
protected List<CommentStatement> createClassHeaderComments(
Service service,
TypeStore typeStore,
Map<String, ResourceName> resourceNames,
Expand All @@ -208,7 +208,10 @@ private static List<CommentStatement> createClassHeaderComments(
service,
SampleCodeWriter.writeInlineSample(classMethodSampleCode.body()),
SampleCodeWriter.writeInlineSample(credentialsSampleCode.body()),
SampleCodeWriter.writeInlineSample(endpointSampleCode.body()));
SampleCodeWriter.writeInlineSample(endpointSampleCode.body()),
null,
null,
null);
}

private List<MethodDefinition> createClassMethods(
Expand Down
Expand Up @@ -14,7 +14,21 @@

package com.google.api.generator.gapic.composer.grpcrest;

import com.google.api.generator.engine.ast.CommentStatement;
import com.google.api.generator.engine.ast.TypeNode;
import com.google.api.generator.gapic.composer.comment.ServiceClientCommentComposer;
import com.google.api.generator.gapic.composer.common.AbstractServiceClientClassComposer;
import com.google.api.generator.gapic.composer.samplecode.SampleCodeWriter;
import com.google.api.generator.gapic.composer.samplecode.ServiceClientHeaderSampleComposer;
import com.google.api.generator.gapic.composer.store.TypeStore;
import com.google.api.generator.gapic.composer.utils.ClassNames;
import com.google.api.generator.gapic.model.Message;
import com.google.api.generator.gapic.model.ResourceName;
import com.google.api.generator.gapic.model.Sample;
import com.google.api.generator.gapic.model.Service;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

public class ServiceClientClassComposer extends AbstractServiceClientClassComposer {
private static final ServiceClientClassComposer INSTANCE = new ServiceClientClassComposer();
Expand All @@ -26,4 +40,37 @@ protected ServiceClientClassComposer() {
public static ServiceClientClassComposer instance() {
return INSTANCE;
}

@Override
protected List<CommentStatement> createClassHeaderComments(
Service service,
TypeStore typeStore,
Map<String, ResourceName> resourceNames,
Map<String, Message> messageTypes,
List<Sample> samples) {
TypeNode clientType = typeStore.get(ClassNames.getServiceClientClassName(service));
TypeNode settingsType = typeStore.get(ClassNames.getServiceSettingsClassName(service));
Sample classMethodSampleCode =
ServiceClientHeaderSampleComposer.composeClassHeaderSample(
service, clientType, resourceNames, messageTypes);
Sample credentialsSampleCode =
ServiceClientHeaderSampleComposer.composeSetCredentialsSample(clientType, settingsType);
Sample endpointSampleCode =
ServiceClientHeaderSampleComposer.composeSetEndpointSample(clientType, settingsType);
Sample transportSampleCode =
ServiceClientHeaderSampleComposer.composeTransportSample(
clientType, settingsType, "defaultHttpJsonTransportProviderBuilder");
samples.addAll(
Arrays.asList(
classMethodSampleCode, credentialsSampleCode, endpointSampleCode, transportSampleCode));

return ServiceClientCommentComposer.createClassHeaderComments(
service,
SampleCodeWriter.writeInlineSample(classMethodSampleCode.body()),
SampleCodeWriter.writeInlineSample(credentialsSampleCode.body()),
SampleCodeWriter.writeInlineSample(endpointSampleCode.body()),
SampleCodeWriter.writeInlineSample(transportSampleCode.body()),
"gRPC",
"REST (HTTP1.1/JSON)");
}
}
Expand Up @@ -277,6 +277,76 @@ public static Sample composeSetEndpointSample(TypeNode clientType, TypeNode sett
return Sample.builder().setBody(sampleBody).setRegionTag(regionTag).build();
}

public static Sample composeTransportSample(
TypeNode clientType, TypeNode settingsType, String transportProviderMethod) {
String settingsName = JavaStyle.toLowerCamelCase(settingsType.reference().name());
String clientName = JavaStyle.toLowerCamelCase(clientType.reference().name());
VariableExpr settingsVarExpr =
VariableExpr.withVariable(
Variable.builder().setName(settingsName).setType(settingsType).build());
MethodInvocationExpr newBuilderMethodExpr =
MethodInvocationExpr.builder()
.setStaticReferenceType(settingsType)
.setMethodName("newBuilder")
.build();
MethodInvocationExpr transportChannelProviderArg =
MethodInvocationExpr.builder()
.setExprReferenceExpr(
MethodInvocationExpr.builder()
.setStaticReferenceType(settingsType)
.setMethodName(transportProviderMethod)
.build())
.setMethodName("build")
.build();
MethodInvocationExpr credentialsMethodExpr =
MethodInvocationExpr.builder()
.setExprReferenceExpr(newBuilderMethodExpr)
.setArguments(transportChannelProviderArg)
.setMethodName("setTransportChannelProvider")
.build();
MethodInvocationExpr buildMethodExpr =
MethodInvocationExpr.builder()
.setExprReferenceExpr(credentialsMethodExpr)
.setReturnType(settingsType)
.setMethodName("build")
.build();
Expr initSettingsVarExpr =
AssignmentExpr.builder()
.setVariableExpr(settingsVarExpr.toBuilder().setIsDecl(true).build())
.setValueExpr(buildMethodExpr)
.build();

// Initialized client with create() method.
// e.g. EchoClient echoClient = EchoClient.create(echoSettings);
VariableExpr clientVarExpr =
VariableExpr.withVariable(
Variable.builder().setName(clientName).setType(clientType).build());
MethodInvocationExpr createMethodExpr =
MethodInvocationExpr.builder()
.setStaticReferenceType(clientType)
.setArguments(settingsVarExpr)
.setMethodName("create")
.setReturnType(clientType)
.build();
String rpcName = createMethodExpr.methodIdentifier().name();
Expr initClientVarExpr =
AssignmentExpr.builder()
.setVariableExpr(clientVarExpr.toBuilder().setIsDecl(true).build())
.setValueExpr(createMethodExpr)
.build();

List<Statement> sampleBody =
Arrays.asList(
ExprStatement.withExpr(initSettingsVarExpr), ExprStatement.withExpr(initClientVarExpr));
RegionTag regionTag =
RegionTag.builder()
.setServiceName(clientName)
.setRpcName(rpcName)
.setOverloadDisambiguation("setCredentialsProvider")
.build();
return Sample.builder().setBody(sampleBody).setRegionTag(regionTag).build();
}

// Create a list of RPC method arguments' variable expressions.
private static List<VariableExpr> createArgumentVariableExprs(List<MethodArgument> arguments) {
return arguments.stream()
Expand Down
@@ -0,0 +1,84 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.api.generator.gapic.composer.grpcrest;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import com.google.api.generator.gapic.composer.common.TestProtoLoader;
import com.google.api.generator.gapic.model.GapicContext;
import com.google.api.generator.gapic.model.GapicServiceConfig;
import com.google.api.generator.gapic.model.Message;
import com.google.api.generator.gapic.model.ResourceName;
import com.google.api.generator.gapic.model.Service;
import com.google.api.generator.gapic.model.Transport;
import com.google.api.generator.gapic.protoparser.Parser;
import com.google.api.generator.gapic.protoparser.ServiceConfigParser;
import com.google.longrunning.OperationsProto;
import com.google.protobuf.Descriptors.FileDescriptor;
import com.google.protobuf.Descriptors.ServiceDescriptor;
import com.google.showcase.grpcrest.v1beta1.EchoGrpcrest;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

public class GrpcRestTestProtoLoader extends TestProtoLoader {
private static final GrpcRestTestProtoLoader INSTANCE = new GrpcRestTestProtoLoader();

protected GrpcRestTestProtoLoader() {
super(Transport.GRPC_REST, "src/test/resources/");
}

public static GrpcRestTestProtoLoader instance() {
return INSTANCE;
}

@Override
public GapicContext parseShowcaseEcho() {
FileDescriptor echoFileDescriptor = EchoGrpcrest.getDescriptor();

ServiceDescriptor echoServiceDescriptor = echoFileDescriptor.getServices().get(0);
assertEquals("Echo", echoServiceDescriptor.getName());

Map<String, Message> messageTypes = Parser.parseMessages(echoFileDescriptor);
Map<String, Message> operationMessageTypes =
Parser.parseMessages(OperationsProto.getDescriptor());
messageTypes.putAll(operationMessageTypes);
Map<String, ResourceName> resourceNames = Parser.parseResourceNames(echoFileDescriptor);
Set<ResourceName> outputResourceNames = new HashSet<>();
List<Service> services =
Parser.parseService(
echoFileDescriptor, messageTypes, resourceNames, Optional.empty(), outputResourceNames);

String jsonFilename = "showcase_grpc_service_config.json";
Path jsonPath = Paths.get(getTestFilesDirectory(), jsonFilename);
Optional<GapicServiceConfig> configOpt = ServiceConfigParser.parse(jsonPath.toString());
assertTrue(configOpt.isPresent());
GapicServiceConfig config = configOpt.get();

return GapicContext.builder()
.setMessages(messageTypes)
.setResourceNames(resourceNames)
.setServices(services)
.setServiceConfig(config)
.setHelperResourceNames(outputResourceNames)
.setTransport(getTransport())
.build();
}
}
@@ -0,0 +1,43 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.api.generator.gapic.composer.grpcrest;

import com.google.api.generator.engine.writer.JavaWriterVisitor;
import com.google.api.generator.gapic.composer.grpc.GrpcServiceCallableFactoryClassComposer;
import com.google.api.generator.gapic.model.GapicClass;
import com.google.api.generator.gapic.model.GapicContext;
import com.google.api.generator.gapic.model.Service;
import com.google.api.generator.test.framework.Assert;
import com.google.api.generator.test.framework.Utils;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.junit.Test;

public class GrpcServiceCallableFactoryClassComposerTest {
@Test
public void generateServiceClasses() {
GapicContext context = GrpcRestTestProtoLoader.instance().parseShowcaseEcho();
Service echoProtoService = context.services().get(0);
GapicClass clazz =
GrpcServiceCallableFactoryClassComposer.instance().generate(context, echoProtoService);

JavaWriterVisitor visitor = new JavaWriterVisitor();
clazz.classDefinition().accept(visitor);
Utils.saveCodegenToFile(this.getClass(), "GrpcEchoCallableFactory.golden", visitor.write());
Path goldenFilePath =
Paths.get(Utils.getGoldenDir(this.getClass()), "GrpcEchoCallableFactory.golden");
Assert.assertCodeEquals(goldenFilePath, visitor.write());
}
}
@@ -0,0 +1,41 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.api.generator.gapic.composer.grpcrest;

import com.google.api.generator.engine.writer.JavaWriterVisitor;
import com.google.api.generator.gapic.composer.grpc.GrpcServiceStubClassComposer;
import com.google.api.generator.gapic.model.GapicClass;
import com.google.api.generator.gapic.model.GapicContext;
import com.google.api.generator.gapic.model.Service;
import com.google.api.generator.test.framework.Assert;
import com.google.api.generator.test.framework.Utils;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.junit.Test;

public class GrpcServiceStubClassComposerTest {
@Test
public void generateServiceClasses() {
GapicContext context = GrpcRestTestProtoLoader.instance().parseShowcaseEcho();
Service echoProtoService = context.services().get(0);
GapicClass clazz = GrpcServiceStubClassComposer.instance().generate(context, echoProtoService);

JavaWriterVisitor visitor = new JavaWriterVisitor();
clazz.classDefinition().accept(visitor);
Utils.saveCodegenToFile(this.getClass(), "GrpcEchoStub.golden", visitor.write());
Path goldenFilePath = Paths.get(Utils.getGoldenDir(this.getClass()), "GrpcEchoStub.golden");
Assert.assertCodeEquals(goldenFilePath, visitor.write());
}
}

0 comments on commit 051713d

Please sign in to comment.