Skip to content

Commit

Permalink
[ggj] feat: extract arg resource names and parse parents (#158)
Browse files Browse the repository at this point in the history
* feat: add initial resource name def parsing

* feat: add early failure for missing fields

* feat: couple resnames and message parsing

* fix!: refactor method arg parsing away from class creation

* feat: add resource_reference parsing

* fix: dot name parsing

* feat: extract arg resource names and parse parents
  • Loading branch information
miraleung authored Aug 7, 2020
1 parent a8cf057 commit 8e31e27
Show file tree
Hide file tree
Showing 16 changed files with 583 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

@AutoValue
public abstract class GapicContext {
Expand All @@ -30,6 +32,8 @@ public abstract class GapicContext {

public abstract ImmutableList<Service> services();

public abstract ImmutableSet<ResourceName> helperResourceNames();

public static Builder builder() {
return new AutoValue_GapicContext.Builder();
}
Expand All @@ -42,6 +46,8 @@ public abstract static class Builder {

public abstract Builder setServices(List<Service> services);

public abstract Builder setHelperResourceNames(Set<ResourceName> helperResourceNames);

public abstract GapicContext build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,26 @@

package com.google.api.generator.gapic.model;

import com.google.api.generator.engine.ast.TypeNode;
import com.google.api.generator.engine.ast.VaporReference;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.Objects;
import javax.annotation.Nullable;

@AutoValue
public abstract class ResourceName {
static final String SLASH = "/";

// The original binding variable name.
// This should be in lower_snake_case in the proto, and expected to be surrounded by braces.
// Example: In projects/{project}/billingAccounts/billing_account, the variable name would be
// "billing_account."
public abstract String variableName();

// The Java package where the resource name was defined.
// The Java package of the project, or that of a subpackage where the resource name was defined.
// That is, resource names defined outside of this project will still have the project's package.
public abstract String pakkage();

// The resource type.
Expand All @@ -36,6 +42,9 @@ public abstract class ResourceName {
// A list of patterns such as projects/{project}/locations/{location}/resources/{this_resource}.
public abstract ImmutableList<String> patterns();

// The Java TypeNode of the resource name helper class to generate.
public abstract TypeNode type();

// The message in which this resource was defined. Optional.
// This is expected to be empty for file-level definitions.
@Nullable
Expand All @@ -49,6 +58,34 @@ public static Builder builder() {
return new AutoValue_ResourceName.Builder();
}

@Override
public boolean equals(Object o) {
if (!(o instanceof ResourceName)) {
return false;
}

ResourceName other = (ResourceName) o;
return variableName().equals(other.variableName())
&& pakkage().equals(other.pakkage())
&& resourceTypeString().equals(other.resourceTypeString())
&& patterns().equals(other.patterns())
&& Objects.equals(parentMessageName(), other.parentMessageName())
&& Objects.equals(type(), other.type());
}

@Override
public int hashCode() {
int parentMessageNameHashCode =
parentMessageName() == null ? 0 : parentMessageName().hashCode();
int typeHashCode = type() == null ? 0 : type().hashCode();
return 17 * variableName().hashCode()
+ 19 * pakkage().hashCode()
+ 23 * resourceTypeString().hashCode()
+ 31 * patterns().hashCode()
+ 37 * parentMessageNameHashCode
+ 41 * typeHashCode;
}

@AutoValue.Builder
public abstract static class Builder {
public abstract Builder setVariableName(String variableName);
Expand All @@ -61,6 +98,23 @@ public abstract static class Builder {

public abstract Builder setParentMessageName(String parentMessageName);

public abstract ResourceName build();
// Private setter.
abstract Builder setType(TypeNode type);

// Private accessors.
abstract String pakkage();

abstract String resourceTypeString();

// Private.
abstract ResourceName autoBuild();

public ResourceName build() {
String typeName = resourceTypeString().substring(resourceTypeString().lastIndexOf(SLASH) + 1);
setType(
TypeNode.withReference(
VaporReference.builder().setName(typeName).setPakkage(pakkage()).build()));
return autoBuild();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,16 @@
import com.google.api.generator.gapic.model.Field;
import com.google.api.generator.gapic.model.Message;
import com.google.api.generator.gapic.model.MethodArgument;
import com.google.api.generator.gapic.model.ResourceName;
import com.google.common.base.Preconditions;
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;
import java.util.Set;
import java.util.stream.Collectors;

// TODO(miraleung): Add tests for this class. Currently exercised in integration tests.
public class MethodSignatureParser {
Expand All @@ -34,8 +38,11 @@ public class MethodSignatureParser {
/** Parses a list of method signature annotations out of an RPC. */
public static List<List<MethodArgument>> parseMethodSignatures(
MethodDescriptor methodDescriptor,
String servicePackage,
TypeNode methodInputType,
Map<String, Message> messageTypes) {
Map<String, Message> messageTypes,
Map<String, ResourceName> resourceNames,
Set<ResourceName> outputArgResourceNames) {
List<String> stringSigs =
methodDescriptor.getOptions().getExtension(ClientProto.methodSignature);

Expand All @@ -44,6 +51,7 @@ public static List<List<MethodArgument>> parseMethodSignatures(
return signatures;
}

Map<String, ResourceName> patternsToResourceNames = createPatternResourceNameMap(resourceNames);
String methodInputTypeName = methodInputType.reference().name();
Message inputMessage = messageTypes.get(methodInputTypeName);

Expand All @@ -54,7 +62,15 @@ public static List<List<MethodArgument>> parseMethodSignatures(
List<MethodArgument> arguments = new ArrayList<>();
for (String argumentName : stringSig.split(METHOD_SIGNATURE_DELIMITER)) {
List<TypeNode> argumentTypePath =
new ArrayList<>(parseTypeFromArgumentName(argumentName, inputMessage, messageTypes));
new ArrayList<>(
parseTypeFromArgumentName(
argumentName,
servicePackage,
inputMessage,
messageTypes,
resourceNames,
patternsToResourceNames,
outputArgResourceNames));

int dotLastIndex = argumentName.lastIndexOf(DOT);
String actualArgumentName =
Expand All @@ -77,14 +93,32 @@ public static List<List<MethodArgument>> parseMethodSignatures(
}

private static List<TypeNode> parseTypeFromArgumentName(
String argumentName, Message inputMessage, Map<String, Message> messageTypes) {
return parseTypeFromArgumentName(argumentName, inputMessage, messageTypes, new ArrayList<>());
String argumentName,
String servicePackage,
Message inputMessage,
Map<String, Message> messageTypes,
Map<String, ResourceName> resourceNames,
Map<String, ResourceName> patternsToResourceNames,
Set<ResourceName> outputArgResourceNames) {
return parseTypeFromArgumentName(
argumentName,
servicePackage,
inputMessage,
messageTypes,
resourceNames,
patternsToResourceNames,
outputArgResourceNames,
new ArrayList<>());
}

private static List<TypeNode> parseTypeFromArgumentName(
String argumentName,
String servicePackage,
Message inputMessage,
Map<String, Message> messageTypes,
Map<String, ResourceName> resourceNames,
Map<String, ResourceName> patternsToResourceNames,
Set<ResourceName> outputArgResourceNames,
List<TypeNode> typeAcc) {
int dotIndex = argumentName.indexOf(DOT);
// TODO(miraleung): Fake out resource names here.
Expand All @@ -103,6 +137,17 @@ private static List<TypeNode> parseTypeFromArgumentName(

// Must be a sub-message for a type's subfield to be valid.
Field firstField = inputMessage.fieldMap().get(firstFieldName);
if (firstField.hasResourceReference()) {
List<ResourceName> resourceNameArgs =
ResourceReferenceParser.parseResourceNames(
firstField.resourceReference(),
servicePackage,
resourceNames,
patternsToResourceNames);
outputArgResourceNames.addAll(resourceNameArgs);
return resourceNameArgs.stream().map(r -> r.type()).collect(Collectors.toList());
}

Preconditions.checkState(
!firstField.isRepeated(),
String.format("Cannot descend into repeated field %s", firstField.name()));
Expand All @@ -122,6 +167,24 @@ private static List<TypeNode> parseTypeFromArgumentName(
List<TypeNode> newAcc = new ArrayList<>(typeAcc);
newAcc.add(firstFieldType);
return parseTypeFromArgumentName(
remainingArgumentName, firstFieldMessage, messageTypes, newAcc);
remainingArgumentName,
servicePackage,
firstFieldMessage,
messageTypes,
resourceNames,
patternsToResourceNames,
outputArgResourceNames,
newAcc);
}

private static Map<String, ResourceName> createPatternResourceNameMap(
Map<String, ResourceName> resourceNames) {
Map<String, ResourceName> patternsToResourceNames = new HashMap<>();
for (ResourceName resourceName : resourceNames.values()) {
for (String pattern : resourceName.patterns()) {
patternsToResourceNames.put(pattern, resourceName);
}
}
return patternsToResourceNames;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,10 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public class Parser {
Expand All @@ -65,16 +67,22 @@ public static GapicContext parse(CodeGeneratorRequest request) {
Map<String, Message> messages = parseMessages(request);
Map<String, ResourceName> resourceNames = parseResourceNames(request);
messages = updateResourceNamesInMessages(messages, resourceNames.values());
List<Service> services = parseServices(request, messages);
Set<ResourceName> outputArgResourceNames = new HashSet<>();
List<Service> services =
parseServices(request, messages, resourceNames, outputArgResourceNames);
return GapicContext.builder()
.setServices(services)
.setMessages(messages)
.setResourceNames(resourceNames)
.setHelperResourceNames(outputArgResourceNames)
.build();
}

public static List<Service> parseServices(
CodeGeneratorRequest request, Map<String, Message> messageTypes) {
CodeGeneratorRequest request,
Map<String, Message> messageTypes,
Map<String, ResourceName> resourceNames,
Set<ResourceName> outputArgResourceNames) {
Map<String, FileDescriptor> fileDescriptors = getFilesToGenerate(request);
List<Service> services = new ArrayList<>();
for (String fileToGenerate : request.getFileToGenerateList()) {
Expand All @@ -84,14 +92,18 @@ public static List<Service> parseServices(
"Missing file descriptor for [%s]",
fileToGenerate);

services.addAll(parseService(fileDescriptor, messageTypes));
services.addAll(
parseService(fileDescriptor, messageTypes, resourceNames, outputArgResourceNames));
}

return services;
}

public static List<Service> parseService(
FileDescriptor fileDescriptor, Map<String, Message> messageTypes) {
FileDescriptor fileDescriptor,
Map<String, Message> messageTypes,
Map<String, ResourceName> resourceNames,
Set<ResourceName> outputArgResourceNames) {
String pakkage = TypeParser.getPackage(fileDescriptor);
return fileDescriptor.getServices().stream()
.map(
Expand All @@ -100,7 +112,9 @@ public static List<Service> parseService(
.setName(s.getName())
.setPakkage(pakkage)
.setProtoPakkage(fileDescriptor.getPackage())
.setMethods(parseMethods(s, messageTypes))
.setMethods(
parseMethods(
s, pakkage, messageTypes, resourceNames, outputArgResourceNames))
.build())
.collect(Collectors.toList());
}
Expand Down Expand Up @@ -168,14 +182,23 @@ public static Map<String, ResourceName> parseResourceNames(CodeGeneratorRequest
fileDescriptors.get(fileToGenerate),
"Missing file descriptor for [%s]",
fileToGenerate);
resourceNames.putAll(ResourceNameParser.parseResourceNames(fileDescriptor));
resourceNames.putAll(parseResourceNames(fileDescriptor));
}
return resourceNames;
}

// Convenience wrapper for package-external unit tests.
public static Map<String, ResourceName> parseResourceNames(FileDescriptor fileDescriptor) {
return ResourceNameParser.parseResourceNames(fileDescriptor);
}

@VisibleForTesting
static List<Method> parseMethods(
ServiceDescriptor serviceDescriptor, Map<String, Message> messageTypes) {
ServiceDescriptor serviceDescriptor,
String servicePackage,
Map<String, Message> messageTypes,
Map<String, ResourceName> resourceNames,
Set<ResourceName> outputArgResourceNames) {
return serviceDescriptor.getMethods().stream()
.map(
md -> {
Expand All @@ -187,7 +210,13 @@ static List<Method> parseMethods(
.setStream(Method.toStream(md.isClientStreaming(), md.isServerStreaming()))
.setLro(parseLro(md, messageTypes))
.setMethodSignatures(
MethodSignatureParser.parseMethodSignatures(md, inputType, messageTypes))
MethodSignatureParser.parseMethodSignatures(
md,
servicePackage,
inputType,
messageTypes,
resourceNames,
outputArgResourceNames))
.setIsPaged(parseIsPaged(md, messageTypes))
.build();
})
Expand Down
Loading

0 comments on commit 8e31e27

Please sign in to comment.