Skip to content

Commit 5e0b8f8

Browse files
authored
Support delimiters(_-.~) as start or end characters for a segment. (#336)
Support delimiters(_-.~) as start or end characters for a segment if the segment does not contain complex resource names. A segment can start with a delimiter, as long as there is no { right after it. A segment can end with a delimiter, as long as there is no } right before it. A segment like .{well}-{known} or {well}-{known}. is invalid. A segment like .well-known, .well-{known} or .-~{well-known} is considered a literal hence is valid.
1 parent 1c332e6 commit 5e0b8f8

File tree

2 files changed

+89
-6
lines changed

2 files changed

+89
-6
lines changed

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

+15-3
Original file line numberDiff line numberDiff line change
@@ -887,9 +887,7 @@ private static ImmutableList<Segment> parseTemplate(String template) {
887887

888888
boolean isLastSegment = (template.indexOf(seg) + seg.length()) == template.length();
889889
boolean isCollectionWildcard = !isLastSegment && (seg.equals("-") || seg.equals("-}"));
890-
if (!isCollectionWildcard
891-
&& (COMPLEX_DELIMITER_PATTERN.matcher(seg.substring(0, 1)).find()
892-
|| COMPLEX_DELIMITER_PATTERN.matcher(seg.substring(seg.length() - 1)).find())) {
890+
if (!isCollectionWildcard && isSegmentBeginOrEndInvalid(seg)) {
893891
throw new ValidationException("parse error: invalid begin or end character in '%s'", seg);
894892
}
895893
// Disallow zero or multiple delimiters between variable names.
@@ -1015,6 +1013,20 @@ private static ImmutableList<Segment> parseTemplate(String template) {
10151013
return builder.build();
10161014
}
10171015

1016+
private static boolean isSegmentBeginOrEndInvalid(String seg) {
1017+
// A segment is invalid if it contains only one character and the character is a delimiter
1018+
if (seg.length() == 1 && COMPLEX_DELIMITER_PATTERN.matcher(seg).find()) {
1019+
return true;
1020+
}
1021+
// A segment can start with a delimiter, as long as there is no { right after it.
1022+
// A segment can end with a delimiter, as long as there is no } right before it.
1023+
// e.g. Invalid: .{well}-{known}, {well}-{known}-
1024+
// Valid: .well-known, .well-{known}, .-~{well-known}, these segments are all considered as literals for matching
1025+
return (COMPLEX_DELIMITER_PATTERN.matcher(seg.substring(0, 1)).find() && seg.charAt(1) == '{')
1026+
|| (COMPLEX_DELIMITER_PATTERN.matcher(seg.substring(seg.length() - 1)).find()
1027+
&& seg.charAt(seg.length() - 2) == '}');
1028+
}
1029+
10181030
private static List<Segment> parseComplexResourceId(String seg) {
10191031
List<Segment> segments = new ArrayList<>();
10201032
List<String> separatorIndices = new ArrayList<>();

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

+74-3
Original file line numberDiff line numberDiff line change
@@ -487,9 +487,6 @@ public void complexResourceBasicInvalidIds() {
487487
@Test
488488
public void complexResourceMultipleDelimiters() {
489489
thrown.expect(ValidationException.class);
490-
PathTemplate.create("projects/*/zones/.-~{zone_a}");
491-
thrown.expectMessage(
492-
String.format("parse error: invalid begin or end character in '%s'", ".-~{zone_a}"));
493490

494491
PathTemplate.create("projects/*/zones/{zone_a}~.{zone_b}");
495492
thrown.expectMessage(
@@ -717,6 +714,80 @@ public void instantiateWithCustomVerbs() {
717714
Truth.assertThat(template.matches(templateInstance)).isTrue();
718715
}
719716

717+
@Test
718+
public void instantiateWithASegmentStartsWithADelimiter() {
719+
PathTemplate pathTemplate =
720+
PathTemplate.create(
721+
"v1beta1/{parent=projects/*/locations/*/clusters/*}/.well-known/openid-configuration");
722+
String pattern =
723+
"v1beta1/projects/abc/locations/def/clusters/yte/.well-known/openid-configuration";
724+
Truth.assertThat(pathTemplate.matches(pattern)).isTrue();
725+
}
726+
727+
@Test
728+
public void instantiateWithASegmentContainingComplexResourceNamesAndStartsWithADelimiter() {
729+
thrown.expect(ValidationException.class);
730+
PathTemplate.create(
731+
"v1beta1/{parent=projects/*/locations/*/clusters/*}/.{well}-{known}/openid-configuration");
732+
thrown.expectMessage(
733+
String.format("parse error: invalid begin or end character in '%s'", ".{well}-{known}"));
734+
}
735+
736+
@Test
737+
public void
738+
instantiateWithASegmentContainingNoComplexResourceNamesAndStartsWithMultipleDelimiters() {
739+
PathTemplate pathTemplate =
740+
PathTemplate.create(
741+
"v1beta1/{parent=projects/*/locations/*/clusters/*}/.-~well-known/openid-configuration");
742+
String pattern =
743+
"v1beta1/projects/abc/locations/def/clusters/yte/.-~well-known/openid-configuration";
744+
Truth.assertThat(pathTemplate.matches(pattern)).isTrue();
745+
}
746+
747+
@Test
748+
public void instantiateWithASegmentOnlyContainingOneDelimiter() {
749+
thrown.expect(ValidationException.class);
750+
PathTemplate.create("v1/publishers/{publisher}/books/.");
751+
thrown.expectMessage(String.format("parse error: invalid begin or end character in '%s'", "."));
752+
}
753+
754+
@Test
755+
public void instantiateWithASegmentOnlyContainingOneCharacter() {
756+
PathTemplate pathTemplate = PathTemplate.create("v1/publishers/{publisher}/books/a");
757+
String pattern = "v1/publishers/o'reilly/books/a";
758+
Truth.assertThat(pathTemplate.matches(pattern)).isTrue();
759+
}
760+
761+
@Test
762+
public void instantiateWithASegmentEndsWithADelimiter() {
763+
PathTemplate pathTemplate =
764+
PathTemplate.create(
765+
"v1beta1/{parent=projects/*/locations/*/clusters/*}/well-known./openid-configuration");
766+
String pattern =
767+
"v1beta1/projects/abc/locations/def/clusters/yte/well-known./openid-configuration";
768+
Truth.assertThat(pathTemplate.matches(pattern)).isTrue();
769+
}
770+
771+
@Test
772+
public void instantiateWithASegmentContainingComplexResourceNamesAndEndsWithADelimiter() {
773+
thrown.expect(ValidationException.class);
774+
PathTemplate.create(
775+
"v1beta1/{parent=projects/*/locations/*/clusters/*}/{well}-{known}./openid-configuration");
776+
thrown.expectMessage(
777+
String.format("parse error: invalid begin or end character in '%s'", "{well}-{known}."));
778+
}
779+
780+
@Test
781+
public void
782+
instantiateWithASegmentContainingNoComplexResourceNamesAndEndsWithMultipleDelimiters() {
783+
PathTemplate pathTemplate =
784+
PathTemplate.create(
785+
"v1beta1/{parent=projects/*/locations/*/clusters/*}/well-known.-~/openid-configuration");
786+
String pattern =
787+
"v1beta1/projects/abc/locations/def/clusters/yte/well-known.-~/openid-configuration";
788+
Truth.assertThat(pathTemplate.matches(pattern)).isTrue();
789+
}
790+
720791
// Other
721792
// =====
722793

0 commit comments

Comments
 (0)