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

feat: update javadocs for Client classes to include table of methods #2114

Merged
merged 23 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from 6 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
9 changes: 9 additions & 0 deletions gapic-generator-java/DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ than the "test" phase.
To run integration test for gapic-generator-java, run this Bazel command in the
root of the repository (where you have WORKSPACE file for Bazel.)

*Note* Make sure you run `mvn clean install` to gather any changes you have made before updating the integration tests.

```sh
# In the repository root directory
bazelisk test //... # integration tests
Expand All @@ -73,6 +75,13 @@ bazelisk test //... # integration tests
bazelisk run //test/integration:update_redis
```

- To update all integration tests you can use this command:

```sh
# In the repository root directory
bazelisk run //test/integration:update_asset && bazelisk run //test/integration:update_credentials && bazelisk run //test/integration:update_iam && bazelisk run //test/integration:update_kms && bazelisk run //test/integration:update_pubsub && bazelisk run //test/integration:update_logging && bazelisk run //test/integration:update_redis && bazelisk run //test/integration:update_storage && bazelisk run //test/integration:update_library && bazelisk run //test/integration:update_compute && bazelisk run //test/integration:update_bigtable && bazelisk run //test/integration:update_apigeeconnect
alicejli marked this conversation as resolved.
Show resolved Hide resolved
```

## Running the Plugin under googleapis with local gapic-generator-java

For running the Plugin with showcase protos and local gapic-generator-java, see
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

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

import static java.util.stream.Collectors.toList;

import com.google.api.generator.engine.ast.CommentStatement;
import com.google.api.generator.engine.ast.JavaDocComment;
import com.google.api.generator.engine.ast.TypeNode;
Expand All @@ -26,11 +28,9 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

