From 5e0fa91d3861618758d2bd2b414d1e3177089aaa Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Fri, 18 Sep 2020 22:24:16 -0700 Subject: [PATCH] [ggj][codegen] feat: add protobuf comments to ServiceClient (#310) * feat: add protobuf comment parser util * fix: add basic proto build rules * feat: add header comments to ServiceClient * fix: build protoc at test time * fix!: wrap protobuf location and process comments * feat: add comment parsing to methods and fields * fix: test * feat: add protobuf comments to ServiceClient * fix: solidify codegen method order with TypeNode/MethodArg and Comparable * fix: clean up tests * fix: merge --- .../composer/ServiceClientClassComposer.java | 9 ++ .../ServiceClientCommentComposer.java | 55 +++++++++ .../ServiceClientClassComposerTest.java | 105 +++++++++++++++++- 3 files changed, 168 insertions(+), 1 deletion(-) 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 112b2a7132..ccb53533ae 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 @@ -587,6 +587,8 @@ private static List createMethodVariants( javaMethods.add( MethodDefinition.builder() + .setHeaderCommentStatements( + ServiceClientCommentComposer.createRpcMethodHeaderComment(method, signature)) .setScope(ScopeNode.PUBLIC) .setIsFinal(true) .setReturnType(methodOutputType) @@ -618,6 +620,8 @@ private static List createMethodVariants( .build(); javaMethods.add( MethodDefinition.builder() + .setHeaderCommentStatements( + ServiceClientCommentComposer.createRpcMethodHeaderComment(method)) .setScope(ScopeNode.PUBLIC) .setIsFinal(true) .setReturnType(methodOutputType) @@ -631,6 +635,7 @@ private static List createMethodVariants( private static MethodDefinition createLroAsyncMethod( String serviceName, Method method, Map types) { + // TODO(miraleung): Create variants as well. Preconditions.checkState( method.hasLro(), String.format("Method %s does not have an LRO", method.name())); String methodName = JavaStyle.toLowerCamelCase(method.name()); @@ -665,6 +670,8 @@ private static MethodDefinition createLroAsyncMethod( .build(); return MethodDefinition.builder() + .setHeaderCommentStatements( + ServiceClientCommentComposer.createRpcMethodHeaderComment(method)) .setScope(ScopeNode.PUBLIC) .setIsFinal(true) .setReturnType(returnType) @@ -736,6 +743,8 @@ private static MethodDefinition createCallableMethod( .build(); return MethodDefinition.builder() + .setHeaderCommentStatements( + ServiceClientCommentComposer.createRpcCallableMethodHeaderComment(method)) .setScope(ScopeNode.PUBLIC) .setIsFinal(true) .setName(methodName) 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 46b7d61bbe..d79bf7f03e 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 @@ -17,14 +17,20 @@ import com.google.api.generator.engine.ast.CommentStatement; import com.google.api.generator.engine.ast.JavaDocComment; import com.google.api.generator.engine.ast.TypeNode; +import com.google.api.generator.gapic.model.Method; +import com.google.api.generator.gapic.model.MethodArgument; 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.List; class ServiceClientCommentComposer { // Tokens. private static final String COLON = ":"; + private static final String EMPTY_STRING = ""; + private static final String API_EXCEPTION_TYPE_NAME = "com.google.api.gax.rpc.ApiException"; + private static final String EXCEPTION_CONDITION = "if the remote call fails"; // Constants. private static final String SERVICE_DESCRIPTION_INTRO_STRING = @@ -49,6 +55,8 @@ class ServiceClientCommentComposer { private static final String SERVICE_DESCRIPTION_ENDPOINT_SUMMARY_STRING = "To customize the endpoint:"; + private static final String METHOD_DESCRIPTION_SAMPLE_CODE_SUMMARY_STRING = "Sample code:"; + private static final List SERVICE_DESCRIPTION_SURFACE_DESCRIPTION = Arrays.asList( "A \"flattened\" method. With this type of method, the fields of the request type have" @@ -133,6 +141,53 @@ static CommentStatement createCreateMethodStubArgComment(TypeNode settingsType) String.format(CREATE_METHOD_STUB_ARG_PATTERN, settingsType.reference().name())); } + static List createRpcMethodHeaderComment( + Method method, List methodArguments) { + JavaDocComment.Builder methodJavadocBuilder = JavaDocComment.builder(); + + if (method.hasDescription()) { + methodJavadocBuilder.addComment(method.description()); + } + + methodJavadocBuilder.addParagraph(METHOD_DESCRIPTION_SAMPLE_CODE_SUMMARY_STRING); + // TODO(summerji): Add sample code here. + + if (methodArguments.isEmpty()) { + methodJavadocBuilder.addParam( + "request", "The request object containing all of the parameters for the API call."); + } else { + for (MethodArgument argument : methodArguments) { + methodJavadocBuilder.addParam( + argument.name(), argument.hasDescription() ? argument.description() : EMPTY_STRING); + } + } + + methodJavadocBuilder.setThrows(API_EXCEPTION_TYPE_NAME, EXCEPTION_CONDITION); + + return Arrays.asList( + CommentComposer.AUTO_GENERATED_METHOD_COMMENT, + CommentStatement.withComment(methodJavadocBuilder.build())); + } + + static List createRpcMethodHeaderComment(Method method) { + return createRpcMethodHeaderComment(method, Collections.emptyList()); + } + + static List createRpcCallableMethodHeaderComment(Method method) { + JavaDocComment.Builder methodJavadocBuilder = JavaDocComment.builder(); + + if (method.hasDescription()) { + methodJavadocBuilder.addComment(method.description()); + } + + methodJavadocBuilder.addParagraph(METHOD_DESCRIPTION_SAMPLE_CODE_SUMMARY_STRING); + // TODO(summerji): Add sample code here. + + return Arrays.asList( + CommentComposer.AUTO_GENERATED_METHOD_COMMENT, + CommentStatement.withComment(methodJavadocBuilder.build())); + } + private static CommentStatement toSimpleComment(String comment) { return CommentStatement.withComment(JavaDocComment.withComment(comment)); } diff --git a/src/test/java/com/google/api/generator/gapic/composer/ServiceClientClassComposerTest.java b/src/test/java/com/google/api/generator/gapic/composer/ServiceClientClassComposerTest.java index 1f52df1ab6..fc56cb6ced 100644 --- a/src/test/java/com/google/api/generator/gapic/composer/ServiceClientClassComposerTest.java +++ b/src/test/java/com/google/api/generator/gapic/composer/ServiceClientClassComposerTest.java @@ -203,6 +203,13 @@ public void generateServiceClasses() { + " return operationsClient;\n" + " }\n" + "\n" + + " // AUTO-GENERATED DOCUMENTATION AND METHOD.\n" + + " /**\n" + + " * Sample code:\n" + + " *\n" + + " * @param parent\n" + + " * @throws com.google.api.gax.rpc.ApiException if the remote call fails\n" + + " */\n" + " public final EchoResponse echo(ResourceName parent) {\n" + " EchoRequest request =\n" + " EchoRequest.newBuilder()\n" @@ -211,11 +218,25 @@ public void generateServiceClasses() { + " return echo(request);\n" + " }\n" + "\n" + + " // AUTO-GENERATED DOCUMENTATION AND METHOD.\n" + + " /**\n" + + " * Sample code:\n" + + " *\n" + + " * @param error\n" + + " * @throws com.google.api.gax.rpc.ApiException if the remote call fails\n" + + " */\n" + " public final EchoResponse echo(Status error) {\n" + " EchoRequest request = EchoRequest.newBuilder().setError(error).build();\n" + " return echo(request);\n" + " }\n" + "\n" + + " // AUTO-GENERATED DOCUMENTATION AND METHOD.\n" + + " /**\n" + + " * Sample code:\n" + + " *\n" + + " * @param name\n" + + " * @throws com.google.api.gax.rpc.ApiException if the remote call fails\n" + + " */\n" + " public final EchoResponse echo(FoobarName name) {\n" + " EchoRequest request =\n" + " EchoRequest.newBuilder()\n" @@ -224,85 +245,167 @@ public void generateServiceClasses() { + " return echo(request);\n" + " }\n" + "\n" + + " // AUTO-GENERATED DOCUMENTATION AND METHOD.\n" + + " /**\n" + + " * Sample code:\n" + + " *\n" + + " * @param content\n" + + " * @throws com.google.api.gax.rpc.ApiException if the remote call fails\n" + + " */\n" + " public final EchoResponse echo(String content) {\n" + " EchoRequest request = EchoRequest.newBuilder().setContent(content).build();\n" + " return echo(request);\n" + " }\n" + "\n" + + " // AUTO-GENERATED DOCUMENTATION AND METHOD.\n" + + " /**\n" + + " * Sample code:\n" + + " *\n" + + " * @param name\n" + + " * @throws com.google.api.gax.rpc.ApiException if the remote call fails\n" + + " */\n" + " public final EchoResponse echo(String name) {\n" + " EchoRequest request = EchoRequest.newBuilder().setName(name).build();\n" + " return echo(request);\n" + " }\n" + "\n" + + " // AUTO-GENERATED DOCUMENTATION AND METHOD.\n" + + " /**\n" + + " * Sample code:\n" + + " *\n" + + " * @param parent\n" + + " * @throws com.google.api.gax.rpc.ApiException if the remote call fails\n" + + " */\n" + " public final EchoResponse echo(String parent) {\n" + " EchoRequest request = EchoRequest.newBuilder().setParent(parent).build();\n" + " return echo(request);\n" + " }\n" + "\n" + + " // AUTO-GENERATED DOCUMENTATION AND METHOD.\n" + + " /**\n" + + " * Sample code:\n" + + " *\n" + + " * @param content\n" + + " * @param severity\n" + + " * @throws com.google.api.gax.rpc.ApiException if the remote call fails\n" + + " */\n" + " public final EchoResponse echo(String content, Severity severity) {\n" + " EchoRequest request =\n" + " EchoRequest.newBuilder().setContent(content).setSeverity(severity).build();\n" + " return echo(request);\n" + " }\n" + "\n" + + " // AUTO-GENERATED DOCUMENTATION AND METHOD.\n" + + " /**\n" + + " * Sample code:\n" + + " *\n" + + " * @param request The request object containing all of the parameters for the API" + + " call.\n" + + " * @throws com.google.api.gax.rpc.ApiException if the remote call fails\n" + + " */\n" + " public final EchoResponse echo(EchoRequest request) {\n" + " return echoCallable().call(request);\n" + " }\n" + "\n" + + " // AUTO-GENERATED DOCUMENTATION AND METHOD.\n" + + " /** Sample code: */\n" + " public final UnaryCallable echoCallable() {\n" + " return stub.echoCallable();\n" + " }\n" + "\n" + + " // AUTO-GENERATED DOCUMENTATION AND METHOD.\n" + + " /** Sample code: */\n" + " public final ServerStreamingCallable expandCallable()" + " {\n" + " return stub.expandCallable();\n" + " }\n" + "\n" + + " // AUTO-GENERATED DOCUMENTATION AND METHOD.\n" + + " /** Sample code: */\n" + " public final ClientStreamingCallable collectCallable()" + " {\n" + " return stub.collectCallable();\n" + " }\n" + "\n" + + " // AUTO-GENERATED DOCUMENTATION AND METHOD.\n" + + " /** Sample code: */\n" + " public final BidiStreamingCallable chatCallable() {\n" + " return stub.chatCallable();\n" + " }\n" + "\n" + + " // AUTO-GENERATED DOCUMENTATION AND METHOD.\n" + + " /** Sample code: */\n" + " public final BidiStreamingCallable chatAgainCallable()" + " {\n" + " return stub.chatAgainCallable();\n" + " }\n" + "\n" + + " // AUTO-GENERATED DOCUMENTATION AND METHOD.\n" + + " /**\n" + + " * Sample code:\n" + + " *\n" + + " * @param request The request object containing all of the parameters for the API" + + " call.\n" + + " * @throws com.google.api.gax.rpc.ApiException if the remote call fails\n" + + " */\n" + " public final PagedExpandPagedResponse pagedExpand(PagedExpandRequest request) {\n" + " return pagedExpandPagedCallable().call(request);\n" + " }\n" + "\n" + + " // AUTO-GENERATED DOCUMENTATION AND METHOD.\n" + + " /** Sample code: */\n" + " public final UnaryCallable\n" + " pagedExpandPagedCallable() {\n" + " return stub.pagedExpandPagedCallable();\n" + " }\n" + "\n" - + " public final UnaryCallable pagedExpandCallable() {\n" + + " // AUTO-GENERATED DOCUMENTATION AND METHOD.\n" + + " /** Sample code: */\n" + + " public final UnaryCallable" + + " pagedExpandCallable() {\n" + " return stub.pagedExpandCallable();\n" + " }\n" + "\n" + + " // AUTO-GENERATED DOCUMENTATION AND METHOD.\n" + + " /**\n" + + " * Sample code:\n" + + " *\n" + + " * @param request The request object containing all of the parameters for the API" + + " call.\n" + + " * @throws com.google.api.gax.rpc.ApiException if the remote call fails\n" + + " */\n" + " public final OperationFuture waitAsync(WaitRequest" + " request) {\n" + " return waitOperationCallable().futureCall(request);\n" + " }\n" + "\n" + + " // AUTO-GENERATED DOCUMENTATION AND METHOD.\n" + + " /** Sample code: */\n" + " public final OperationCallable" + " waitOperationCallable() {\n" + " return stub.waitOperationCallable();\n" + " }\n" + "\n" + + " // AUTO-GENERATED DOCUMENTATION AND METHOD.\n" + + " /** Sample code: */\n" + " public final UnaryCallable waitCallable() {\n" + " return stub.waitCallable();\n" + " }\n" + "\n" + + " // AUTO-GENERATED DOCUMENTATION AND METHOD.\n" + + " /**\n" + + " * Sample code:\n" + + " *\n" + + " * @param request The request object containing all of the parameters for the API" + + " call.\n" + + " * @throws com.google.api.gax.rpc.ApiException if the remote call fails\n" + + " */\n" + " public final BlockResponse block(BlockRequest request) {\n" + " return blockCallable().call(request);\n" + " }\n" + "\n" + + " // AUTO-GENERATED DOCUMENTATION AND METHOD.\n" + + " /** Sample code: */\n" + " public final UnaryCallable blockCallable() {\n" + " return stub.blockCallable();\n" + " }\n"