-
Notifications
You must be signed in to change notification settings - Fork 1.1k
feat(gapic-generator-java): Extract resource name heuristicly #12207
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
Changes from 18 commits
3b82880
da23a42
366075b
2e56cee
4efed5a
409522d
877fec6
fbd911f
1b4b5e9
c18077a
523573e
3edd75f
a819980
ad9bc73
3bf58f0
e5142f1
67168ad
f63e3eb
d49e0f1
57dedd3
5dc67eb
9279f60
72bd55a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -296,6 +296,124 @@ public Set<String> vars() { | |
| return bindings.keySet(); | ||
| } | ||
|
|
||
| /** Returns the set of resource literals. A resource literal is a literal followed by a binding */ | ||
| // For example, projects/{project} is a literal/binding pair and projects is a resource literal. | ||
| public Set<String> getResourceLiterals() { | ||
| Set<String> canonicalSegments = new java.util.LinkedHashSet<>(); | ||
| boolean inBinding = false; | ||
| for (int i = 0; i < segments.size(); i++) { | ||
| Segment seg = segments.get(i); | ||
| if (seg.kind() == SegmentKind.BINDING) { | ||
| inBinding = true; | ||
|
lqiu96 marked this conversation as resolved.
|
||
| } else if (seg.kind() == SegmentKind.END_BINDING) { | ||
| inBinding = false; | ||
| } else if (seg.kind() == SegmentKind.LITERAL) { | ||
| String value = seg.value(); | ||
| if (value.matches("^v\\d+[a-zA-Z0-9]*$")) { // just in case | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From the AIP, I believe the regex should be: For collection ID:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is to skip version literals such as
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh I misread this. In that case, can you add a small comment that this is to skip the version in the http path?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added. |
||
| continue; | ||
| } | ||
| if (inBinding) { | ||
| // This is for extracting "projects" and "locations" from named binding | ||
| // {name=projects/*/locations/*} | ||
| canonicalSegments.add(value); | ||
| } else if (i + 1 < segments.size() && segments.get(i + 1).kind() == SegmentKind.BINDING) { | ||
| // This is for regular cases projects/{project}/locations/{location} | ||
| canonicalSegments.add(value); | ||
| } | ||
| } | ||
|
diegomarquezp marked this conversation as resolved.
|
||
| } | ||
| return canonicalSegments; | ||
| } | ||
|
|
||
| /** | ||
| * Returns the canonical resource name string. A canonical resource name is extracted from the | ||
| * template by finding the version literal, then finding the last binding that is a | ||
| * literal/binding pair or named binding, and then extracting the segments between the version | ||
| * literal and the last binding. | ||
| */ | ||
| // For example, projects/{project} is a literal/binding pair. {bar=projects/*/locations/*/bars/*} | ||
|
diegomarquezp marked this conversation as resolved.
|
||
| // is a named binding. | ||
| // If a template is /compute/v1/projects/{project}/locations/{location}, known resources are | ||
| // "projects" and "locations", the canonical resource name is | ||
| // projects/{project}/locations/{location}. See unit tests for all cases. | ||
|
lqiu96 marked this conversation as resolved.
|
||
| public String getCanonicalResourceName(Set<String> knownResources) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the format of the knownResources? Does it come in as the resource pattern (e.g. something like I believe this would be different from the url format which would be Just want to double check this: when the logic compares against the previous segment, it takes care of checking the possible wildcard values with the resource pattern that has no wildcards?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It actually should be renamed to |
||
| if (knownResources == null) { | ||
| return ""; | ||
| } | ||
|
|
||
| int startIndex = 0; | ||
| for (int i = 0; i < segments.size(); i++) { | ||
| Segment seg = segments.get(i); | ||
| if (seg.kind() == SegmentKind.LITERAL) { | ||
|
lqiu96 marked this conversation as resolved.
|
||
| String value = seg.value(); | ||
| if (value.matches("^v\\d+[a-zA-Z0-9]*$")) { | ||
| startIndex = i + 1; | ||
|
diegomarquezp marked this conversation as resolved.
|
||
| break; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| int lastValidEndBindingIndex = -1; | ||
| // Iterate from the end of the segments to find the last valid resource binding. | ||
| // Searching backwards allows us to stop immediately once the last valid pair is found. | ||
| for (int i = segments.size() - 1; i >= 0; i--) { | ||
| Segment seg = segments.get(i); | ||
|
|
||
| // We are looking for the end of a binding (e.g., "}" in "{project}" or "{name=projects/*}") | ||
| if (seg.kind() == SegmentKind.END_BINDING) { | ||
| int bindingStartIndex = -1; | ||
| int literalCountInBinding = 0; | ||
| boolean isValidPair = false; | ||
|
|
||
| // Traverse backwards to find the start of this specific binding | ||
| // and count the literals captured inside it. | ||
| for (int j = i - 1; j >= 0; j--) { | ||
| Segment innerSeg = segments.get(j); | ||
| if (innerSeg.kind() == SegmentKind.BINDING) { | ||
| bindingStartIndex = j; | ||
| break; | ||
| } else if (innerSeg.kind() == SegmentKind.LITERAL) { | ||
| literalCountInBinding++; | ||
| } | ||
| } | ||
|
|
||
| if (bindingStartIndex != -1) { | ||
| // 1. If the binding contains any literals, it is considered a valid named resource | ||
| // binding. | ||
| if (literalCountInBinding > 0) { | ||
| isValidPair = true; | ||
| } else if (bindingStartIndex > 0) { | ||
| // 2. For simple bindings like "{project}", the binding itself has no inner literal | ||
| // resources. | ||
| // Instead, we check if the literal segment immediately preceding it (e.g., "projects/") | ||
| // is a known resource. | ||
| Segment prevSeg = segments.get(bindingStartIndex - 1); | ||
| if (prevSeg.kind() == SegmentKind.LITERAL && knownResources.contains(prevSeg.value())) { | ||
| isValidPair = true; | ||
| } | ||
| } | ||
|
|
||
| if (isValidPair) { | ||
| // We successfully found the last valid binding! Record its end index and terminate the | ||
| // search. | ||
| lastValidEndBindingIndex = i; | ||
| break; | ||
| } | ||
| // The current binding wasn't a valid resource pair. | ||
| // Skip over all inner segments of this invalid binding so we don't evaluate them again. | ||
| i = bindingStartIndex; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (lastValidEndBindingIndex == -1 || lastValidEndBindingIndex < startIndex) { | ||
| return ""; | ||
| } | ||
|
|
||
| List<Segment> canonicalSegments = segments.subList(startIndex, lastValidEndBindingIndex + 1); | ||
| return toSyntax(canonicalSegments, true).replace("=*}", "}"); | ||
|
lqiu96 marked this conversation as resolved.
|
||
| } | ||
|
|
||
| /** | ||
| * Returns a template for the parent of this template. | ||
| * | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.