Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: resource name class deduplication #1854

Merged
merged 10 commits into from
Jul 18, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ public GapicClass generate(ResourceName resourceName, GapicContext context) {
.setAnnotations(createClassAnnotations())
.setScope(ScopeNode.PUBLIC)
.setName(className)
.setImplementsTypes(createImplementsTypes())
.setImplementsTypes(createImplementsTypes(className))
.setStatements(
createClassStatements(
templateFinalVarExprs,
Expand Down Expand Up @@ -160,8 +160,27 @@ private static List<AnnotationNode> createClassAnnotations() {
.build());
}

private static List<TypeNode> createImplementsTypes() {
return Arrays.asList(FIXED_TYPESTORE.get("ResourceName"));
/**
* Returns a singleton list with {@code ResourceName} as its only member. Checks for collisions
*
* @param implementingClassName class that is implementing the resulting list
*/
private static List<TypeNode> createImplementsTypes(String implementingClassName) {
// the original resource name reference has useFullName == false
TypeNode originalResourceName = FIXED_TYPESTORE.get("ResourceName");
if (implementingClassName.equals(originalResourceName.reference().name())) {
// we create a copy with useFullName == true
return Arrays.asList(
TypeNode.withReference(
ConcreteReference.builder()
.setUseFullName(true)
.setClazz(com.google.api.resourcenames.ResourceName.class)
.setGenerics(originalResourceName.reference().generics())
.setIsStaticImport(originalResourceName.reference().isStaticImport())
.setWildcardUpperBound(originalResourceName.reference().wildcardUpperBound())
.build()));
}
return Arrays.asList(originalResourceName);
}

private static List<VariableExpr> createTemplateClassMembers(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

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

import com.google.api.generator.engine.writer.JavaWriterVisitor;
import com.google.api.generator.gapic.model.GapicClass;
Expand All @@ -34,6 +35,7 @@
import com.google.protobuf.Descriptors.ServiceDescriptor;
import com.google.showcase.v1beta1.EchoOuterClass;
import com.google.showcase.v1beta1.TestingOuterClass;
import com.google.test.collisions.CollisionsOuterClass;
import google.cloud.CommonResources;
import java.nio.file.Path;
import java.nio.file.Paths;
Expand All @@ -50,6 +52,9 @@
import org.junit.Test;

public class ResourceNameHelperClassComposerTest {

private final String COLLIDING_RESOURCE_NAME_KEY = "config.googleapis.com/Resource";

private ServiceDescriptor echoService;
private FileDescriptor echoFileDescriptor;

Expand Down Expand Up @@ -238,4 +243,29 @@ public void generateResourceNameClass_childSingleton() {
Paths.get(GoldenFileWriter.getGoldenDir(this.getClass()), "AgentName.golden");
Assert.assertCodeEquals(goldenFilePath, visitor.write());
}

@Test
public void generateResourceNameClass_resourceNameCollisionIsAvoided() {
ResourceName collidingResourceName =
Parser.parseResourceNames(CollisionsOuterClass.getDescriptor())
.get(COLLIDING_RESOURCE_NAME_KEY);

GapicContext irrelevantContext = TestProtoLoader.instance().parseShowcaseEcho();
GapicClass clazz =
ResourceNameHelperClassComposer.instance()
.generate(collidingResourceName, irrelevantContext);
JavaWriterVisitor visitor = new JavaWriterVisitor();
clazz.classDefinition().accept(visitor);
GoldenFileWriter.saveCodegenToFile(
this.getClass(), "CollisionResourceName.golden", visitor.write());
Path goldenFilePath =
Paths.get(GoldenFileWriter.getGoldenDir(this.getClass()), "CollisionResourceName.golden");
Assert.assertCodeEquals(goldenFilePath, visitor.write());

assertEquals(1, clazz.classDefinition().implementsTypes().size());
assertTrue(clazz.classDefinition().implementsTypes().get(0).reference().useFullName());
assertEquals(
clazz.classDefinition().classIdentifier().name(),
clazz.classDefinition().implementsTypes().get(0).reference().name());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
package com.google.test.collisions;

import com.google.api.pathtemplate.PathTemplate;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.annotation.Generated;

// AUTO-GENERATED DOCUMENTATION AND CLASS.
@Generated("by gapic-generator-java")
public class ResourceName implements com.google.api.resourcenames.ResourceName {
private static final PathTemplate PROJECT_LOCATION_DEPLOYMENT_REVISION_RESOURCE =
PathTemplate.createWithoutUrlEncoding(
"projects/{project}/locations/{location}/deployments/{deployment}/revisions/{revision}/resources/{resource}");
private volatile Map<String, String> fieldValuesMap;
private final String project;
private final String location;
private final String deployment;
private final String revision;
private final String resource;

@Deprecated
protected ResourceName() {
project = null;
location = null;
deployment = null;
revision = null;
resource = null;
}

private ResourceName(Builder builder) {
project = Preconditions.checkNotNull(builder.getProject());
location = Preconditions.checkNotNull(builder.getLocation());
deployment = Preconditions.checkNotNull(builder.getDeployment());
revision = Preconditions.checkNotNull(builder.getRevision());
resource = Preconditions.checkNotNull(builder.getResource());
}

public String getProject() {
return project;
}

public String getLocation() {
return location;
}

public String getDeployment() {
return deployment;
}

public String getRevision() {
return revision;
}

public String getResource() {
return resource;
}

public static Builder newBuilder() {
return new Builder();
}

public Builder toBuilder() {
return new Builder(this);
}

public static ResourceName of(
String project, String location, String deployment, String revision, String resource) {
return newBuilder()
.setProject(project)
.setLocation(location)
.setDeployment(deployment)
.setRevision(revision)
.setResource(resource)
.build();
}

public static String format(
String project, String location, String deployment, String revision, String resource) {
return newBuilder()
.setProject(project)
.setLocation(location)
.setDeployment(deployment)
.setRevision(revision)
.setResource(resource)
.build()
.toString();
}

public static ResourceName parse(String formattedString) {
if (formattedString.isEmpty()) {
return null;
}
Map<String, String> matchMap =
PROJECT_LOCATION_DEPLOYMENT_REVISION_RESOURCE.validatedMatch(
formattedString, "ResourceName.parse: formattedString not in valid format");
return of(
matchMap.get("project"),
matchMap.get("location"),
matchMap.get("deployment"),
matchMap.get("revision"),
matchMap.get("resource"));
}

public static List<ResourceName> parseList(List<String> formattedStrings) {
List<ResourceName> list = new ArrayList<>(formattedStrings.size());
for (String formattedString : formattedStrings) {
list.add(parse(formattedString));
}
return list;
}

public static List<String> toStringList(List<ResourceName> values) {
List<String> list = new ArrayList<>(values.size());
for (ResourceName value : values) {
if (value == null) {
list.add("");
} else {
list.add(value.toString());
}
}
return list;
}

public static boolean isParsableFrom(String formattedString) {
return PROJECT_LOCATION_DEPLOYMENT_REVISION_RESOURCE.matches(formattedString);
}

@Override
public Map<String, String> getFieldValuesMap() {
if (fieldValuesMap == null) {
synchronized (this) {
if (fieldValuesMap == null) {
ImmutableMap.Builder<String, String> fieldMapBuilder = ImmutableMap.builder();
if (project != null) {
fieldMapBuilder.put("project", project);
}
if (location != null) {
fieldMapBuilder.put("location", location);
}
if (deployment != null) {
fieldMapBuilder.put("deployment", deployment);
}
if (revision != null) {
fieldMapBuilder.put("revision", revision);
}
if (resource != null) {
fieldMapBuilder.put("resource", resource);
}
fieldValuesMap = fieldMapBuilder.build();
}
}
}
return fieldValuesMap;
}

public String getFieldValue(String fieldName) {
return getFieldValuesMap().get(fieldName);
}

@Override
public String toString() {
return PROJECT_LOCATION_DEPLOYMENT_REVISION_RESOURCE.instantiate(
"project",
project,
"location",
location,
"deployment",
deployment,
"revision",
revision,
"resource",
resource);
}

@Override
public boolean equals(java.lang.Object o) {
if (o == this) {
return true;
}
if (o != null || getClass() == o.getClass()) {
ResourceName that = ((ResourceName) o);
return Objects.equals(this.project, that.project)
&& Objects.equals(this.location, that.location)
&& Objects.equals(this.deployment, that.deployment)
&& Objects.equals(this.revision, that.revision)
&& Objects.equals(this.resource, that.resource);
}
return false;
}

@Override
public int hashCode() {
int h = 1;
h *= 1000003;
h ^= Objects.hashCode(project);
h *= 1000003;
h ^= Objects.hashCode(location);
h *= 1000003;
h ^= Objects.hashCode(deployment);
h *= 1000003;
h ^= Objects.hashCode(revision);
h *= 1000003;
h ^= Objects.hashCode(resource);
return h;
}

/**
* Builder for
* projects/{project}/locations/{location}/deployments/{deployment}/revisions/{revision}/resources/{resource}.
*/
public static class Builder {
private String project;
private String location;
private String deployment;
private String revision;
private String resource;

protected Builder() {}

public String getProject() {
return project;
}

public String getLocation() {
return location;
}

public String getDeployment() {
return deployment;
}

public String getRevision() {
return revision;
}

public String getResource() {
return resource;
}

public Builder setProject(String project) {
this.project = project;
return this;
}

public Builder setLocation(String location) {
this.location = location;
return this;
}

public Builder setDeployment(String deployment) {
this.deployment = deployment;
return this;
}

public Builder setRevision(String revision) {
this.revision = revision;
return this;
}

public Builder setResource(String resource) {
this.resource = resource;
return this;
}

private Builder(ResourceName resourceName) {
this.project = resourceName.project;
this.location = resourceName.location;
this.deployment = resourceName.deployment;
this.revision = resourceName.revision;
this.resource = resourceName.resource;
}

public ResourceName build() {
return new ResourceName(this);
}
}
}
Loading
Loading