Skip to content

Commit

Permalink
[ggj] feat: flatten sigs, handle wildcards, update Client methods (#160)
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

* feat: flatten sigs, handle wildcards, update Client methods
  • Loading branch information
miraleung authored Aug 7, 2020
1 parent 8e31e27 commit 0aa8336
Show file tree
Hide file tree
Showing 13 changed files with 550 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ java_library(
deps = [
"//src/main/java/com/google/api/generator:autovalue",
"//src/main/java/com/google/api/generator/engine/ast",
"//src/main/java/com/google/api/generator/gapic/utils",
"@com_google_auto_value_auto_value//jar",
"@com_google_auto_value_auto_value_annotations//jar",
"@com_google_code_findbugs_jsr305//jar",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,13 @@ public abstract class MethodArgument {
// appeared as the last element).
public abstract ImmutableList<TypeNode> nestedTypes();

// Returns true if this is a resource name helper tyep.
public abstract boolean isResourceNameHelper();

public static Builder builder() {
return new AutoValue_MethodArgument.Builder().setNestedTypes(ImmutableList.of());
return new AutoValue_MethodArgument.Builder()
.setNestedTypes(ImmutableList.of())
.setIsResourceNameHelper(false);
}

@AutoValue.Builder
Expand All @@ -41,6 +46,8 @@ public abstract static class Builder {

public abstract Builder setNestedTypes(List<TypeNode> nestedTypes);

public abstract Builder setIsResourceNameHelper(boolean isResourceNameHelper);

public abstract MethodArgument build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

import com.google.api.generator.engine.ast.TypeNode;
import com.google.api.generator.engine.ast.VaporReference;
import com.google.api.generator.gapic.utils.JavaStyle;
import com.google.api.generator.gapic.utils.ResourceNameConstants;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import java.util.List;
Expand Down Expand Up @@ -45,6 +47,8 @@ public abstract class ResourceName {
// The Java TypeNode of the resource name helper class to generate.
public abstract TypeNode type();

public abstract boolean isOnlyWildcard();

// The message in which this resource was defined. Optional.
// This is expected to be empty for file-level definitions.
@Nullable
Expand All @@ -55,7 +59,27 @@ public boolean hasParentMessageName() {
}

public static Builder builder() {
return new AutoValue_ResourceName.Builder();

return new AutoValue_ResourceName.Builder().setIsOnlyWildcard(false);
}

public static ResourceName createWildcard(String resourceTypeString, String pakkage) {
String placeholderVarName =
JavaStyle.toLowerCamelCase(
resourceTypeString.substring(resourceTypeString.indexOf(SLASH) + 1));
return builder()
.setVariableName(placeholderVarName)
.setPakkage(pakkage)
.setResourceTypeString(resourceTypeString)
.setPatterns(ImmutableList.of(ResourceNameConstants.WILDCARD_PATTERN))
.setIsOnlyWildcard(true)
.setType(
TypeNode.withReference(
VaporReference.builder()
.setName("ResourceName")
.setPakkage("com.google.api.resourcenames")
.build()))
.build();
}

@Override
Expand Down Expand Up @@ -98,22 +122,32 @@ public abstract static class Builder {

public abstract Builder setParentMessageName(String parentMessageName);

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

abstract Builder setIsOnlyWildcard(boolean isOnlyWildcard);

// Private accessors.
abstract String pakkage();

abstract String resourceTypeString();

abstract boolean isOnlyWildcard();

// 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()));
if (!isOnlyWildcard()) {
String typeName =
resourceTypeString().substring(resourceTypeString().lastIndexOf(SLASH) + 1);
setType(
TypeNode.withReference(
VaporReference.builder()
.setName(String.format("%sName", typeName))
.setPakkage(pakkage())
.build()));
}
return autoBuild();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
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.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.protobuf.Descriptors.MethodDescriptor;
import java.util.ArrayList;
import java.util.Arrays;
Expand Down Expand Up @@ -56,40 +58,89 @@ public static List<List<MethodArgument>> parseMethodSignatures(
Message inputMessage = messageTypes.get(methodInputTypeName);

// Example from Expand in echo.proto:
// Input: ["content,error", "content,error,info"].
// Output: [["content", "error"], ["content", "error", "info"]].
// stringSigs: ["content,error", "content,error,info"].
for (String stringSig : stringSigs) {
List<MethodArgument> arguments = new ArrayList<>();
for (String argumentName : stringSig.split(METHOD_SIGNATURE_DELIMITER)) {
List<TypeNode> argumentTypePath =
new ArrayList<>(
parseTypeFromArgumentName(
argumentName,
servicePackage,
inputMessage,
messageTypes,
resourceNames,
patternsToResourceNames,
outputArgResourceNames));
List<String> argumentNames = new ArrayList<>();
Map<String, List<MethodArgument>> argumentNameToOverloads = new HashMap<>();

// stringSig.split: ["content", "error"].
for (String argumentName : stringSig.split(METHOD_SIGNATURE_DELIMITER)) {
// 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(
argumentName,
servicePackage,
inputMessage,
messageTypes,
resourceNames,
patternsToResourceNames,
argumentTypePathAcc,
outputArgResourceNames);
int dotLastIndex = argumentName.lastIndexOf(DOT);
String actualArgumentName =
dotLastIndex < 0 ? argumentName : argumentName.substring(dotLastIndex + 1);
argumentNames.add(actualArgumentName);
argumentNameToOverloads.put(
actualArgumentName,
argumentTypes.stream()
.map(
type ->
MethodArgument.builder()
.setName(actualArgumentName)
.setType(type)
.setIsResourceNameHelper(
argumentTypes.size() > 1 && !type.equals(TypeNode.STRING))
.setNestedTypes(argumentTypePathAcc)
.build())
.collect(Collectors.toList()));
}
signatures.addAll(flattenMethodSignatureVariants(argumentNames, argumentNameToOverloads));
}
return signatures;
}

int typeLastIndex = argumentTypePath.size() - 1;
TypeNode argumentType = argumentTypePath.get(typeLastIndex);
argumentTypePath.remove(typeLastIndex);
@VisibleForTesting
static List<List<MethodArgument>> flattenMethodSignatureVariants(
List<String> argumentNames, Map<String, List<MethodArgument>> argumentNameToOverloads) {
Preconditions.checkState(
argumentNames.size() == argumentNameToOverloads.size(),
String.format(
"Cardinality of argument names %s do not match that of overloaded types %s",
argumentNames, argumentNameToOverloads));
for (String name : argumentNames) {
Preconditions.checkNotNull(
argumentNameToOverloads.get(name),
String.format("No corresponding overload types found for argument %s", name));
}
return flattenMethodSignatureVariants(argumentNames, argumentNameToOverloads, 0);
}

arguments.add(
MethodArgument.builder()
.setName(actualArgumentName)
.setType(argumentType)
.setNestedTypes(argumentTypePath)
.build());
private static List<List<MethodArgument>> flattenMethodSignatureVariants(
List<String> argumentNames,
Map<String, List<MethodArgument>> argumentNameToOverloads,
int depth) {
List<List<MethodArgument>> methodArgs = new ArrayList<>();
if (depth >= argumentNames.size() - 1) {
for (MethodArgument methodArg : argumentNameToOverloads.get(argumentNames.get(depth))) {
methodArgs.add(Lists.newArrayList(methodArg));
}
signatures.add(arguments);
return methodArgs;
}
return signatures;

List<List<MethodArgument>> subsequentArgs =
flattenMethodSignatureVariants(argumentNames, argumentNameToOverloads, depth + 1);
for (MethodArgument methodArg : argumentNameToOverloads.get(argumentNames.get(depth))) {
for (List<MethodArgument> subsequentArg : subsequentArgs) {
// Use a new list to avoid appending all subsequent elements (in upcoming loop iterations)
// to the same list.
List<MethodArgument> appendedArgs = new ArrayList<>(subsequentArg);
appendedArgs.add(0, methodArg);
methodArgs.add(appendedArgs);
}
}
return methodArgs;
}

private static List<TypeNode> parseTypeFromArgumentName(
Expand All @@ -99,35 +150,33 @@ private static List<TypeNode> parseTypeFromArgumentName(
Map<String, Message> messageTypes,
Map<String, ResourceName> resourceNames,
Map<String, ResourceName> patternsToResourceNames,
List<TypeNode> argumentTypePathAcc,
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.
if (dotIndex < 1) {
Field field = inputMessage.fieldMap().get(argumentName);
Preconditions.checkNotNull(
field, String.format("Field %s not found, %s", argumentName, inputMessage.fieldMap()));
return Arrays.asList(field.type());
field,
String.format(
"Field %s not found from input message %s values %s",
argumentName, inputMessage.name(), inputMessage.fieldMap().keySet()));
if (!field.hasResourceReference()) {
return Arrays.asList(field.type());
}

// Parse the resource name tyeps.
List<ResourceName> resourceNameArgs =
ResourceReferenceParser.parseResourceNames(
field.resourceReference(), servicePackage, 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;
}

Preconditions.checkState(
dotIndex < argumentName.length() - 1,
String.format(
Expand All @@ -137,16 +186,6 @@ 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(),
Expand All @@ -164,17 +203,16 @@ private static List<TypeNode> parseTypeFromArgumentName(
String.format(
"Message type %s for field reference %s invalid", firstFieldTypeName, firstFieldName));

List<TypeNode> newAcc = new ArrayList<>(typeAcc);
newAcc.add(firstFieldType);
argumentTypePathAcc.add(firstFieldType);
return parseTypeFromArgumentName(
remainingArgumentName,
servicePackage,
firstFieldMessage,
messageTypes,
resourceNames,
patternsToResourceNames,
outputArgResourceNames,
newAcc);
argumentTypePathAcc,
outputArgResourceNames);
}

private static Map<String, ResourceName> createPatternResourceNameMap(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ private static Optional<ResourceName> createResourceName(
protoResource.getType()));

if (patterns.size() == 1 && patterns.get(0).equals(ResourceNameConstants.WILDCARD_PATTERN)) {
return Optional.empty();
return Optional.of(ResourceName.createWildcard(protoResource.getType(), pakkage));
}

// Assuming that both patterns end with the same variable name.
Expand Down
Loading

0 comments on commit 0aa8336

Please sign in to comment.