public class ServiceClientCommentComposer {
// Tokens.
Expand Down Expand Up @@ -122,30 +122,11 @@ public static List<CommentStatement> createClassHeaderComments(
String.format(
SERVICE_DESCRIPTION_CLOSE_PATTERN, ClassNames.getServiceClientClassName(service)));

// Build the map of methods and descriptions
Map<String, String> mapOfMethodsAndDescriptions =
Collections.unmodifiableMap(
service.methods().stream()
.collect(
Collectors.toMap(
Method::getName,
method -> {
String description = method.getDescription();
return description != null ? description : "";
},
(existingValue, newValue) -> existingValue,
LinkedHashMap::new)));

// Build a list of MethodAndVariants to create the table
List<MethodAndVariants> methodAndVariantsList = new ArrayList<>();
for (Map.Entry<String, String> method : mapOfMethodsAndDescriptions.entrySet()) {
MethodAndVariants methodAndVariants =
new MethodAndVariants(
method.getKey(),
method.getValue(),
methodVariantsForClientHeader.get(method.getKey()));
methodAndVariantsList.add(methodAndVariants);
}
// Build the map of methods and descriptions to create the table in Client Overviews
List<MethodAndVariants> methodAndVariantsList =
service.methods().stream()
.map((Method method) -> createMethodAndVariants(method, methodVariantsForClientHeader))
.collect(toList());

classHeaderJavadocBuilder.addUnescapedComment(createTableOfMethods(methodAndVariantsList));
classHeaderJavadocBuilder.addParagraph(SERVICE_DESCRIPTION_SURFACE_CODA_STRING);
Expand Down Expand Up @@ -229,15 +210,23 @@ public static List<CommentStatement> createRpcMethodHeaderComment(
return comments;
}

private static MethodAndVariants createMethodAndVariants(
Method method, Map<String, List<String>> methodVariantsForClientHeader) {
String name = method.name();
String description = method.description();
if (description == null) description = "";
return new MethodAndVariants(name, description, methodVariantsForClientHeader.get(name));
}

private static String createTableOfMethods(List<MethodAndVariants> methodAndVariantsList) {
String FLATTENED_METHODS =
"<p>\"Flattened\" method variants have the fields of the request type converted into function parameters to enable multiple ways to call the same method.</p>\n";
"<p>\"Flattened\" method variants have converted the fields of the request object into function parameters to enable multiple ways to call the same method.</p>\n";
String REQUEST_OBJECT_METHODS =
"<p>Request object method variants only takes one parameter, a request object, which must be constructed before the call.</p>\n";
"<p>Request object method variants only take one parameter, a request object, which must be constructed before the call.</p>\n";
String CALLABLE_METHODS =
"<p>Callable method variants take no parameters and returns an immutable API callable object, which can be used to initiate calls to the service.</p>\n";
"<p>Callable method variants take no parameters and return an immutable API callable object, which can be used to initiate calls to the service.</p>\n";
String ASYNC_METHODS =
"<p>Methods that return long-running operations have \"Async\" method variants that return `OperationFuture` which is used to track polling of the service.</p>\n";
"<p>Methods that return long-running operations have \"Async\" method variants that return `OperationFuture`, which is used to track polling of the service.</p>\n";

StringBuilder tableBuilder = new StringBuilder();
tableBuilder
Expand All @@ -256,85 +245,53 @@ private static String createTableOfMethods(List<MethodAndVariants> methodAndVari
.append("<p>" + method.description + "</p>")
.append("</td>\n")
.append(" <td>\n");
if (method.hasRequestObjectVariants) {
tableBuilder
.append(" " + REQUEST_OBJECT_METHODS + " ")
.append("<ul>\n")
.append(" <li>")
.append(String.join("\n <li>", method.requestObjectVariants))
.append("\n")
.append(" </ul>")
.append("\n");
}
if (method.hasFlattenedVariants) {
tableBuilder
.append(" " + FLATTENED_METHODS + " ")
.append("<ul>\n")
.append(" <li>")
.append(String.join("\n <li>", method.flattenedVariants))
.append("\n")
.append(" </ul>")
.append("\n");
}
if (method.hasAsyncVariants) {
tableBuilder
.append(" " + ASYNC_METHODS + " ")
.append("<ul>\n")
.append(" <li>")
.append(String.join("\n <li>", method.asyncVariants))
.append("\n")
.append(" </ul>")
.append("\n");
}
if (method.hasCallableVariants) {
tableBuilder
.append(" " + CALLABLE_METHODS + " ")
.append("<ul>\n")
.append(" <li>")
.append(String.join("\n <li>", method.callableVariants))
.append("\n")
.append(" </ul>")
.append("\n");
}
generateUnorderedListMethodVariants(
tableBuilder, REQUEST_OBJECT_METHODS, method.requestObjectVariants);
generateUnorderedListMethodVariants(
tableBuilder, FLATTENED_METHODS, method.flattenedVariants);
generateUnorderedListMethodVariants(tableBuilder, ASYNC_METHODS, method.asyncVariants);
generateUnorderedListMethodVariants(tableBuilder, CALLABLE_METHODS, method.callableVariants);
tableBuilder.append(" </td>\n").append(" </tr>\n");
}
tableBuilder.append(" </table>\n");
return tableBuilder.toString();
}

public static class MethodAndVariants {
String method;
String description;
boolean hasFlattenedVariants = false;
boolean hasRequestObjectVariants;
boolean hasCallableVariants;
boolean hasAsyncVariants;
private static void generateUnorderedListMethodVariants(
StringBuilder tableBuilder, String methodType, List<String> methodVariants) {
if (!methodVariants.isEmpty()) {
tableBuilder
.append(" " + methodType + " ")
.append("<ul>\n")
.append(" <li>")
.append(String.join("\n <li>", methodVariants))
.append("\n")
.append(" </ul>")
.append("\n");
}
}

private static class MethodAndVariants {
private final String method;
private final String description;
alicejli marked this conversation as resolved.
Show resolved Hide resolved

List<String> flattenedVariants;
List<String> requestObjectVariants;
List<String> callableVariants;
List<String> asyncVariants;
private final List<String> flattenedVariants;
private final List<String> requestObjectVariants;
private final List<String> callableVariants;
private final List<String> asyncVariants;

public MethodAndVariants(String method, String description, List<String> methodVariants) {
private MethodAndVariants(String method, String description, List<String> methodVariants) {
this.method = method;
this.description = description;
requestObjectVariants =
methodVariants.stream().filter(s -> s.contains("request")).collect(Collectors.toList());
hasRequestObjectVariants = methodVariants.removeAll(requestObjectVariants);
// hasRequestObjectVariants = methodVariants.stream().anyMatch(s -> s.contains("request"));
methodVariants.stream().filter(s -> s.contains("request")).collect(toList());
methodVariants.removeAll(requestObjectVariants);
callableVariants =
methodVariants.stream().filter(s -> s.contains("Callable")).collect(Collectors.toList());
hasCallableVariants = methodVariants.removeAll(callableVariants);
// hasCallableVariants = methodVariants.stream().anyMatch(s -> s.contains("Callable"));
asyncVariants =
methodVariants.stream().filter(s -> s.contains("Async")).collect(Collectors.toList());
hasAsyncVariants = methodVariants.removeAll(asyncVariants);
// hasAsyncVariants = methodVariants.stream().anyMatch(s -> s.contains("Async"));
// Whatever is remaining should just be flattened variants
methodVariants.stream().filter(s -> s.contains("Callable")).collect(toList());
methodVariants.removeAll(callableVariants);
asyncVariants = methodVariants.stream().filter(s -> s.contains("Async")).collect(toList());
methodVariants.removeAll(asyncVariants);
flattenedVariants = methodVariants;
alicejli marked this conversation as resolved.
Show resolved Hide resolved
if (!flattenedVariants.isEmpty()) {
hasFlattenedVariants = true;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,35 @@ private List<MethodDefinition> createGetterMethods(
.collect(Collectors.toList());
}

private static String getJavaMethod(MethodDefinition m) {
StringBuilder methodSignature = new StringBuilder();

// Method name
methodSignature.append(m.methodIdentifier().name()).append("(");

// Iterate through and add all parameters
List<Variable> parameters =
m.arguments().stream().map(VariableExpr::variable).collect(Collectors.toList());

for (int i = 0; i < parameters.size(); i++) {
Variable param = parameters.get(i);
String paramType =
param.type().reference() != null ? param.type().reference().name() + " " : "";
alicejli marked this conversation as resolved.
Show resolved Hide resolved
String paramName = param.identifier().name();

methodSignature.append(paramType).append(paramName);

// Add a comma if there are more parameters
if (i < parameters.size() - 1) {
methodSignature.append(", ");
}
}

methodSignature.append(")");

return methodSignature.toString();
}

private static List<MethodDefinition> createServiceMethods(
Service service,
Map<String, Message> messageTypes,
Expand All @@ -588,27 +617,6 @@ private static List<MethodDefinition> createServiceMethods(
List<Sample> samples) {
List<MethodDefinition> javaMethods = new ArrayList<>();
Function<MethodDefinition, String> javaMethodNameFn = m -> m.methodIdentifier().name();
// This generates the list of method variants with arguments for the Client header statements
Function<MethodDefinition, String> javaMethodFn =
m -> {
String s;
if (m.arguments().isEmpty()) {
s = m.methodIdentifier().name() + "()";
} else {
String argument =
m.arguments().get(0).variable().type().reference() != null
? m.arguments().get(0).variable().type().reference().name() + " "
: "";
s =
m.methodIdentifier().name()
+ "("
+ argument
+ m.arguments().get(0).variable().identifier().name()
+ ")";
}
;
return s;
};
for (Method method : service.methods()) {
if (!grpcRpcToJavaMethodMetadata.containsKey(method.name())) {
grpcRpcToJavaMethodMetadata.put(method.name(), new ArrayList<>());
Expand Down Expand Up @@ -638,7 +646,7 @@ private static List<MethodDefinition> createServiceMethods(
.get(method.name())
.addAll(
generatedMethods.stream()
.map(m -> javaMethodFn.apply(m))
.map(AbstractServiceClientClassComposer::getJavaMethod)
.collect(Collectors.toList()));
javaMethods.addAll(generatedMethods);

Expand All @@ -654,7 +662,7 @@ private static List<MethodDefinition> createServiceMethods(

// Collect data for gapic_metadata.json and client header.
grpcRpcToJavaMethodMetadata.get(method.name()).add(javaMethodNameFn.apply(generatedMethod));
methodVariantsForClientHeader.get(method.name()).add(javaMethodFn.apply(generatedMethod));
methodVariantsForClientHeader.get(method.name()).add(getJavaMethod(generatedMethod));
javaMethods.add(generatedMethod);
}
if (method.hasLro()) {
Expand All @@ -664,7 +672,7 @@ private static List<MethodDefinition> createServiceMethods(

// Collect data for gapic_metadata.json and client header.
grpcRpcToJavaMethodMetadata.get(method.name()).add(javaMethodNameFn.apply(generatedMethod));
methodVariantsForClientHeader.get(method.name()).add(javaMethodFn.apply(generatedMethod));
methodVariantsForClientHeader.get(method.name()).add(getJavaMethod(generatedMethod));
javaMethods.add(generatedMethod);
}
if (method.isPaged()) {
Expand All @@ -674,15 +682,15 @@ private static List<MethodDefinition> createServiceMethods(

// Collect data for gapic_metadata.json and client header.
grpcRpcToJavaMethodMetadata.get(method.name()).add(javaMethodNameFn.apply(generatedMethod));
methodVariantsForClientHeader.get(method.name()).add(javaMethodFn.apply(generatedMethod));
methodVariantsForClientHeader.get(method.name()).add(getJavaMethod(generatedMethod));
javaMethods.add(generatedMethod);
}
MethodDefinition generatedMethod =
createCallableMethod(service, method, typeStore, messageTypes, resourceNames, samples);

// Collect data for the gapic_metadata.json file and client header.
grpcRpcToJavaMethodMetadata.get(method.name()).add(javaMethodNameFn.apply(generatedMethod));
methodVariantsForClientHeader.get(method.name()).add(javaMethodFn.apply(generatedMethod));
methodVariantsForClientHeader.get(method.name()).add(getJavaMethod(generatedMethod));
javaMethods.add(generatedMethod);
}
return javaMethods;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,6 @@ public boolean isPaged() {

public abstract boolean operationPollingMethod();

public String getName() {
return name();
}

public String getDescription() {
return description();
}

public boolean hasLro() {
return lro() != null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ import javax.annotation.Generated;
* <td>NestedMessageMethod</td>
* <td><p></p></td>
* <td>
* <p>Request object method variants only takes one parameter, a request object, which must be constructed before the call.</p>
* <p>Request object method variants only take one parameter, a request object, which must be constructed before the call.</p>
* <ul>
* <li>nestedMessageMethod(Outer.Middle request)
* </ul>
* <p>Callable method variants take no parameters and returns an immutable API callable object, which can be used to initiate calls to the service.</p>
* <p>Callable method variants take no parameters and return an immutable API callable object, which can be used to initiate calls to the service.</p>
* <ul>
* <li>nestedMessageMethodCallable()
* </ul>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,16 @@ import javax.annotation.Generated;
* <td>GetBook</td>
* <td><p></p></td>
* <td>
* <p>Request object method variants only takes one parameter, a request object, which must be constructed before the call.</p>
* <p>Request object method variants only take one parameter, a request object, which must be constructed before the call.</p>
* <ul>
* <li>getBook(GetBookRequest request)
* </ul>
* <p>"Flattened" method variants have the fields of the request type converted into function parameters to enable multiple ways to call the same method.</p>
* <p>"Flattened" method variants have converted the fields of the request object into function parameters to enable multiple ways to call the same method.</p>
* <ul>
* <li>getBook(booksCount)
* <li>getBook(String booksList)
* <li>getBook(booksCount, List<Book> books)
* <li>getBook(String booksList, List<Book> books)
* </ul>
* <p>Callable method variants take no parameters and returns an immutable API callable object, which can be used to initiate calls to the service.</p>
* <p>Callable method variants take no parameters and return an immutable API callable object, which can be used to initiate calls to the service.</p>
* <ul>
* <li>getBookCallable()
* </ul>
Expand Down
Loading
Loading