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,26 @@ private static List<AnnotationNode> createClassAnnotations() {
.build());
}

private static List<TypeNode> createImplementsTypes() {
return Arrays.asList(FIXED_TYPESTORE.get("ResourceName"));
/**
* Builds a list of types to be implemented. If the type has the same name then we use its full
* name
*
* @param implementingClassName class that is implementing the resulting list. Collisions are
* checked
* @return
*/
private static List<TypeNode> createImplementsTypes(String implementingClassName) {
List<TypeNode> preprocessed = Arrays.asList(FIXED_TYPESTORE.get("ResourceName"));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FIXED_TYPESTORE always returns only one result. Can we check if there is conflict and fix it first, then wrap it as a list in the end?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used a slightly different but similarly concise way (wrap both possible results in the end). Hope you agree it's good too.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this looks much better!
nit: It would be great if we can return TypeNode only in this private method and wrap it as a list when we call it, since the list always contains just one element.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I only saw the review comment instead of this one. I'll leave it as a quick follow up

List<TypeNode> result = new ArrayList<>();
for (TypeNode typeNode : preprocessed) {
if (!typeNode.isPrimitiveType()
&& typeNode.reference().name().compareTo(implementingClassName) == 0) {
diegomarquezp marked this conversation as resolved.
Show resolved Hide resolved
result.add(FIXED_TYPESTORE.getWithFullName(typeNode.reference().name()));
diegomarquezp marked this conversation as resolved.
Show resolved Hide resolved
} else {
result.add(typeNode);
}
}
return result;
}

private static List<VariableExpr> createTemplateClassMembers(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,19 @@
package com.google.api.generator.gapic.composer.store;

import com.google.api.generator.engine.ast.ConcreteReference;
import com.google.api.generator.engine.ast.Reference;
import com.google.api.generator.engine.ast.TypeNode;
import com.google.api.generator.engine.ast.VaporReference;
import com.google.api.generator.gapic.model.Message;
import com.google.common.base.Preconditions;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class TypeStore {
private final Map<String, TypeNode> store = new HashMap<>();
private final Map<String, Class<?>> clazzStore = new HashMap<String, Class<?>>();

public TypeStore() {}

Expand All @@ -33,18 +36,42 @@ public TypeStore(List<Class<?>> concreteClasses) {
}

private void putConcreteClassses(List<Class<?>> concreteClasses) {
store.putAll(
concreteClasses.stream()
.collect(
Collectors.toMap(
Class::getSimpleName,
c -> TypeNode.withReference(ConcreteReference.withClazz(c)))));
for (Class<?> clazz : concreteClasses) {
store.put(clazz.getSimpleName(), TypeNode.withReference(ConcreteReference.withClazz(clazz)));
clazzStore.put(clazz.getSimpleName(), clazz);
}
}

public TypeNode get(String typeName) {
return store.get(typeName);
}

public TypeNode getWithFullName(String typeName) {
TypeNode stored = store.get(typeName);
Preconditions.checkNotNull(stored);
Reference ref;
if (stored.reference() instanceof ConcreteReference) {
ref =
diegomarquezp marked this conversation as resolved.
Show resolved Hide resolved
ConcreteReference.builder()
.setClazz(clazzStore.get(typeName))
.setUseFullName(true)
.build();
} else if (stored.reference() instanceof VaporReference) {
ref =
VaporReference.builder()
.setUseFullName(true)
.setName(typeName)
.setPakkage(stored.reference().pakkage())
.build();
} else {
throw new IllegalArgumentException(
String.format(
"Unexpected reference %s of type %s",
typeName, stored.reference().getClass().getSimpleName()));
}
return TypeNode.withReference(ref);
}

public void put(String pakkage, String typeName) {
store.put(
typeName,
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());
}
}
Loading
Loading