Skip to content

Commit da23a42

Browse files
committed
Filter heuristic resource name extraction by proto package
1 parent 3b82880 commit da23a42

File tree

8 files changed

+1392
-45
lines changed

8 files changed

+1392
-45
lines changed

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

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,162 @@ public Set<String> vars() {
296296
return bindings.keySet();
297297
}
298298

299+
/**
300+
* Returns the set of resource literals. A resource literal is a literal followed by a binding or
301+
* inside a binding.
302+
*/
303+
public Set<String> getResourceLiterals() {
304+
Set<String> canonicalSegments = new java.util.LinkedHashSet<>();
305+
boolean inBinding = false;
306+
for (int i = 0; i < segments.size(); i++) {
307+
Segment seg = segments.get(i);
308+
if (seg.kind() == SegmentKind.BINDING) {
309+
inBinding = true;
310+
} else if (seg.kind() == SegmentKind.END_BINDING) {
311+
inBinding = false;
312+
} else if (seg.kind() == SegmentKind.LITERAL) {
313+
String value = seg.value();
314+
if (value.matches("^v\\d+.*") || value.matches("^u\\d+.*")) { // just in case
315+
continue;
316+
}
317+
if (inBinding) {
318+
canonicalSegments.add(value);
319+
} else if (i + 1 < segments.size() && segments.get(i + 1).kind() == SegmentKind.BINDING) {
320+
canonicalSegments.add(value);
321+
}
322+
}
323+
}
324+
return canonicalSegments;
325+
}
326+
327+
/**
328+
* Returns the canonical resource name string. A segment is canonical if it is a literal followed
329+
* by a binding or inside a binding. If a literal is not in knownResources, the extraction stops.
330+
*/
331+
public String getCanonicalResourceName(Set<String> knownResources) {
332+
if (knownResources == null) {
333+
return "";
334+
}
335+
StringBuilder canonical = new StringBuilder();
336+
StringBuilder currentSequence = new StringBuilder();
337+
boolean inBinding = false;
338+
String currentBindingName = "";
339+
boolean keepBinding = true;
340+
List<Segment> bindingSegments = new ArrayList<>();
341+
boolean afterKeptNamedBinding = false;
342+
343+
for (int i = 0; i < segments.size(); i++) {
344+
Segment seg = segments.get(i);
345+
if (seg.kind() == SegmentKind.BINDING) {
346+
inBinding = true;
347+
currentBindingName = seg.value();
348+
bindingSegments.clear();
349+
keepBinding = true;
350+
} else if (seg.kind() == SegmentKind.END_BINDING) {
351+
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+
}
372+
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;
381+
}
382+
}
383+
}
384+
}
385+
386+
if (extractInner) {
387+
if (innerPattern.length() > 0) {
388+
if (canonical.length() > 0) {
389+
canonical.append("/");
390+
}
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("/");
403+
}
404+
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+
}
416+
}
417+
} else if (seg.kind() == SegmentKind.LITERAL) {
418+
String value = seg.value();
419+
if (value.matches("^v\\d+.*") || value.matches("^u\\d+.*")) {
420+
continue;
421+
}
422+
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+
}
444+
}
445+
}
446+
} else if (seg.kind() == SegmentKind.WILDCARD) {
447+
if (inBinding) {
448+
bindingSegments.add(seg);
449+
}
450+
}
451+
}
452+
return canonical.toString();
453+
}
454+
299455
/**
300456
* Returns a template for the parent of this template.
301457
*
@@ -997,6 +1153,9 @@ private static ImmutableList<Segment> parseTemplate(String template) {
9971153
}
9981154

9991155
private static boolean isSegmentBeginOrEndInvalid(String seg) {
1156+
if (seg.isEmpty()) {
1157+
return false;
1158+
}
10001159
// A segment is invalid if it contains only one character and the character is a delimiter
10011160
if (seg.length() == 1 && COMPLEX_DELIMITER_PATTERN.matcher(seg).find()) {
10021161
return true;

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

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
package com.google.api.pathtemplate;
3232

3333
import com.google.common.collect.ImmutableMap;
34+
import com.google.common.collect.ImmutableSet;
3435
import com.google.common.truth.Truth;
3536
import java.util.Map;
3637
import java.util.Set;
@@ -894,6 +895,188 @@ void testTemplateWithMultipleSimpleBindings() {
894895
Truth.assertThat(url).isEqualTo("v1/shelves/s1/books/b1");
895896
}
896897

898+
@Test
899+
void name() {
900+
PathTemplate pathTemplate =
901+
PathTemplate.create("projects/{project}/zones/{zone}/{parent_name}");
902+
System.out.println(
903+
pathTemplate.instantiate("project", "project1", "zone", "zone1", "parent_name", "name1"));
904+
}
905+
906+
@Test
907+
void testGetResourceLiterals_simplePath() {
908+
PathTemplate template =
909+
PathTemplate.create("/compute/v1/projects/{project}/locations/{location}/widgets/{widget}");
910+
Truth.assertThat(template.getResourceLiterals())
911+
.containsExactly("projects", "locations", "widgets");
912+
}
913+
914+
@Test
915+
void testGetResourceLiterals_regexPath() {
916+
PathTemplate template =
917+
PathTemplate.create("v1/projects/{project=projects/*}/instances/{instance_id=instances/*}");
918+
Truth.assertThat(template.getResourceLiterals()).containsExactly("projects", "instances");
919+
}
920+
921+
@Test
922+
void testGetResourceSegments_onlyNonResourceLiterals() {
923+
PathTemplate template = PathTemplate.create("compute/v1/projects");
924+
Truth.assertThat(template.getResourceLiterals()).isEmpty();
925+
}
926+
927+
@Test
928+
void testGetResourceLiterals_nameBinding() {
929+
PathTemplate template = PathTemplate.create("v1/{name=projects/*/instances/*}");
930+
Truth.assertThat(template.getResourceLiterals()).containsExactly("projects", "instances");
931+
}
932+
933+
@Test
934+
void testGetResourceSegments_complexResourceId() {
935+
PathTemplate template = PathTemplate.create("projects/{project}/zones/{zone_a}~{zone_b}");
936+
Truth.assertThat(template.getResourceLiterals()).containsExactly("projects", "zones");
937+
}
938+
939+
@Test
940+
void testGetResourceLiterals_customVerb() {
941+
PathTemplate template = PathTemplate.create("projects/{project}/instances/{instance}:execute");
942+
Truth.assertThat(template.getResourceLiterals()).containsExactly("projects", "instances");
943+
}
944+
945+
@Test
946+
void testGetResourceLiterals_multipleVersions() {
947+
PathTemplate template =
948+
PathTemplate.create(
949+
"v1/compute/v2/projects/{project}/locations/{location}/widgets/{widget}");
950+
Truth.assertThat(template.getResourceLiterals())
951+
.containsExactly("projects", "locations", "widgets");
952+
}
953+
954+
@Test
955+
void testGetResourceLiterals_namedBindings() {
956+
PathTemplate template =
957+
PathTemplate.create(
958+
"/compute/v1/projects/{project}/zones/{zone}/{parent_name=reservations/*/reservationBlocks/*/reservationSubBlocks/*}");
959+
Truth.assertThat(template.getResourceLiterals())
960+
.containsExactly(
961+
"projects", "zones", "reservations", "reservationBlocks", "reservationSubBlocks");
962+
}
963+
964+
@Test
965+
void testGetCanonicalResourceName_namedBindings() {
966+
PathTemplate template =
967+
PathTemplate.create(
968+
"/v1/projects/{project}/locations/{location}/{parent_name=reservations/*/reservationBlocks/*/reservationSubBlocks/*}/heuristics/{heuristic}");
969+
970+
Set<String> resourceLiterals = ImmutableSet.of("projects", "locations", "heuristics");
971+
Truth.assertThat(template.getCanonicalResourceName(resourceLiterals))
972+
.isEqualTo(
973+
"projects/{project}/locations/{location}/{parent_name=reservations/*/reservationBlocks/*/reservationSubBlocks/*}/heuristics/{heuristic}");
974+
}
975+
976+
@Test
977+
void testGetCanonicalResourceName_namedBindingsSimple() {
978+
Set<String> moreKnownResources = ImmutableSet.of("projects", "locations", "bars");
979+
PathTemplate template = PathTemplate.create("/v1/{bar=projects/*/locations/*/bars/*}");
980+
Truth.assertThat(template.getCanonicalResourceName(moreKnownResources))
981+
.isEqualTo("{bar=projects/*/locations/*/bars/*}");
982+
}
983+
984+
@Test
985+
void testGetCanonicalResourceName_simplePath() {
986+
Set<String> knownResources = ImmutableSet.of("projects", "locations", "instances", "widgets");
987+
PathTemplate template =
988+
PathTemplate.create("/compute/v1/projects/{project}/locations/{location}/widgets/{widget}");
989+
Truth.assertThat(template.getCanonicalResourceName(knownResources))
990+
.isEqualTo("projects/{project}/locations/{location}/widgets/{widget}");
991+
}
992+
993+
@Test
994+
void testGetCanonicalResourceName_regexVariables() {
995+
Set<String> knownResources = ImmutableSet.of("projects", "locations", "instances", "widgets");
996+
PathTemplate template =
997+
PathTemplate.create("v1/projects/{project=projects/*}/instances/{instance_id=instances/*}");
998+
Truth.assertThat(template.getCanonicalResourceName(knownResources))
999+
.isEqualTo("projects/{project}/instances/{instance_id}");
1000+
}
1001+
1002+
@Test
1003+
void testGetCanonicalResourceName_noVariables() {
1004+
Set<String> knownResources = ImmutableSet.of("projects", "locations", "instances", "widgets");
1005+
PathTemplate template = PathTemplate.create("v1/projects/locations");
1006+
Truth.assertThat(template.getCanonicalResourceName(knownResources)).isEmpty();
1007+
}
1008+
1009+
@Test
1010+
void testGetCanonicalResourceName_unknownResource() {
1011+
Set<String> knownResources = ImmutableSet.of("projects", "locations", "instances", "widgets");
1012+
PathTemplate template =
1013+
PathTemplate.create("v1/projects/{project}/unknownResource/{unknownResource}");
1014+
Truth.assertThat(template.getCanonicalResourceName(knownResources))
1015+
.isEqualTo("projects/{project}");
1016+
}
1017+
1018+
@Test
1019+
void testGetCanonicalResourceName_ignoreVersions() {
1020+
Set<String> knownResources = ImmutableSet.of("projects", "locations", "instances", "widgets");
1021+
PathTemplate template =
1022+
PathTemplate.create(
1023+
"v1/compute/v2/projects/{project}/locations/{location}/widgets/{widget}");
1024+
Truth.assertThat(template.getCanonicalResourceName(knownResources))
1025+
.isEqualTo("projects/{project}/locations/{location}/widgets/{widget}");
1026+
}
1027+
1028+
@Test
1029+
void testGetCanonicalResourceName_customVerb() {
1030+
Set<String> knownResources = ImmutableSet.of("projects", "locations", "instances", "widgets");
1031+
PathTemplate template = PathTemplate.create("projects/{project}/instances/{instance}:execute");
1032+
Truth.assertThat(template.getCanonicalResourceName(knownResources))
1033+
.isEqualTo("projects/{project}/instances/{instance}");
1034+
}
1035+
1036+
@Test
1037+
void testGetCanonicalResourceName_nameBinding() {
1038+
Set<String> knownResources = ImmutableSet.of("projects", "locations", "instances", "widgets");
1039+
PathTemplate template = PathTemplate.create("v1/{field=projects/*/instances/*}");
1040+
Truth.assertThat(template.getCanonicalResourceName(knownResources))
1041+
.isEqualTo("{field=projects/*/instances/*}");
1042+
}
1043+
1044+
@Test
1045+
void testGetCanonicalResourceName_nameBindingMixedWithSimpleBinding() {
1046+
Set<String> knownResources = ImmutableSet.of("projects", "locations", "instances", "widgets");
1047+
PathTemplate template =
1048+
PathTemplate.create("v1/{field=projects/*/instances/*}/actions/{action}");
1049+
Truth.assertThat(template.getCanonicalResourceName(knownResources))
1050+
.isEqualTo("{field=projects/*/instances/*}/actions/{action}");
1051+
}
1052+
1053+
@Test
1054+
void testGetCanonicalResourceName_nameBindingWithUnknownLiterals() {
1055+
PathTemplate template =
1056+
PathTemplate.create(
1057+
"/compute/v1/projects/{project}/zones/{zone}/{parent_name=reservations/*/reservationBlocks/*/reservationSubBlocks/*}/reservationSlots/{reservation_slot}");
1058+
String canonical = template.getCanonicalResourceName(template.getResourceLiterals());
1059+
Truth.assertThat(canonical)
1060+
.isEqualTo(
1061+
"projects/{project}/zones/{zone}/{parent_name=reservations/*/reservationBlocks/*/reservationSubBlocks/*}/reservationSlots/{reservation_slot}");
1062+
}
1063+
1064+
@Test
1065+
void testGetCanonicalResourceName_nameBindingMixedWithSimpleBinding_moreKnownResources() {
1066+
Set<String> moreKnownResources = ImmutableSet.of("projects", "instances", "actions");
1067+
PathTemplate template =
1068+
PathTemplate.create("v1/{name=projects/*/instances/*}/actions/{action}");
1069+
Truth.assertThat(template.getCanonicalResourceName(moreKnownResources))
1070+
.isEqualTo("projects/*/instances/*/actions/{action}");
1071+
}
1072+
1073+
@Test
1074+
void testGetCanonicalResourceName_nullKnownResources() {
1075+
PathTemplate template =
1076+
PathTemplate.create("v1/projects/{project}/locations/{location}/widgets/{widget}");
1077+
Truth.assertThat(template.getCanonicalResourceName(null)).isEmpty();
1078+
}
1079+
8971080
private static void assertPositionalMatch(Map<String, String> match, String... expected) {
8981081
Truth.assertThat(match).isNotNull();
8991082
int i = 0;

0 commit comments

Comments
 (0)