Skip to content

Commit 877fec6

Browse files
committed
fix: Update the heuristic logic per latest discussion.
1 parent 409522d commit 877fec6

File tree

2 files changed

+78
-117
lines changed

2 files changed

+78
-117
lines changed

sdk-platform-java/api-common-java/src/main/java/com/google/api/pathtemplate/PathTemplate.java

Lines changed: 60 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -332,124 +332,87 @@ public String getCanonicalResourceName(Set<String> knownResources) {
332332
if (knownResources == null) {
333333
return "";
334334
}
335-
StringBuilder canonical = new StringBuilder();
336-
StringBuilder currentSequence = new StringBuilder();
335+
336+
int firstBindingIndex = -1;
337+
for (int i = 0; i < segments.size(); i++) {
338+
if (segments.get(i).kind() == SegmentKind.BINDING) {
339+
firstBindingIndex = i;
340+
break;
341+
}
342+
}
343+
344+
if (firstBindingIndex == -1) {
345+
return "";
346+
}
347+
348+
int startIndex = 0;
349+
for (int i = firstBindingIndex - 1; i >= 0; i--) {
350+
Segment seg = segments.get(i);
351+
if (seg.kind() == SegmentKind.LITERAL) {
352+
String value = seg.value();
353+
if (value.matches("^v\\d+.*") || value.matches("^u\\d+.*")) {
354+
startIndex = i + 1;
355+
break;
356+
}
357+
}
358+
}
359+
360+
int lastValidEndBindingIndex = -1;
337361
boolean inBinding = false;
338-
String currentBindingName = "";
339-
boolean keepBinding = true;
340-
List<Segment> bindingSegments = new ArrayList<>();
341-
boolean afterKeptNamedBinding = false;
362+
int literalCountInBinding = 0;
363+
int currentBindingStartIndex = -1;
342364

343365
for (int i = 0; i < segments.size(); i++) {
344366
Segment seg = segments.get(i);
345367
if (seg.kind() == SegmentKind.BINDING) {
346368
inBinding = true;
347-
currentBindingName = seg.value();
348-
bindingSegments.clear();
349-
keepBinding = true;
369+
literalCountInBinding = 0;
370+
currentBindingStartIndex = i;
350371
} else if (seg.kind() == SegmentKind.END_BINDING) {
351372
inBinding = false;
352-
StringBuilder innerPattern = new StringBuilder();
353-
int literalCount = 0;
354-
for (Segment innerSeg : bindingSegments) {
355-
if (innerSeg.kind() == SegmentKind.LITERAL) {
356-
String value = innerSeg.value();
357-
if (value.matches("^v\\d+.*") || value.matches("^u\\d+.*")) {
358-
continue;
359-
}
360-
literalCount++;
361-
if (innerPattern.length() > 0) {
362-
innerPattern.append("/");
363-
}
364-
innerPattern.append(value);
365-
} else if (innerSeg.kind() == SegmentKind.WILDCARD) {
366-
if (innerPattern.length() > 0) {
367-
innerPattern.append("/");
368-
}
369-
innerPattern.append("*");
370-
}
371-
}
373+
boolean isValidPair = false;
372374

373-
boolean extractInner = false;
374-
if (canonical.length() == 0 && currentSequence.length() == 0) {
375-
if (i + 1 < segments.size()) {
376-
Segment nextSeg = segments.get(i + 1);
377-
if (nextSeg.kind() == SegmentKind.LITERAL) {
378-
String nextValue = nextSeg.value();
379-
if (knownResources.contains(nextValue)) {
380-
extractInner = true;
375+
if (literalCountInBinding > 1) {
376+
// Named bindings are unconditionally considered pairs
377+
isValidPair = true;
378+
} else {
379+
// Check inner literals
380+
for (int j = currentBindingStartIndex + 1; j < i; j++) {
381+
if (segments.get(j).kind() == SegmentKind.LITERAL) {
382+
if (knownResources.contains(segments.get(j).value())) {
383+
isValidPair = true;
384+
break;
381385
}
382386
}
383387
}
384-
}
385-
386-
if (extractInner) {
387-
if (innerPattern.length() > 0) {
388-
if (canonical.length() > 0) {
389-
canonical.append("/");
388+
// If not valid yet, check preceding literal
389+
if (!isValidPair && currentBindingStartIndex > 0) {
390+
Segment prevSeg = segments.get(currentBindingStartIndex - 1);
391+
if (prevSeg.kind() == SegmentKind.LITERAL && knownResources.contains(prevSeg.value())) {
392+
isValidPair = true;
390393
}
391-
canonical.append(innerPattern);
392-
}
393-
} else {
394-
if (currentSequence.length() > 0) {
395-
if (canonical.length() > 0) {
396-
canonical.append("/");
397-
}
398-
canonical.append(currentSequence);
399-
currentSequence.setLength(0);
400-
}
401-
if (canonical.length() > 0) {
402-
canonical.append("/");
403394
}
395+
}
404396

405-
if (literalCount <= 1 || innerPattern.toString().equals("*")) {
406-
canonical.append("{").append(currentBindingName).append("}");
407-
} else {
408-
canonical
409-
.append("{")
410-
.append(currentBindingName)
411-
.append("=")
412-
.append(innerPattern)
413-
.append("}");
414-
afterKeptNamedBinding = true;
415-
}
397+
if (isValidPair) {
398+
lastValidEndBindingIndex = i;
416399
}
417400
} else if (seg.kind() == SegmentKind.LITERAL) {
418-
String value = seg.value();
419-
if (value.matches("^v\\d+.*") || value.matches("^u\\d+.*")) {
420-
continue;
421-
}
422401
if (inBinding) {
423-
bindingSegments.add(seg);
424-
if (!knownResources.contains(value)) {
425-
keepBinding = false;
426-
}
427-
} else {
428-
if (knownResources.contains(value)) {
429-
if (currentSequence.length() > 0) {
430-
currentSequence.append("/");
431-
}
432-
currentSequence.append(value);
433-
} else {
434-
if (afterKeptNamedBinding) {
435-
if (currentSequence.length() > 0) {
436-
currentSequence.append("/");
437-
}
438-
currentSequence.append(value);
439-
} else {
440-
if (canonical.length() > 0 || currentSequence.length() > 0) {
441-
break;
442-
}
443-
}
402+
String value = seg.value();
403+
if (!value.matches("^v\\d+.*") && !value.matches("^u\\d+.*")) {
404+
literalCountInBinding++;
444405
}
445406
}
446-
} else if (seg.kind() == SegmentKind.WILDCARD) {
447-
if (inBinding) {
448-
bindingSegments.add(seg);
449-
}
450407
}
451408
}
452-
return canonical.toString();
409+
410+
if (lastValidEndBindingIndex == -1 || lastValidEndBindingIndex < startIndex) {
411+
return "";
412+
}
413+
414+
List<Segment> canonicalSegments = segments.subList(startIndex, lastValidEndBindingIndex + 1);
415+
return toSyntax(canonicalSegments, true);
453416
}
454417

455418
/**
@@ -1278,7 +1241,7 @@ private String decodeUrl(String url) {
12781241
// the list iterator in its state.
12791242
private static boolean peek(ListIterator<Segment> segments, SegmentKind... kinds) {
12801243
int start = segments.nextIndex();
1281-
boolean success = false;
1244+
boolean success = true;
12821245
for (SegmentKind kind : kinds) {
12831246
if (!segments.hasNext() || segments.next().kind() != kind) {
12841247
success = false;

sdk-platform-java/api-common-java/src/test/java/com/google/api/pathtemplate/PathTemplateTest.java

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -966,7 +966,7 @@ void testGetCanonicalResourceName_regexVariables() {
966966
PathTemplate template =
967967
PathTemplate.create("v1/projects/{project=projects/*}/instances/{instance_id=instances/*}");
968968
Truth.assertThat(template.getCanonicalResourceName(knownResources))
969-
.isEqualTo("projects/{project}/instances/{instance_id}");
969+
.isEqualTo("projects/{project=projects/*}/instances/{instance_id=instances/*}");
970970
}
971971

972972
@Test
@@ -1004,40 +1004,38 @@ void testGetCanonicalResourceName_customVerb() {
10041004
}
10051005

10061006
@Test
1007-
void testGetCanonicalResourceName_nameBinding() {
1007+
void testGetCanonicalResourceName_nameBindingMixedWithSimpleBinding() {
10081008
Set<String> knownResources = ImmutableSet.of("projects", "locations", "instances", "widgets");
1009-
PathTemplate template = PathTemplate.create("v1/{field=projects/*/instances/*}");
1009+
PathTemplate template =
1010+
PathTemplate.create("v1/{field=projects/*/instances/*}/actions/{action}");
10101011
Truth.assertThat(template.getCanonicalResourceName(knownResources))
10111012
.isEqualTo("{field=projects/*/instances/*}");
10121013
}
10131014

10141015
@Test
1015-
void testGetCanonicalResourceName_nameBindingMixedWithSimpleBinding() {
1016-
Set<String> knownResources = ImmutableSet.of("projects", "locations", "instances", "widgets");
1017-
PathTemplate template =
1018-
PathTemplate.create("v1/{field=projects/*/instances/*}/actions/{action}");
1016+
void testGetCanonicalResourceName_multipleLiteralsWithSimpleBinding() {
1017+
Set<String> knownResources = ImmutableSet.of("actions");
1018+
PathTemplate template = PathTemplate.create("v1/locations/global/actions/{action}");
10191019
Truth.assertThat(template.getCanonicalResourceName(knownResources))
1020-
.isEqualTo("{field=projects/*/instances/*}/actions/{action}");
1020+
.isEqualTo("locations/global/actions/{action}");
10211021
}
10221022

10231023
@Test
1024-
void testGetCanonicalResourceName_nameBindingWithUnknownLiterals() {
1024+
void testGetCanonicalResourceName_multipleLiteralsWithMultipleBindings() {
1025+
Set<String> knownResources = ImmutableSet.of("instances", "actions");
10251026
PathTemplate template =
1026-
PathTemplate.create(
1027-
"/compute/v1/projects/{project}/zones/{zone}/{parent_name=reservations/*/reservationBlocks/*/reservationSubBlocks/*}/reservationSlots/{reservation_slot}");
1028-
String canonical = template.getCanonicalResourceName(template.getResourceLiterals());
1029-
Truth.assertThat(canonical)
1030-
.isEqualTo(
1031-
"projects/{project}/zones/{zone}/{parent_name=reservations/*/reservationBlocks/*/reservationSubBlocks/*}/reservationSlots/{reservation_slot}");
1027+
PathTemplate.create("v1/locations/global/instances/{instance}/actions/{action}");
1028+
Truth.assertThat(template.getCanonicalResourceName(knownResources))
1029+
.isEqualTo("locations/global/instances/{instance}/actions/{action}");
10321030
}
10331031

10341032
@Test
1035-
void testGetCanonicalResourceName_nameBindingMixedWithSimpleBinding_moreKnownResources() {
1036-
Set<String> moreKnownResources = ImmutableSet.of("projects", "instances", "actions");
1033+
void testGetCanonicalResourceName_multipleLiteralsBetweenMultipleBindings() {
1034+
Set<String> knownResources = ImmutableSet.of("instances", "actions");
10371035
PathTemplate template =
1038-
PathTemplate.create("v1/{name=projects/*/instances/*}/actions/{action}");
1039-
Truth.assertThat(template.getCanonicalResourceName(moreKnownResources))
1040-
.isEqualTo("projects/*/instances/*/actions/{action}");
1036+
PathTemplate.create("v1/instances/{instance}/locations/global/actions/{action}");
1037+
Truth.assertThat(template.getCanonicalResourceName(knownResources))
1038+
.isEqualTo("instances/{instance}/locations/global/actions/{action}");
10411039
}
10421040

10431041
@Test

0 commit comments

Comments
 (0)