Skip to content

Commit d3fea22

Browse files
authored
[ggj][codegen] fix: support singleton resnames' patterns in parsing and codegen (#574)
* fix: handle empty method_signature in parsing protos * fix: add google/type:type_java_proto as default test dep * fix: support singleton resource names in parsing and codegen
1 parent 3b7bb40 commit d3fea22

File tree

4 files changed

+287
-11
lines changed

4 files changed

+287
-11
lines changed

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

+10-7
Original file line numberDiff line numberDiff line change
@@ -187,17 +187,20 @@ static Optional<String> parseParentPattern(String pattern) {
187187
return Optional.empty();
188188
}
189189

190+
int lastTokenIndex = tokens.length - 2;
191+
int minLengthWithParent = 4;
192+
// Singleton patterns, e.g. projects/{project}/agent.
193+
if (!lastToken.contains("{")) {
194+
minLengthWithParent = 3;
195+
lastTokenIndex = tokens.length - 1;
196+
}
197+
190198
// No fully-formed parent. Expected: ancestors/{ancestor}/childNodes/{child_node}.
191-
if (tokens.length < 4) {
199+
if (tokens.length < minLengthWithParent) {
192200
return Optional.empty();
193201
}
194202

195-
Preconditions.checkState(
196-
lastToken.contains("{"),
197-
String.format(
198-
"Pattern %s must end with a brace-encapsulated variable, e.g. {foobar}", pattern));
199-
200-
return Optional.of(String.join(SLASH, Arrays.asList(tokens).subList(0, tokens.length - 2)));
203+
return Optional.of(String.join(SLASH, Arrays.asList(tokens).subList(0, lastTokenIndex)));
201204
}
202205

203206
@VisibleForTesting

src/test/java/com/google/api/generator/gapic/composer/ResourceNameHelperClassComposerTest.java

