Skip to content

Commit

Permalink
[ggj][codegen][engx] fix: separate resname tokenizing from class comp…
Browse files Browse the repository at this point in the history
…oser (#344)

* fix!: refactor field into MethodArgument, add enum/msg flags

* feat: partial isAssignableFrom VaporRef support, enable full-name type usage

* feat: support negative numeric literals

* fix: separate resname tokenizing from class composer
  • Loading branch information
miraleung authored Sep 26, 2020
1 parent 9e79457 commit c396a76
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 122 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,6 @@

public class ResourceNameHelperClassComposer {
private static final String CLASS_NAME_PATTERN = "%sName";
private static final String SLASH = "/";
private static final String LEFT_BRACE = "{";
private static final String RIGHT_BRACE = "}";

private static final String BUILDER_CLASS_HEADER_PATTERN = "Builder for %s.";

private static final ResourceNameHelperClassComposer INSTANCE =
Expand All @@ -89,7 +85,8 @@ public static ResourceNameHelperClassComposer instance() {
}

public GapicClass generate(ResourceName resourceName) {
List<List<String>> tokenHierarchies = parseTokenHierarchy(resourceName.patterns());
List<List<String>> tokenHierarchies =
ResourceNameTokenizer.parseTokenHierarchy(resourceName.patterns());
Map<String, TypeNode> types = createDynamicTypes(resourceName, tokenHierarchies);
List<VariableExpr> templateFinalVarExprs = createTemplateClassMembers(tokenHierarchies);
Map<String, VariableExpr> patternTokenVarExprs =
Expand Down Expand Up @@ -1448,40 +1445,6 @@ private static TypeNode getBuilderType(
: types.get(getBuilderTypeName(tokenHierarchies, index));
}

@VisibleForTesting
static List<List<String>> parseTokenHierarchy(List<String> patterns) {
List<String> nonSlashSepStrings = Arrays.asList("}_{", "}-{", "}.{", "}~{");

List<List<String>> tokenHierachies = new ArrayList<>();
for (String pattern : patterns) {
List<String> hierarchy = new ArrayList<>();
Set<String> vars = PathTemplate.create(pattern).vars();
String[] patternTokens = pattern.split(SLASH);
for (String patternToken : patternTokens) {
if (patternToken.startsWith(LEFT_BRACE) && patternToken.endsWith(RIGHT_BRACE)) {
String processedPatternToken = patternToken;

// Handle non-slash separators.
if (nonSlashSepStrings.stream().anyMatch(s -> patternToken.contains(s))) {
for (String str : nonSlashSepStrings) {
processedPatternToken = processedPatternToken.replace(str, "_");
}
} else {
// Handles wildcards.
processedPatternToken =
vars.stream()
.filter(v -> patternToken.contains(v))
.collect(Collectors.toList())
.get(0);
}
hierarchy.add(processedPatternToken.replace("{", "").replace("}", ""));
}
}
tokenHierachies.add(hierarchy);
}
return tokenHierachies;
}

@VisibleForTesting
static Set<String> getTokenSet(List<List<String>> tokenHierarchy) {
return tokenHierarchy.stream()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.api.generator.gapic.composer;

import com.google.api.pathtemplate.PathTemplate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public class ResourceNameTokenizer {
private static final String SLASH = "/";
private static final String LEFT_BRACE = "{";
private static final String RIGHT_BRACE = "}";

static List<List<String>> parseTokenHierarchy(List<String> patterns) {
List<String> nonSlashSepStrings = Arrays.asList("}_{", "}-{", "}.{", "}~{");

List<List<String>> tokenHierachies = new ArrayList<>();
for (String pattern : patterns) {
List<String> hierarchy = new ArrayList<>();
Set<String> vars = PathTemplate.create(pattern).vars();
String[] patternTokens = pattern.split(SLASH);
for (String patternToken : patternTokens) {
if (patternToken.startsWith(LEFT_BRACE) && patternToken.endsWith(RIGHT_BRACE)) {
String processedPatternToken = patternToken;

// Handle non-slash separators.
if (nonSlashSepStrings.stream().anyMatch(s -> patternToken.contains(s))) {
for (String str : nonSlashSepStrings) {
processedPatternToken = processedPatternToken.replace(str, "_");
}
} else {
// Handles wildcards.
processedPatternToken =
vars.stream()
.filter(v -> patternToken.contains(v))
.collect(Collectors.toList())
.get(0);
}
hierarchy.add(processedPatternToken.replace("{", "").replace("}", ""));
}
}
tokenHierachies.add(hierarchy);
}
return tokenHierachies;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,95 +48,13 @@ public void setUp() {
assertEquals(echoService.getName(), "Echo");
}

@Test
public void parseTokenHierarchy_basic() {
List<String> patterns =
Arrays.asList(
"projects/{project}/locations/{location}/autoscalingPolicies/{autoscaling_policy}",
"projects/{project}/regions/{region}/autoscalingPolicies/{autoscaling_policy}",
"projects/{project}/autoscalingPolicies/{autoscaling_policy}");
List<List<String>> tokenHierarchies =
ResourceNameHelperClassComposer.parseTokenHierarchy(patterns);
assertEquals(3, tokenHierarchies.size());
assertThat(tokenHierarchies.get(0))
.containsExactly("project", "location", "autoscaling_policy");
assertThat(tokenHierarchies.get(1)).containsExactly("project", "region", "autoscaling_policy");
assertThat(tokenHierarchies.get(2)).containsExactly("project", "autoscaling_policy");
}

@Test
public void parseTokenHierarchy_wildcards() {
List<String> patterns =
Arrays.asList(
"projects/{project}/metricDescriptors/{metric_descriptor=**}",
"organizations/{organization}/metricDescriptors/{metric_descriptor=**}",
"folders/{folder=**}/metricDescriptors/{metric_descriptor}");
List<List<String>> tokenHierarchies =
ResourceNameHelperClassComposer.parseTokenHierarchy(patterns);
assertEquals(3, tokenHierarchies.size());
assertThat(tokenHierarchies.get(0)).containsExactly("project", "metric_descriptor");
assertThat(tokenHierarchies.get(1)).containsExactly("organization", "metric_descriptor");
assertThat(tokenHierarchies.get(2)).containsExactly("folder", "metric_descriptor");
}

@Test
public void parseTokenHierarchy_singletonCollection() {
List<String> patterns =
Arrays.asList(
"projects/{project}/agent/sessions/{session}",
"projects/{project}/agent/environments/{environment}/users/{user}/sessions/{session}");
List<List<String>> tokenHierarchies =
ResourceNameHelperClassComposer.parseTokenHierarchy(patterns);
assertEquals(2, tokenHierarchies.size());
assertThat(tokenHierarchies.get(0)).containsExactly("project", "session");
assertThat(tokenHierarchies.get(1))
.containsExactly("project", "environment", "user", "session");
}

@Test
public void parseTokenHierarchy_singletonCollectionAndNonSlashSeparators() {
List<String> patterns =
Arrays.asList(
"users/{user}/profile/blurbs/legacy/{legacy_user}~{blurb}",
"users/{user}/profile/blurbs/{blurb}",
"rooms/{room}/blurbs/{blurb}",
"users/{user}/profile/blurbs/legacy/{legacy_document}_{blurb}",
"users/{user}/profile/blurbs/legacy/{legacy_book}-{blurb}",
"rooms/{room}/blurbs/legacy/{legacy_room}.{blurb}");

List<List<String>> tokenHierarchies =
ResourceNameHelperClassComposer.parseTokenHierarchy(patterns);
assertEquals(6, tokenHierarchies.size());
assertThat(tokenHierarchies.get(0)).containsExactly("user", "legacy_user_blurb");
assertThat(tokenHierarchies.get(1)).containsExactly("user", "blurb");
assertThat(tokenHierarchies.get(2)).containsExactly("room", "blurb");
assertThat(tokenHierarchies.get(3)).containsExactly("user", "legacy_document_blurb");
assertThat(tokenHierarchies.get(4)).containsExactly("user", "legacy_book_blurb");
assertThat(tokenHierarchies.get(5)).containsExactly("room", "legacy_room_blurb");
}

@Test
public void parseTokenHierarchy_invalidPatterns() {
List<String> patterns =
Arrays.asList(
"projects/{project}/agent/sessions/{session}/anotherSingleton",
"{project}/agent/environments/{environment}/users/{user}/sessions/{session}");
List<List<String>> tokenHierarchies =
ResourceNameHelperClassComposer.parseTokenHierarchy(patterns);
assertEquals(2, tokenHierarchies.size());
assertThat(tokenHierarchies.get(0)).containsExactly("project", "session");
assertThat(tokenHierarchies.get(1))
.containsExactly("project", "environment", "user", "session");
}

@Test
public void getTokenSet_basic() {
List<String> patterns =
Arrays.asList(
"projects/{project}/agent/sessions/{session}",
"projects/{project}/agent/environments/{environment}/users/{user}/sessions/{session}");
List<List<String>> tokenHierarchies =
ResourceNameHelperClassComposer.parseTokenHierarchy(patterns);
List<List<String>> tokenHierarchies = ResourceNameTokenizer.parseTokenHierarchy(patterns);

Set<String> tokenSet = ResourceNameHelperClassComposer.getTokenSet(tokenHierarchies);
assertEquals(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.api.generator.gapic.composer;

import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertEquals;

import com.google.protobuf.Descriptors.FileDescriptor;
import com.google.protobuf.Descriptors.ServiceDescriptor;
import com.google.showcase.v1beta1.EchoOuterClass;
import java.util.Arrays;
import java.util.List;
import org.junit.Before;
import org.junit.Test;

public class ResourceNameTokenizerTest {
private ServiceDescriptor echoService;
private FileDescriptor echoFileDescriptor;

@Before
public void setUp() {
echoFileDescriptor = EchoOuterClass.getDescriptor();
echoService = echoFileDescriptor.getServices().get(0);
assertEquals(echoService.getName(), "Echo");
}

@Test
public void parseTokenHierarchy_basic() {
List<String> patterns =
Arrays.asList(
"projects/{project}/locations/{location}/autoscalingPolicies/{autoscaling_policy}",
"projects/{project}/regions/{region}/autoscalingPolicies/{autoscaling_policy}",
"projects/{project}/autoscalingPolicies/{autoscaling_policy}");
List<List<String>> tokenHierarchies = ResourceNameTokenizer.parseTokenHierarchy(patterns);
assertEquals(3, tokenHierarchies.size());
assertThat(tokenHierarchies.get(0))
.containsExactly("project", "location", "autoscaling_policy");
assertThat(tokenHierarchies.get(1)).containsExactly("project", "region", "autoscaling_policy");
assertThat(tokenHierarchies.get(2)).containsExactly("project", "autoscaling_policy");
}

@Test
public void parseTokenHierarchy_wildcards() {
List<String> patterns =
Arrays.asList(
"projects/{project}/metricDescriptors/{metric_descriptor=**}",
"organizations/{organization}/metricDescriptors/{metric_descriptor=**}",
"folders/{folder=**}/metricDescriptors/{metric_descriptor}");
List<List<String>> tokenHierarchies = ResourceNameTokenizer.parseTokenHierarchy(patterns);
assertEquals(3, tokenHierarchies.size());
assertThat(tokenHierarchies.get(0)).containsExactly("project", "metric_descriptor");
assertThat(tokenHierarchies.get(1)).containsExactly("organization", "metric_descriptor");
assertThat(tokenHierarchies.get(2)).containsExactly("folder", "metric_descriptor");
}

@Test
public void parseTokenHierarchy_singletonCollection() {
List<String> patterns =
Arrays.asList(
"projects/{project}/agent/sessions/{session}",
"projects/{project}/agent/environments/{environment}/users/{user}/sessions/{session}");
List<List<String>> tokenHierarchies = ResourceNameTokenizer.parseTokenHierarchy(patterns);
assertEquals(2, tokenHierarchies.size());
assertThat(tokenHierarchies.get(0)).containsExactly("project", "session");
assertThat(tokenHierarchies.get(1))
.containsExactly("project", "environment", "user", "session");
}

@Test
public void parseTokenHierarchy_singletonCollectionAndNonSlashSeparators() {
List<String> patterns =
Arrays.asList(
"users/{user}/profile/blurbs/legacy/{legacy_user}~{blurb}",
"users/{user}/profile/blurbs/{blurb}",
"rooms/{room}/blurbs/{blurb}",
"users/{user}/profile/blurbs/legacy/{legacy_document}_{blurb}",
"users/{user}/profile/blurbs/legacy/{legacy_book}-{blurb}",
"rooms/{room}/blurbs/legacy/{legacy_room}.{blurb}");

List<List<String>> tokenHierarchies = ResourceNameTokenizer.parseTokenHierarchy(patterns);
assertEquals(6, tokenHierarchies.size());
assertThat(tokenHierarchies.get(0)).containsExactly("user", "legacy_user_blurb");
assertThat(tokenHierarchies.get(1)).containsExactly("user", "blurb");
assertThat(tokenHierarchies.get(2)).containsExactly("room", "blurb");
assertThat(tokenHierarchies.get(3)).containsExactly("user", "legacy_document_blurb");
assertThat(tokenHierarchies.get(4)).containsExactly("user", "legacy_book_blurb");
assertThat(tokenHierarchies.get(5)).containsExactly("room", "legacy_room_blurb");
}

@Test
public void parseTokenHierarchy_invalidPatterns() {
List<String> patterns =
Arrays.asList(
"projects/{project}/agent/sessions/{session}/anotherSingleton",
"{project}/agent/environments/{environment}/users/{user}/sessions/{session}");
List<List<String>> tokenHierarchies = ResourceNameTokenizer.parseTokenHierarchy(patterns);
assertEquals(2, tokenHierarchies.size());
assertThat(tokenHierarchies.get(0)).containsExactly("project", "session");
assertThat(tokenHierarchies.get(1))
.containsExactly("project", "environment", "user", "session");
}
}

0 comments on commit c396a76

Please sign in to comment.