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

[ggj][codegen] feat: add comment parsing to methods and fields #309

Merged
merged 13 commits into from
Sep 19, 2020
9 changes: 9 additions & 0 deletions src/main/java/com/google/api/generator/gapic/model/Field.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ public abstract class Field {
@Nullable
public abstract ResourceReference resourceReference();

@Nullable
public abstract String description();

public boolean hasDescription() {
return description() != null;
}

public boolean hasResourceReference() {
return type().equals(TypeNode.STRING) && resourceReference() != null;
}
Expand All @@ -51,6 +58,8 @@ public abstract static class Builder {

public abstract Builder setResourceReference(ResourceReference resourceReference);

public abstract Builder setDescription(String description);

public abstract Field build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ public enum Stream {
@Nullable
public abstract LongrunningOperation lro();

@Nullable
public abstract String description();

// Example from Expand in echo.proto: Thet TypeNodes that map to
// [["content", "error"], ["content", "error", "info"]].
public abstract ImmutableList<List<MethodArgument>> methodSignatures();
Expand All @@ -50,7 +53,9 @@ public boolean hasLro() {
return lro() != null;
}

// TODO(miraleung): Parse annotations, comments.
public boolean hasDescription() {
return description() != null;
}

public static Builder builder() {
return new AutoValue_Method.Builder()
Expand Down Expand Up @@ -84,6 +89,8 @@ public abstract static class Builder {

public abstract Builder setLro(LongrunningOperation lro);

public abstract Builder setDescription(String description);

public abstract Builder setMethodSignatures(List<List<MethodArgument>> methodSignature);

public abstract Builder setIsPaged(boolean isPaged);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import java.util.List;
import javax.annotation.Nullable;

@AutoValue
public abstract class MethodArgument implements Comparable<MethodArgument> {
Expand All @@ -32,6 +33,13 @@ public abstract class MethodArgument implements Comparable<MethodArgument> {
// Returns true if this is a resource name helper tyep.
public abstract boolean isResourceNameHelper();

@Nullable
public abstract String description();

public boolean hasDescription() {
return description() != null;
}

@Override
public int compareTo(MethodArgument other) {
int compareVal = type().compareTo(other.type());
Expand All @@ -57,6 +65,8 @@ public abstract static class Builder {

public abstract Builder setIsResourceNameHelper(boolean isResourceNameHelper);

public abstract Builder setDescription(String description);

public abstract MethodArgument build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,17 @@ public abstract class ResourceName {
@Nullable
public abstract String parentMessageName();

@Nullable
public abstract String description();

public boolean hasParentMessageName() {
return parentMessageName() != null;
}

public boolean hasDescription() {
return description() != null;
}

public String resourceTypeName() {
return resourceTypeString().substring(resourceTypeString().indexOf(SLASH) + 1);
}
Expand Down Expand Up @@ -92,6 +99,7 @@ public boolean equals(Object o) {
}

ResourceName other = (ResourceName) o;
// Exclude the description from the resource name because it's just a comment.
return variableName().equals(other.variableName())
&& pakkage().equals(other.pakkage())
&& resourceTypeString().equals(other.resourceTypeString())
Expand Down Expand Up @@ -125,6 +133,8 @@ public abstract static class Builder {

public abstract Builder setParentMessageName(String parentMessageName);

public abstract Builder setDescription(String description);

// Private setters.
abstract Builder setType(TypeNode type);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import com.google.common.collect.Lists;
import com.google.protobuf.Descriptors.MethodDescriptor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -68,8 +67,8 @@ public static List<List<MethodArgument>> parseMethodSignatures(
// For resource names, this will be empty.
List<TypeNode> argumentTypePathAcc = new ArrayList<>();
// There should be more than one type returned only when we encounter a reousrce name.
List<TypeNode> argumentTypes =
parseTypeFromArgumentName(
Map<TypeNode, String> argumentTypes =
parseTypeAndCommentFromArgumentName(
argumentName,
servicePackage,
inputMessage,
Expand All @@ -84,14 +83,15 @@ public static List<List<MethodArgument>> parseMethodSignatures(
argumentNames.add(actualArgumentName);
argumentNameToOverloads.put(
actualArgumentName,
argumentTypes.stream()
argumentTypes.entrySet().stream()
.map(
type ->
e ->
MethodArgument.builder()
.setName(actualArgumentName)
.setType(type)
.setDescription(e.getValue()) // May be null.
.setType(e.getKey())
.setIsResourceNameHelper(
argumentTypes.size() > 1 && !type.equals(TypeNode.STRING))
argumentTypes.size() > 1 && !e.getKey().equals(TypeNode.STRING))
.setNestedTypes(argumentTypePathAcc)
.build())
.collect(Collectors.toList()));
Expand Down Expand Up @@ -143,7 +143,7 @@ private static List<List<MethodArgument>> flattenMethodSignatureVariants(
return methodArgs;
}

private static List<TypeNode> parseTypeFromArgumentName(
private static Map<TypeNode, String> parseTypeAndCommentFromArgumentName(
String argumentName,
String servicePackage,
Message inputMessage,
Expand All @@ -153,6 +153,8 @@ private static List<TypeNode> parseTypeFromArgumentName(
List<TypeNode> argumentTypePathAcc,
Set<ResourceName> outputArgResourceNames) {

// Comment values may be null.
Map<TypeNode, String> typeToComment = new HashMap<>();
int dotIndex = argumentName.indexOf(DOT);
if (dotIndex < 1) {
Field field = inputMessage.fieldMap().get(argumentName);
Expand All @@ -162,19 +164,27 @@ private static List<TypeNode> parseTypeFromArgumentName(
"Field %s not found from input message %s values %s",
argumentName, inputMessage.name(), inputMessage.fieldMap().keySet()));
if (!field.hasResourceReference()) {
return Arrays.asList(field.type());
typeToComment.put(field.type(), field.description());
return typeToComment;
}

// Parse the resource name tyeps.
List<ResourceName> resourceNameArgs =
ResourceReferenceParser.parseResourceNames(
field.resourceReference(), servicePackage, resourceNames, patternsToResourceNames);
field.resourceReference(),
servicePackage,
field.description(),
resourceNames,
patternsToResourceNames);
outputArgResourceNames.addAll(resourceNameArgs);
List<TypeNode> allFieldTypes = new ArrayList<>();
allFieldTypes.add(TypeNode.STRING);
allFieldTypes.addAll(
resourceNameArgs.stream().map(r -> r.type()).collect(Collectors.toList()));
return allFieldTypes;
typeToComment.put(
TypeNode.STRING,
resourceNameArgs.isEmpty() ? null : resourceNameArgs.get(0).description());
typeToComment.putAll(
resourceNameArgs.stream()
.collect(
Collectors.toMap(r -> r.type(), r -> r.hasDescription() ? r.description() : "")));
return typeToComment;
}

Preconditions.checkState(
Expand Down Expand Up @@ -204,7 +214,7 @@ private static List<TypeNode> parseTypeFromArgumentName(
"Message type %s for field reference %s invalid", firstFieldTypeName, firstFieldName));

argumentTypePathAcc.add(firstFieldType);
return parseTypeFromArgumentName(
return parseTypeAndCommentFromArgumentName(
remainingArgumentName,
servicePackage,
firstFieldMessage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.google.api.generator.gapic.model.ResourceName;
import com.google.api.generator.gapic.model.ResourceReference;
import com.google.api.generator.gapic.model.Service;
import com.google.api.generator.gapic.model.SourceCodeInfoLocation;
import com.google.api.generator.gapic.utils.ResourceNameConstants;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
Expand All @@ -55,6 +56,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
Expand All @@ -64,6 +66,9 @@ public class Parser {
private static final String COLON = ":";
private static final String DEFAULT_PORT = "443";

// Allow other parsers to access this.
protected static final SourceCodeInfoParser SOURCE_CODE_INFO_PARSER = new SourceCodeInfoParser();

static class GapicParserException extends RuntimeException {
public GapicParserException(String errorMessage) {
super(errorMessage);
Expand Down Expand Up @@ -147,7 +152,17 @@ public static List<Service> parseService(
List<String> oauthScopes =
Arrays.asList(serviceOptions.getExtension(ClientProto.oauthScopes).split(COMMA));

return Service.builder()
Service.Builder serviceBuilder = Service.builder();
if (fileDescriptor.toProto().hasSourceCodeInfo()) {
SourceCodeInfoLocation protoServiceLocation =
SOURCE_CODE_INFO_PARSER.getLocation(s);
if (!Objects.isNull(protoServiceLocation)
&& !Strings.isNullOrEmpty(protoServiceLocation.getLeadingComments())) {
serviceBuilder.setDescription(protoServiceLocation.getLeadingComments());
}
}

return serviceBuilder
.setName(s.getName())
.setDefaultHost(defaultHost)
.setOauthScopes(oauthScopes)
Expand Down Expand Up @@ -244,8 +259,18 @@ static List<Method> parseMethods(
for (MethodDescriptor protoMethod : serviceDescriptor.getMethods()) {
// Parse the method.
TypeNode inputType = TypeParser.parseType(protoMethod.getInputType());
Method.Builder methodBuilder = Method.builder();
if (protoMethod.getFile().toProto().hasSourceCodeInfo()) {
SourceCodeInfoLocation protoMethodLocation =
SOURCE_CODE_INFO_PARSER.getLocation(protoMethod);
if (!Objects.isNull(protoMethodLocation)
&& !Strings.isNullOrEmpty(protoMethodLocation.getLeadingComments())) {
methodBuilder.setDescription(protoMethodLocation.getLeadingComments());
}
}

methods.add(
Method.builder()
methodBuilder
.setName(protoMethod.getName())
.setInputType(inputType)
.setOutputType(TypeParser.parseType(protoMethod.getOutputType()))
Expand Down Expand Up @@ -358,7 +383,17 @@ private static Field parseField(FieldDescriptor fieldDescriptor, Descriptor mess
}
}

return Field.builder()
Field.Builder fieldBuilder = Field.builder();
if (fieldDescriptor.getFile().toProto().hasSourceCodeInfo()) {
SourceCodeInfoLocation protoFieldLocation =
SOURCE_CODE_INFO_PARSER.getLocation(fieldDescriptor);
if (!Objects.isNull(protoFieldLocation)
&& !Strings.isNullOrEmpty(protoFieldLocation.getLeadingComments())) {
fieldBuilder.setDescription(protoFieldLocation.getLeadingComments());
}
}

return fieldBuilder
.setName(fieldDescriptor.getName())
.setType(TypeParser.parseType(fieldDescriptor))
.setIsRepeated(fieldDescriptor.isRepeated())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.annotation.Nullable;

public class ResourceReferenceParser {
private static final String EMPTY_STRING = "";
Expand All @@ -39,6 +40,7 @@ public class ResourceReferenceParser {
public static List<ResourceName> parseResourceNames(
ResourceReference resourceReference,
String servicePackage,
@Nullable String description,
Map<String, ResourceName> resourceNames,
Map<String, ResourceName> patternsToResourceNames) {
ResourceName resourceName = resourceNames.get(resourceReference.resourceTypeString());
Expand All @@ -62,6 +64,7 @@ public static List<ResourceName> parseResourceNames(
servicePackage,
resourceName.pakkage(),
resourceName.resourceTypeString(),
description,
patternsToResourceNames);
// Prevent duplicates.
if (parentResourceNameOpt.isPresent()
Expand All @@ -80,6 +83,7 @@ static Optional<ResourceName> parseParentResourceName(
String servicePackage,
String resourcePackage,
String resourceTypeString,
@Nullable String description,
Map<String, ResourceName> patternsToResourceNames) {
Optional<String> parentPatternOpt = parseParentPattern(pattern);
if (!parentPatternOpt.isPresent()) {
Expand Down Expand Up @@ -143,6 +147,7 @@ static Optional<ResourceName> parseParentResourceName(
.setPakkage(pakkage)
.setResourceTypeString(parentResourceTypeString)
.setPatterns(Arrays.asList(parentPattern))
.setDescription(description)
.build();
patternsToResourceNames.put(parentPattern, parentResourceName);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public void setUp() {
public void parseParentResourceName_createFromPattern() {
String resourceNamePackage = String.format("%s.common", MAIN_PACKAGE);
String domainName = "cloudbilling.googleapis.com";
String description = "This is the resource name description";
String resourceTypeString = String.format("%s/BillingAccount", domainName);
String parentResourceTypeString = String.format("%s/Project", domainName);
Map<String, ResourceName> patternsToResourceNames = new HashMap<>();
Expand All @@ -59,6 +60,7 @@ public void parseParentResourceName_createFromPattern() {
MAIN_PACKAGE,
resourceNamePackage,
resourceTypeString,
description,
patternsToResourceNames);
assertTrue(parentResourceNameOpt.isPresent());

Expand All @@ -67,6 +69,7 @@ public void parseParentResourceName_createFromPattern() {
assertEquals(Arrays.asList(parentPattern), parentResourceName.patterns());
assertEquals(parentResourceTypeString, parentResourceName.resourceTypeString());
assertEquals(resourceNamePackage, parentResourceName.pakkage());
assertEquals(description, parentResourceName.description());
assertEquals(
TypeNode.withReference(
VaporReference.builder()
Expand All @@ -93,6 +96,7 @@ public void parseParentResourceName_parentResourceNameExists() {
ResourceReferenceParser.parseParentResourceName(
"projects/{project}/folders/{folder}/documents/{document}",
MAIN_PACKAGE,
null,
MAIN_PACKAGE,
"cloudresourcemanager.googleapis.com/Document",
patternsToResourceNames);
Expand All @@ -109,6 +113,7 @@ public void parseParentResourceName_badPattern() {
ResourceReferenceParser.parseParentResourceName(
"projects/{project}/billingAccounts",
MAIN_PACKAGE,
null,
MAIN_PACKAGE,
"cloudbilling.googleapis.com/Feature",
new HashMap<String, ResourceName>());
Expand Down