+22
Original file line numberDiff line numberDiff line change
@@ -216,4 +216,26 @@ public void generateResourceNameClass_testingBlueprintPatternWithNonSlashSeparat
216216
Path goldenFilePath = Paths.get(ComposerConstants.GOLDENFILES_DIRECTORY, "TestName.golden");
217217
Assert.assertCodeEquals(goldenFilePath, visitor.write());
218218
}
219+
220+
@Test
221+
public void generateResourceNameClass_childSingleton() {
222+
ResourceName agentResname =
223+
ResourceName.builder()
224+
.setVariableName("agent")
225+
.setPakkage("com.google.cloud.dialogflow.v2beta1")
226+
.setResourceTypeString("dialogflow.googleapis.com/Agent")
227+
.setPatterns(
228+
Arrays.asList(
229+
"projects/{project}/locations/{location}/agent", "projects/{project}/agent"))
230+
.setParentMessageName("Agent")
231+
.setDescription("This is a description")
232+
.build();
233+
234+
GapicClass clazz = ResourceNameHelperClassComposer.instance().generate(agentResname);
235+
JavaWriterVisitor visitor = new JavaWriterVisitor();
236+
clazz.classDefinition().accept(visitor);
237+
Utils.saveCodegenToFile(this.getClass(), "AgentName.golden", visitor.write());
238+
Path goldenFilePath = Paths.get(ComposerConstants.GOLDENFILES_DIRECTORY, "AgentName.golden");
239+
Assert.assertCodeEquals(goldenFilePath, visitor.write());
240+
}
219241
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
package com.google.cloud.dialogflow.v2beta1;
2+
3+
import com.google.api.core.BetaApi;
4+
import com.google.api.pathtemplate.PathTemplate;
5+
import com.google.api.pathtemplate.ValidationException;
6+
import com.google.api.resourcenames.ResourceName;
7+
import com.google.common.base.Preconditions;
8+
import com.google.common.collect.ImmutableMap;
9+
import java.util.ArrayList;
10+
import java.util.List;
11+
import java.util.Map;
12+
import java.util.Objects;
13+
import javax.annotation.Generated;
14+
15+
// AUTO-GENERATED DOCUMENTATION AND CLASS.
16+
@Generated("by gapic-generator-java")
17+
public class AgentName implements ResourceName {
18+
private static final PathTemplate PROJECT_LOCATION =
19+
PathTemplate.createWithoutUrlEncoding("projects/{project}/locations/{location}/agent");
20+
private static final PathTemplate PROJECT =
21+
PathTemplate.createWithoutUrlEncoding("projects/{project}/agent");
22+
private volatile Map<String, String> fieldValuesMap;
23+
private PathTemplate pathTemplate;
24+
private String fixedValue;
25+
private final String project;
26+
private final String location;
27+
28+
@Deprecated
29+
protected AgentName() {
30+
project = null;
31+
location = null;
32+
}
33+
34+
private AgentName(Builder builder) {
35+
project = Preconditions.checkNotNull(builder.getProject());
36+
location = Preconditions.checkNotNull(builder.getLocation());
37+
pathTemplate = PROJECT_LOCATION;
38+
}
39+
40+
private AgentName(ProjectBuilder builder) {
41+
project = Preconditions.checkNotNull(builder.getProject());
42+
location = null;
43+
pathTemplate = PROJECT;
44+
}
45+
46+
public String getProject() {
47+
return project;
48+
}
49+
50+
public String getLocation() {
51+
return location;
52+
}
53+
54+
public static Builder newBuilder() {
55+
return new Builder();
56+
}
57+
58+
@BetaApi("The per-pattern Builders are not stable yet and may be changed in the future.")
59+
public static Builder newProjectLocationBuilder() {
60+
return new Builder();
61+
}
62+
63+
@BetaApi("The per-pattern Builders are not stable yet and may be changed in the future.")
64+
public static ProjectBuilder newProjectBuilder() {
65+
return new ProjectBuilder();
66+
}
67+
68+
public Builder toBuilder() {
69+
return new Builder(this);
70+
}
71+
72+
public static AgentName of(String project, String location) {
73+
return newBuilder().setProject(project).setLocation(location).build();
74+
}
75+
76+
@BetaApi("The static create methods are not stable yet and may be changed in the future.")
77+
public static AgentName ofProjectLocationName(String project, String location) {
78+
return newBuilder().setProject(project).setLocation(location).build();
79+
}
80+
81+
@BetaApi("The static create methods are not stable yet and may be changed in the future.")
82+
public static AgentName ofProjectName(String project) {
83+
return newProjectBuilder().setProject(project).build();
84+
}
85+
86+
public static String format(String project, String location) {
87+
return newBuilder().setProject(project).setLocation(location).build().toString();
88+
}
89+
90+
@BetaApi("The static format methods are not stable yet and may be changed in the future.")
91+
public static String formatProjectLocationName(String project, String location) {
92+
return newBuilder().setProject(project).setLocation(location).build().toString();
93+
}
94+
95+
@BetaApi("The static format methods are not stable yet and may be changed in the future.")
96+
public static String formatProjectName(String project) {
97+
return newProjectBuilder().setProject(project).build().toString();
98+
}
99+
100+
public static AgentName parse(String formattedString) {
101+
if (formattedString.isEmpty()) {
102+
return null;
103+
}
104+
if (PROJECT_LOCATION.matches(formattedString)) {
105+
Map<String, String> matchMap = PROJECT_LOCATION.match(formattedString);
106+
return ofProjectLocationName(matchMap.get("project"), matchMap.get("location"));
107+
} else if (PROJECT.matches(formattedString)) {
108+
Map<String, String> matchMap = PROJECT.match(formattedString);
109+
return ofProjectName(matchMap.get("project"));
110+
}
111+
throw new ValidationException("AgentName.parse: formattedString not in valid format");
112+
}
113+
114+
public static List<AgentName> parseList(List<String> formattedStrings) {
115+
List<AgentName> list = new ArrayList<>(formattedStrings.size());
116+
for (String formattedString : formattedStrings) {
117+
list.add(parse(formattedString));
118+
}
119+
return list;
120+
}
121+
122+
public static List<String> toStringList(List<AgentName> values) {
123+
List<String> list = new ArrayList<>(values.size());
124+
for (AgentName value : values) {
125+
if (Objects.isNull(value)) {
126+
list.add("");
127+
} else {
128+
list.add(value.toString());
129+
}
130+
}
131+
return list;
132+
}
133+
134+
public static boolean isParsableFrom(String formattedString) {
135+
return PROJECT_LOCATION.matches(formattedString) || PROJECT.matches(formattedString);
136+
}
137+
138+
@Override
139+
public Map<String, String> getFieldValuesMap() {
140+
if (Objects.isNull(fieldValuesMap)) {
141+
synchronized (this) {
142+
if (Objects.isNull(fieldValuesMap)) {
143+
ImmutableMap.Builder<String, String> fieldMapBuilder = ImmutableMap.builder();
144+
if (!Objects.isNull(project)) {
145+
fieldMapBuilder.put("project", project);
146+
}
147+
if (!Objects.isNull(location)) {
148+
fieldMapBuilder.put("location", location);
149+
}
150+
fieldValuesMap = fieldMapBuilder.build();
151+
}
152+
}
153+
}
154+
return fieldValuesMap;
155+
}
156+
157+
public String getFieldValue(String fieldName) {
158+
return getFieldValuesMap().get(fieldName);
159+
}
160+
161+
@Override
162+
public String toString() {
163+
return !Objects.isNull(fixedValue) ? fixedValue : pathTemplate.instantiate(getFieldValuesMap());
164+
}
165+
166+
@Override
167+
public boolean equals(Object o) {
168+
if (o == this) {
169+
return true;
170+
}
171+
if (o != null || getClass() == o.getClass()) {
172+
AgentName that = ((AgentName) o);
173+
return Objects.equals(this.project, that.project)
174+
&& Objects.equals(this.location, that.location);
175+
}
176+
return false;
177+
}
178+
179+
@Override
180+
public int hashCode() {
181+
int h = 1;
182+
h *= 1000003;
183+
h ^= Objects.hashCode(fixedValue);
184+
h *= 1000003;
185+
h ^= Objects.hashCode(project);
186+
h *= 1000003;
187+
h ^= Objects.hashCode(location);
188+
return h;
189+
}
190+
191+
/** Builder for projects/{project}/locations/{location}/agent. */
192+
public static class Builder {
193+
private String project;
194+
private String location;
195+
196+
protected Builder() {}
197+
198+
public String getProject() {
199+
return project;
200+
}
201+
202+
public String getLocation() {
203+
return location;
204+
}
205+
206+
public Builder setProject(String project) {
207+
this.project = project;
208+
return this;
209+
}
210+
211+
public Builder setLocation(String location) {
212+
this.location = location;
213+
return this;
214+
}
215+
216+
private Builder(AgentName agentName) {
217+
Preconditions.checkArgument(
218+
Objects.equals(agentName.pathTemplate, PROJECT_LOCATION),
219+
"toBuilder is only supported when AgentName has the pattern of projects/{project}/locations/{location}/agent");
220+
project = agentName.project;
221+
location = agentName.location;
222+
}
223+
224+
public AgentName build() {
225+
return new AgentName(this);
226+
}
227+
}
228+
229+
/** Builder for projects/{project}/agent. */
230+
@BetaApi("The per-pattern Builders are not stable yet and may be changed in the future.")
231+
public static class ProjectBuilder {
232+
private String project;
233+
234+
protected ProjectBuilder() {}
235+
236+
public String getProject() {
237+
return project;
238+
}
239+
240+
public ProjectBuilder setProject(String project) {
241+
this.project = project;
242+
return this;
243+
}
244+
245+
public AgentName build() {
246+
return new AgentName(this);
247+
}
248+
}
249+
}

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

+6-4
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,12 @@ public void parseParentResourceName_badPattern() {
113113
ResourceReferenceParser.parseParentResourceName(
114114
"projects/{project}/billingAccounts",
115115
MAIN_PACKAGE,
116-
null,
117-
MAIN_PACKAGE,
116+
"com.google.cloud.billing.v1",
118117
"cloudbilling.googleapis.com/Feature",
118+
null,
119119
new HashMap<String, ResourceName>());
120-
assertFalse(parentResourceNameOpt.isPresent());
120+
assertTrue(parentResourceNameOpt.isPresent());
121+
assertEquals("projects/{project}", parentResourceNameOpt.get().patterns().get(0));
121122
}
122123

123124
@Test
@@ -159,7 +160,8 @@ public void parseParentPattern_insufficientPathComponents() {
159160
public void parseParentPattern_lastComponentIsNotAVariable() {
160161
Optional<String> parentPatternOpt =
161162
ResourceReferenceParser.parseParentPattern("projects/{project}/foobars");
162-
assertFalse(parentPatternOpt.isPresent());
163+
assertTrue(parentPatternOpt.isPresent());
164+
assertEquals("projects/{project}", parentPatternOpt.get());
163165
}
164166

165167
@Test

0 commit comments

Comments
 (0)