Skip to content

Commit 4f535a7

Browse files
authored
feat: add parsing of autopopulated fields from serviceyaml (#2312)
* feat: add parsing of autopopulated fields from serviceyaml * fix lint * fix lint and update goldens * refactor * add todo * fix lint * update comment * add unit tests * update hashcode() * update comments * remove empty check * remove unit test * fix lint * add todo
1 parent 3d885ba commit 4f535a7

22 files changed

+368
-7
lines changed

gapic-generator-java/src/main/java/com/google/api/generator/gapic/model/Field.java

+19
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

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

17+
import com.google.api.FieldInfo.Format;
1718
import com.google.api.generator.engine.ast.TypeNode;
1819
import com.google.auto.value.AutoValue;
1920
import java.util.Objects;
@@ -32,6 +33,15 @@ public abstract class Field {
3233

3334
public abstract TypeNode type();
3435

36+
// If the field is annotated with google.api.field_behavior = REQUIRED, then this is true. This is
37+
// currently only used to check if a field should be auto-populated. If it is true, then the field
38+
// should
39+
// *not* be autopopulated.
40+
public abstract boolean isRequired();
41+
42+
@Nullable
43+
public abstract Format fieldInfoFormat();
44+
3545
public abstract boolean isMessage();
3646

3747
public abstract boolean isEnum();
@@ -72,6 +82,8 @@ public boolean equals(Object o) {
7282
return name().equals(other.name())
7383
&& originalName().equals(other.originalName())
7484
&& type().equals(other.type())
85+
&& isRequired() == other.isRequired()
86+
&& fieldInfoFormat() == other.fieldInfoFormat()
7587
&& isMessage() == other.isMessage()
7688
&& isEnum() == other.isEnum()
7789
&& isRepeated() == other.isRepeated()
@@ -89,6 +101,8 @@ public int hashCode() {
89101
+ 19 * type().hashCode()
90102
+ (isMessage() ? 1 : 0) * 23
91103
+ (isEnum() ? 1 : 0) * 29
104+
+ (isRequired() ? 1 : 0) * 31
105+
+ (fieldInfoFormat() == null ? 0 : fieldInfoFormat().hashCode())
92106
+ (isRepeated() ? 1 : 0) * 31
93107
+ (isMap() ? 1 : 0) * 37
94108
+ (isContainedInOneof() ? 1 : 0) * 41
@@ -101,6 +115,7 @@ public int hashCode() {
101115

102116
public static Builder builder() {
103117
return new AutoValue_Field.Builder()
118+
.setIsRequired(false)
104119
.setIsMessage(false)
105120
.setIsEnum(false)
106121
.setIsRepeated(false)
@@ -117,6 +132,10 @@ public abstract static class Builder {
117132

118133
public abstract Builder setType(TypeNode type);
119134

135+
public abstract Builder setIsRequired(boolean isRequired);
136+
137+
public abstract Builder setFieldInfoFormat(Format fieldInfoFormat);
138+
120139
public abstract Builder setIsMessage(boolean isMessage);
121140

122141
public abstract Builder setIsEnum(boolean isEnum);

gapic-generator-java/src/main/java/com/google/api/generator/gapic/model/Method.java

+14
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import com.google.api.generator.engine.ast.TypeNode;
1818
import com.google.auto.value.AutoValue;
1919
import com.google.common.collect.ImmutableList;
20+
import java.util.ArrayList;
2021
import java.util.List;
2122
import javax.annotation.Nullable;
2223

@@ -69,6 +70,16 @@ public boolean isPaged() {
6970
// [["content", "error"], ["content", "error", "info"]].
7071
public abstract ImmutableList<List<MethodArgument>> methodSignatures();
7172

73+
public abstract List<String> autoPopulatedFields();
74+
75+
/**
76+
* If a service's service_config.yaml file contains method_settings.auto_populated_fields for this
77+
* method, and the method is a Unary-type, then this is true
78+
*/
79+
public boolean hasAutoPopulatedFields() {
80+
return !autoPopulatedFields().isEmpty() && stream() == Stream.NONE;
81+
}
82+
7283
public abstract boolean operationPollingMethod();
7384

7485
public boolean hasLro() {
@@ -123,6 +134,7 @@ public boolean isSupportedByTransport(Transport transport) {
123134
public static Builder builder() {
124135
return new AutoValue_Method.Builder()
125136
.setStream(Stream.NONE)
137+
.setAutoPopulatedFields(new ArrayList<>())
126138
.setMethodSignatures(ImmutableList.of())
127139
.setIsBatching(false)
128140
.setIsDeprecated(false)
@@ -170,6 +182,8 @@ public abstract static class Builder {
170182

171183
public abstract Builder setOperationPollingMethod(boolean operationPollingMethod);
172184

185+
public abstract Builder setAutoPopulatedFields(List<String> autoPopulatedFields);
186+
173187
public abstract Builder setRoutingHeaderRule(RoutingHeaderRule routingHeaderRule);
174188

175189
public abstract Method build();

gapic-generator-java/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java

+55
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@
1616

1717
import com.google.api.ClientProto;
1818
import com.google.api.DocumentationRule;
19+
import com.google.api.FieldBehavior;
20+
import com.google.api.FieldBehaviorProto;
21+
import com.google.api.FieldInfo.Format;
22+
import com.google.api.FieldInfoProto;
1923
import com.google.api.HttpRule;
24+
import com.google.api.MethodSettings;
2025
import com.google.api.ResourceDescriptor;
2126
import com.google.api.ResourceProto;
2227
import com.google.api.generator.engine.ast.TypeNode;
@@ -47,6 +52,7 @@
4752
import com.google.common.collect.BiMap;
4853
import com.google.common.collect.HashBiMap;
4954
import com.google.common.collect.ImmutableList;
55+
import com.google.common.collect.ImmutableMap;
5056
import com.google.common.collect.ImmutableSet;
5157
import com.google.common.collect.Maps;
5258
import com.google.longrunning.OperationInfo;
@@ -493,6 +499,7 @@ public static List<Service> parseService(
493499
messageTypes,
494500
resourceNames,
495501
serviceConfigOpt,
502+
serviceYamlProtoOpt,
496503
outputArgResourceNames,
497504
transport))
498505
.build();
@@ -683,9 +690,15 @@ static List<Method> parseMethods(
683690
Map<String, Message> messageTypes,
684691
Map<String, ResourceName> resourceNames,
685692
Optional<GapicServiceConfig> serviceConfigOpt,
693+
Optional<com.google.api.Service> serviceYamlProtoOpt,
686694
Set<ResourceName> outputArgResourceNames,
687695
Transport transport) {
688696
List<Method> methods = new ArrayList<>();
697+
698+
// Parse the serviceYaml for autopopulated methods and fields once and put into a map
699+
Map<String, List<String>> autoPopulatedMethodsWithFields =
700+
parseAutoPopulatedMethodsAndFields(serviceYamlProtoOpt);
701+
689702
for (MethodDescriptor protoMethod : serviceDescriptor.getMethods()) {
690703
// Parse the method.
691704
TypeNode inputType = TypeParser.parseType(protoMethod.getInputType());
@@ -699,6 +712,12 @@ static List<Method> parseMethods(
699712
}
700713
}
701714

715+
// Associate the autopopulated fields with the correct method
716+
List<String> autoPopulatedFields = new ArrayList<>();
717+
if (autoPopulatedMethodsWithFields.containsKey(protoMethod.getFullName())) {
718+
autoPopulatedFields = autoPopulatedMethodsWithFields.get(protoMethod.getFullName());
719+
}
720+
702721
boolean isDeprecated = false;
703722
if (protoMethod.getOptions().hasDeprecated()) {
704723
isDeprecated = protoMethod.getOptions().getDeprecated();
@@ -743,6 +762,7 @@ static List<Method> parseMethods(
743762
resourceNames,
744763
outputArgResourceNames))
745764
.setHttpBindings(httpBindings)
765+
.setAutoPopulatedFields(autoPopulatedFields)
746766
.setRoutingHeaderRule(routingHeaderRule)
747767
.setIsBatching(isBatching)
748768
.setPageSizeFieldName(parsePageSizeFieldName(protoMethod, messageTypes, transport))
@@ -970,6 +990,8 @@ private static Field parseField(
970990
FieldOptions fieldOptions = fieldDescriptor.getOptions();
971991
MessageOptions messageOptions = messageDescriptor.getOptions();
972992
ResourceReference resourceReference = null;
993+
boolean isRequired = false;
994+
Format fieldInfoFormat = null;
973995
if (fieldOptions.hasExtension(ResourceProto.resourceReference)) {
974996
com.google.api.ResourceReference protoResourceReference =
975997
fieldOptions.getExtension(ResourceProto.resourceReference);
@@ -1000,6 +1022,16 @@ private static Field parseField(
10001022
}
10011023
}
10021024

1025+
if (fieldOptions.hasExtension(FieldInfoProto.fieldInfo)) {
1026+
fieldInfoFormat = fieldOptions.getExtension(FieldInfoProto.fieldInfo).getFormat();
1027+
}
1028+
if (fieldOptions.getExtensionCount(FieldBehaviorProto.fieldBehavior) > 0) {
1029+
if (fieldOptions
1030+
.getExtension(FieldBehaviorProto.fieldBehavior)
1031+
.contains(FieldBehavior.REQUIRED)) ;
1032+
isRequired = true;
1033+
}
1034+
10031035
Field.Builder fieldBuilder = Field.builder();
10041036
if (fieldDescriptor.getFile().toProto().hasSourceCodeInfo()) {
10051037
SourceCodeInfoLocation protoFieldLocation =
@@ -1030,6 +1062,8 @@ private static Field parseField(
10301062
fieldDescriptor.getContainingOneof() != null
10311063
&& fieldDescriptor.getContainingOneof().isSynthetic())
10321064
.setIsRepeated(fieldDescriptor.isRepeated())
1065+
.setIsRequired(isRequired)
1066+
.setFieldInfoFormat(fieldInfoFormat)
10331067
.setIsMap(fieldDescriptor.isMapField())
10341068
.setResourceReference(resourceReference)
10351069
.build();
@@ -1124,4 +1158,25 @@ static String parseNestedProtoTypeName(String fullyQualifiedName) {
11241158
.collect(Collectors.toList());
11251159
return String.join(".", nestedTypeComponents);
11261160
}
1161+
1162+
/**
1163+
* Converts a serviceYaml file to a map of methods and autopopulated fields. Note: this does NOT
1164+
* currently support wildcards in MethodSettings.selectors.
1165+
*/
1166+
@VisibleForTesting
1167+
static Map<String, List<String>> parseAutoPopulatedMethodsAndFields(
1168+
Optional<com.google.api.Service> serviceYamlProtoOpt) {
1169+
if (!hasMethodSettings(serviceYamlProtoOpt)) {
1170+
return ImmutableMap.<String, List<String>>builder().build();
1171+
}
1172+
return serviceYamlProtoOpt.get().getPublishing().getMethodSettingsList().stream()
1173+
.collect(
1174+
Collectors.toMap(
1175+
MethodSettings::getSelector, MethodSettings::getAutoPopulatedFieldsList));
1176+
}
1177+
1178+
@VisibleForTesting
1179+
static boolean hasMethodSettings(Optional<com.google.api.Service> serviceYamlProtoOpt) {
1180+
return serviceYamlProtoOpt.isPresent() && serviceYamlProtoOpt.get().hasPublishing();
1181+
}
11271182
}

gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/defaultvalue/DefaultValueComposerTest.java

+1
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,7 @@ public void createSimpleMessage_containsMessagesEnumsAndResourceName() {
568568
"EchoRequest.newBuilder().setName("
569569
+ "FoobarName.ofProjectFoobarName(\"[PROJECT]\", \"[FOOBAR]\").toString())"
570570
+ ".setParent(FoobarName.ofProjectFoobarName(\"[PROJECT]\", \"[FOOBAR]\").toString())"
571+
+ ".setRequestId(\"requestId693933066\")"
571572
+ ".setSeverity(Severity.forNumber(0))"
572573
+ ".setFoobar(Foobar.newBuilder().build()).build()",
573574
writerVisitor.write());

gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/EchoClient.golden

+12-1
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,7 @@ public class EchoClient implements BackgroundResource {
516516
* EchoRequest.newBuilder()
517517
* .setName(FoobarName.ofProjectFoobarName("[PROJECT]", "[FOOBAR]").toString())
518518
* .setParent(FoobarName.ofProjectFoobarName("[PROJECT]", "[FOOBAR]").toString())
519+
* .setRequestId("requestId693933066")
519520
* .setSeverity(Severity.forNumber(0))
520521
* .setFoobar(Foobar.newBuilder().build())
521522
* .build();
@@ -545,6 +546,7 @@ public class EchoClient implements BackgroundResource {
545546
* EchoRequest.newBuilder()
546547
* .setName(FoobarName.ofProjectFoobarName("[PROJECT]", "[FOOBAR]").toString())
547548
* .setParent(FoobarName.ofProjectFoobarName("[PROJECT]", "[FOOBAR]").toString())
549+
* .setRequestId("requestId693933066")
548550
* .setSeverity(Severity.forNumber(0))
549551
* .setFoobar(Foobar.newBuilder().build())
550552
* .build();
@@ -570,7 +572,11 @@ public class EchoClient implements BackgroundResource {
570572
* // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library
571573
* try (EchoClient echoClient = EchoClient.create()) {
572574
* ExpandRequest request =
573-
* ExpandRequest.newBuilder().setContent("content951530617").setInfo("info3237038").build();
575+
* ExpandRequest.newBuilder()
576+
* .setContent("content951530617")
577+
* .setInfo("info3237038")
578+
* .setRequestId("requestId693933066")
579+
* .build();
574580
* ServerStream<EchoResponse> stream = echoClient.expandCallable().call(request);
575581
* for (EchoResponse response : stream) {
576582
* // Do something when a response is received.
@@ -616,6 +622,7 @@ public class EchoClient implements BackgroundResource {
616622
* EchoRequest.newBuilder()
617623
* .setName(FoobarName.ofProjectFoobarName("[PROJECT]", "[FOOBAR]").toString())
618624
* .setParent(FoobarName.ofProjectFoobarName("[PROJECT]", "[FOOBAR]").toString())
625+
* .setRequestId("requestId693933066")
619626
* .setSeverity(Severity.forNumber(0))
620627
* .setFoobar(Foobar.newBuilder().build())
621628
* .build();
@@ -643,6 +650,7 @@ public class EchoClient implements BackgroundResource {
643650
* EchoRequest.newBuilder()
644651
* .setName(FoobarName.ofProjectFoobarName("[PROJECT]", "[FOOBAR]").toString())
645652
* .setParent(FoobarName.ofProjectFoobarName("[PROJECT]", "[FOOBAR]").toString())
653+
* .setRequestId("requestId693933066")
646654
* .setSeverity(Severity.forNumber(0))
647655
* .setFoobar(Foobar.newBuilder().build())
648656
* .build();
@@ -673,6 +681,7 @@ public class EchoClient implements BackgroundResource {
673681
* EchoRequest.newBuilder()
674682
* .setName(FoobarName.ofProjectFoobarName("[PROJECT]", "[FOOBAR]").toString())
675683
* .setParent(FoobarName.ofProjectFoobarName("[PROJECT]", "[FOOBAR]").toString())
684+
* .setRequestId("requestId693933066")
676685
* .setSeverity(Severity.forNumber(0))
677686
* .setFoobar(Foobar.newBuilder().build())
678687
* .build();
@@ -1081,6 +1090,7 @@ public class EchoClient implements BackgroundResource {
10811090
* EchoRequest.newBuilder()
10821091
* .setName(FoobarName.ofProjectFoobarName("[PROJECT]", "[FOOBAR]").toString())
10831092
* .setParent(FoobarName.ofProjectFoobarName("[PROJECT]", "[FOOBAR]").toString())
1093+
* .setRequestId("requestId693933066")
10841094
* .setSeverity(Severity.forNumber(0))
10851095
* .setFoobar(Foobar.newBuilder().build())
10861096
* .build();
@@ -1110,6 +1120,7 @@ public class EchoClient implements BackgroundResource {
11101120
* EchoRequest.newBuilder()
11111121
* .setName(FoobarName.ofProjectFoobarName("[PROJECT]", "[FOOBAR]").toString())
11121122
* .setParent(FoobarName.ofProjectFoobarName("[PROJECT]", "[FOOBAR]").toString())
1123+
* .setRequestId("requestId693933066")
11131124
* .setSeverity(Severity.forNumber(0))
11141125
* .setFoobar(Foobar.newBuilder().build())
11151126
* .build();

0 commit comments

Comments
 (0)