Skip to content

Commit ec2b56c

Browse files
authored
fix: support non-name fields with res-refs in resname def parsing (#418)
1 parent 3ca2534 commit ec2b56c

File tree

4 files changed

+87
-13
lines changed

4 files changed

+87
-13
lines changed

src/main/java/com/google/api/generator/gapic/protoparser/ResourceNameParser.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@
2222
import com.google.common.annotations.VisibleForTesting;
2323
import com.google.common.base.Preconditions;
2424
import com.google.common.base.Strings;
25+
import com.google.protobuf.DescriptorProtos.FieldOptions;
2526
import com.google.protobuf.DescriptorProtos.FileOptions;
2627
import com.google.protobuf.DescriptorProtos.MessageOptions;
2728
import com.google.protobuf.Descriptors.Descriptor;
29+
import com.google.protobuf.Descriptors.FieldDescriptor;
2830
import com.google.protobuf.Descriptors.FileDescriptor;
2931
import java.util.ArrayList;
3032
import java.util.HashMap;
@@ -107,12 +109,25 @@ static Optional<ResourceName> parseResourceNameFromMessageType(
107109
}
108110

109111
ResourceDescriptor protoResource = messageOptions.getExtension(ResourceProto.resource);
110-
// aip.dev/4231.
112+
// Validation - check that a resource name field is present.
111113
if (Strings.isNullOrEmpty(protoResource.getNameField())) {
112-
Preconditions.checkNotNull(
113-
messageTypeDescriptor.findFieldByName(ResourceNameConstants.NAME_FIELD_NAME),
114+
// aip.dev/4231
115+
boolean resourceNameFieldFound =
116+
messageTypeDescriptor.findFieldByName(ResourceNameConstants.NAME_FIELD_NAME) != null;
117+
// If this is null, look for a field with a resource reference is found.
118+
// Example: AccountBudgetProposal.
119+
for (FieldDescriptor fieldDescriptor : messageTypeDescriptor.getFields()) {
120+
FieldOptions fieldOptions = fieldDescriptor.getOptions();
121+
if (fieldOptions.hasExtension(ResourceProto.resourceReference)) {
122+
resourceNameFieldFound = true;
123+
break;
124+
}
125+
}
126+
Preconditions.checkState(
127+
resourceNameFieldFound,
114128
String.format(
115-
"Message %s has a resource annotation but no \"name\" field",
129+
"Message %s has a resource annotation but no field titled \"name\" or containing a"
130+
+ " resource reference",
116131
messageTypeDescriptor.getName()));
117132
}
118133

src/test/java/com/google/api/generator/gapic/protoparser/ResourceNameParserTest.java

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ public void parseResourceNames_messageResourceDefinition() {
125125
List<Descriptor> messageDescriptors = lockerServiceFileDescriptor.getMessageTypes();
126126
Map<String, ResourceName> typeStringsToResourceNames =
127127
ResourceNameParser.parseResourceNamesFromMessages(messageDescriptors, pakkage);
128-
assertEquals(1, typeStringsToResourceNames.size());
128+
assertEquals(2, typeStringsToResourceNames.size());
129129

130130
ResourceName resourceName = typeStringsToResourceNames.get("testgapic.googleapis.com/Document");
131131
assertEquals(2, resourceName.patterns().size());
@@ -140,7 +140,31 @@ public void parseResourceNames_messageResourceDefinition() {
140140
}
141141

142142
@Test
143-
public void parseResourceNames_messageWithoutResourceDefinition() {
143+
public void parseResourceNames_badMessageResourceNameDefinitionMissingNameField() {
144+
FileDescriptor protoFileDescriptor = BadMessageResnameDefProto.getDescriptor();
145+
List<Descriptor> messageDescriptors = protoFileDescriptor.getMessageTypes();
146+
Descriptor messageDescriptor = messageDescriptors.get(0);
147+
String pakkage = TypeParser.getPackage(protoFileDescriptor);
148+
149+
assertThrows(
150+
IllegalStateException.class,
151+
() -> ResourceNameParser.parseResourceNameFromMessageType(messageDescriptor, pakkage));
152+
}
153+
154+
@Test
155+
public void parseResourceNameFromMessage_basicResourceDefinition() {
156+
String pakkage = TypeParser.getPackage(lockerServiceFileDescriptor);
157+
List<Descriptor> messageDescriptors = lockerServiceFileDescriptor.getMessageTypes();
158+
Descriptor documentMessageDescriptor = messageDescriptors.get(0);
159+
assertEquals("Document", documentMessageDescriptor.getName());
160+
Optional<ResourceName> resourceNameOpt =
161+
ResourceNameParser.parseResourceNameFromMessageType(documentMessageDescriptor, pakkage);
162+
assertTrue(resourceNameOpt.isPresent());
163+
assertEquals("testgapic.googleapis.com/Document", resourceNameOpt.get().resourceTypeString());
164+
}
165+
166+
@Test
167+
public void parseResourceNamesFromMessage_noResourceDefinition() {
144168
String pakkage = TypeParser.getPackage(lockerServiceFileDescriptor);
145169
List<Descriptor> messageDescriptors = lockerServiceFileDescriptor.getMessageTypes();
146170
Descriptor folderMessageDescriptor = messageDescriptors.get(1);
@@ -151,15 +175,28 @@ public void parseResourceNames_messageWithoutResourceDefinition() {
151175
}
152176

153177
@Test
154-
public void parseResourceNames_badMessageResourceNameDefinitionMissingNameField() {
178+
public void parseResourceNameFromMessage_nonNameResourceReferenceField() {
179+
String pakkage = TypeParser.getPackage(lockerServiceFileDescriptor);
180+
List<Descriptor> messageDescriptors = lockerServiceFileDescriptor.getMessageTypes();
181+
Descriptor binderMessageDescriptor = messageDescriptors.get(2);
182+
assertEquals("Binder", binderMessageDescriptor.getName());
183+
Optional<ResourceName> resourceNameOpt =
184+
ResourceNameParser.parseResourceNameFromMessageType(binderMessageDescriptor, pakkage);
185+
assertTrue(resourceNameOpt.isPresent());
186+
assertEquals("testgapic.googleapis.com/Binder", resourceNameOpt.get().resourceTypeString());
187+
}
188+
189+
@Test
190+
public void parseResourceNamesFromMessage_noNameOrResourceReferenceField() {
155191
FileDescriptor protoFileDescriptor = BadMessageResnameDefProto.getDescriptor();
156-
List<Descriptor> messageDescriptors = protoFileDescriptor.getMessageTypes();
157-
Descriptor messageDescriptor = messageDescriptors.get(0);
158192
String pakkage = TypeParser.getPackage(protoFileDescriptor);
159-
193+
List<Descriptor> messageDescriptors = protoFileDescriptor.getMessageTypes();
194+
Descriptor pencilMessageDescriptor = messageDescriptors.get(1);
195+
assertEquals("Pencil", pencilMessageDescriptor.getName());
160196
assertThrows(
161-
NullPointerException.class,
162-
() -> ResourceNameParser.parseResourceNameFromMessageType(messageDescriptor, pakkage));
197+
IllegalStateException.class,
198+
() ->
199+
ResourceNameParser.parseResourceNameFromMessageType(pencilMessageDescriptor, pakkage));
163200
}
164201

165202
@Test

src/test/java/com/google/api/generator/gapic/testdata/bad_message_resname_def.proto

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,14 @@ message BarFoo {
3232

3333
string display_name = 1;
3434
}
35+
36+
// A proto with a resource definition, but missing a field titled "name" or
37+
// containing a resource reference.
38+
message Pencil {
39+
option (google.api.resource) = {
40+
type: "testgapic.googleapis.com/Pencil"
41+
pattern: "pencils/{pencil}"
42+
};
43+
44+
string owner = 1;
45+
}

src/test/java/com/google/api/generator/gapic/testdata/locker.proto

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ message Document {
7979
pattern: "*"
8080
};
8181

82-
// The resource name of the user.
82+
// The resource name of the document.
8383
string name = 1;
8484
}
8585

@@ -88,6 +88,17 @@ message Folder {
8888
"cloudresourcemanager.googleapis.com/Folder"];
8989
}
9090

91+
message Binder {
92+
option (google.api.resource) = {
93+
type: "testgapic.googleapis.com/Binder"
94+
pattern: "binders/{binder}"
95+
};
96+
97+
// The resource name of the binder.
98+
string binder_name = 1 [(google.api.resource_reference).type =
99+
"testgapic.googleapis.com/Binder"];
100+
}
101+
91102
message GetFolderRequest {
92103
string name = 1 [
93104
(google.api.resource_reference).type =

0 commit comments

Comments
 (0)