Skip to content
This repository has been archived by the owner on Sep 27, 2023. It is now read-only.

Commit

Permalink
Support delimiters(_-.~) as start or end characters for a segment. (#336
Browse files Browse the repository at this point in the history
)

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.
  • Loading branch information
blakeli0 committed Feb 4, 2022
1 parent 6a8a0a4 commit 309521b
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 6 deletions.
18 changes: 15 additions & 3 deletions src/main/java/com/google/api/pathtemplate/PathTemplate.java
Original file line number Diff line number Diff line change
Expand Up @@ -887,9 +887,7 @@ private static ImmutableList<Segment> parseTemplate(String template) {

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

private static boolean isSegmentBeginOrEndInvalid(String seg) {
// A segment is invalid if it contains only one character and the character is a delimiter
if (seg.length() == 1 && COMPLEX_DELIMITER_PATTERN.matcher(seg).find()) {
return true;
}
// 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.
// e.g. Invalid: .{well}-{known}, {well}-{known}-
// Valid: .well-known, .well-{known}, .-~{well-known}, these segments are all considered as literals for matching
return (COMPLEX_DELIMITER_PATTERN.matcher(seg.substring(0, 1)).find() && seg.charAt(1) == '{')
|| (COMPLEX_DELIMITER_PATTERN.matcher(seg.substring(seg.length() - 1)).find()
&& seg.charAt(seg.length() - 2) == '}');
}

private static List<Segment> parseComplexResourceId(String seg) {
List<Segment> segments = new ArrayList<>();
List<String> separatorIndices = new ArrayList<>();
Expand Down
77 changes: 74 additions & 3 deletions src/test/java/com/google/api/pathtemplate/PathTemplateTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -487,9 +487,6 @@ public void complexResourceBasicInvalidIds() {
@Test
public void complexResourceMultipleDelimiters() {
thrown.expect(ValidationException.class);
PathTemplate.create("projects/*/zones/.-~{zone_a}");
thrown.expectMessage(
String.format("parse error: invalid begin or end character in '%s'", ".-~{zone_a}"));

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

@Test
public void instantiateWithASegmentStartsWithADelimiter() {
PathTemplate pathTemplate =
PathTemplate.create(
"v1beta1/{parent=projects/*/locations/*/clusters/*}/.well-known/openid-configuration");
String pattern =
"v1beta1/projects/abc/locations/def/clusters/yte/.well-known/openid-configuration";
Truth.assertThat(pathTemplate.matches(pattern)).isTrue();
}

@Test
public void instantiateWithASegmentContainingComplexResourceNamesAndStartsWithADelimiter() {
thrown.expect(ValidationException.class);
PathTemplate.create(
"v1beta1/{parent=projects/*/locations/*/clusters/*}/.{well}-{known}/openid-configuration");
thrown.expectMessage(
String.format("parse error: invalid begin or end character in '%s'", ".{well}-{known}"));
}

@Test
public void
instantiateWithASegmentContainingNoComplexResourceNamesAndStartsWithMultipleDelimiters() {
PathTemplate pathTemplate =
PathTemplate.create(
"v1beta1/{parent=projects/*/locations/*/clusters/*}/.-~well-known/openid-configuration");
String pattern =
"v1beta1/projects/abc/locations/def/clusters/yte/.-~well-known/openid-configuration";
Truth.assertThat(pathTemplate.matches(pattern)).isTrue();
}

@Test
public void instantiateWithASegmentOnlyContainingOneDelimiter() {
thrown.expect(ValidationException.class);
PathTemplate.create("v1/publishers/{publisher}/books/.");
thrown.expectMessage(String.format("parse error: invalid begin or end character in '%s'", "."));
}

@Test
public void instantiateWithASegmentOnlyContainingOneCharacter() {
PathTemplate pathTemplate = PathTemplate.create("v1/publishers/{publisher}/books/a");
String pattern = "v1/publishers/o'reilly/books/a";
Truth.assertThat(pathTemplate.matches(pattern)).isTrue();
}

@Test
public void instantiateWithASegmentEndsWithADelimiter() {
PathTemplate pathTemplate =
PathTemplate.create(
"v1beta1/{parent=projects/*/locations/*/clusters/*}/well-known./openid-configuration");
String pattern =
"v1beta1/projects/abc/locations/def/clusters/yte/well-known./openid-configuration";
Truth.assertThat(pathTemplate.matches(pattern)).isTrue();
}

@Test
public void instantiateWithASegmentContainingComplexResourceNamesAndEndsWithADelimiter() {
thrown.expect(ValidationException.class);
PathTemplate.create(
"v1beta1/{parent=projects/*/locations/*/clusters/*}/{well}-{known}./openid-configuration");
thrown.expectMessage(
String.format("parse error: invalid begin or end character in '%s'", "{well}-{known}."));
}

@Test
public void
instantiateWithASegmentContainingNoComplexResourceNamesAndEndsWithMultipleDelimiters() {
PathTemplate pathTemplate =
PathTemplate.create(
"v1beta1/{parent=projects/*/locations/*/clusters/*}/well-known.-~/openid-configuration");
String pattern =
"v1beta1/projects/abc/locations/def/clusters/yte/well-known.-~/openid-configuration";
Truth.assertThat(pathTemplate.matches(pattern)).isTrue();
}

// Other
// =====

Expand Down

0 comments on commit 309521b

Please sign in to comment.