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

Merged
merged 10 commits into from
Aug 7, 2020
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