Skip to content

Commit

Permalink
feat (java-generator/core) : CustomResource classes generated should
Browse files Browse the repository at this point in the history
have `@EqualsAndHashCode(callSuper = true)` (fabric8io#4259)

CustomResource classes generated in extra-annotations mode by Java generator
should have `@EqualsAndHashCode` annotation configured with `callSuper = true` in
case class extends another class.

Signed-off-by: Rohan Kumar <rohaan@redhat.com>
  • Loading branch information
rohanKanojia committed Jul 28, 2022
1 parent e28f569 commit c0f7960
Show file tree
Hide file tree
Showing 10 changed files with 185 additions and 92 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* Fix #4041: adding Quantity.getNumericalAmount with an explanation about bytes and cores.
* Fix #4241: added more context to informer logs with the endpoint path
* Fix #4250: allowing for deserialization of polymorphic unwrapped fields
* Fix #4259: Java Generator's CR should have Lombok's `@EqualsAndHashCode` with `callSuper = true`

#### Dependency Upgrade

Expand Down
5 changes: 5 additions & 0 deletions java-generator/core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,17 +106,16 @@ public GeneratorResult generateJava() {
? new ClassOrInterfaceType().setName(this.pkg + "." + this.statusClassName)
: jlVoid;

if (config.isObjectExtraAnnotations()) {
addExtraAnnotations(clz);
}

ClassOrInterfaceType crType = new ClassOrInterfaceType()
.setName("io.fabric8.kubernetes.client.CustomResource")
.setTypeArguments(spec, status);

clz.addExtendedType(crType);
clz.addImplementedType("io.fabric8.kubernetes.api.model.Namespaced");

if (config.isObjectExtraAnnotations()) {
addExtraAnnotations(clz);
}
return new GeneratorResult(
Collections.singletonList(new GeneratorResult.ClassResult(className, cu)));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,13 @@ public interface JObjectExtraAnnotations {

default void addExtraAnnotations(ClassOrInterfaceDeclaration clz) {
clz.addAnnotation("lombok.ToString");
clz.addAnnotation("lombok.EqualsAndHashCode");
if (clz.getExtendedTypes().isEmpty()) {
clz.addAnnotation("lombok.EqualsAndHashCode");
} else {
clz.addAnnotation(new SingleMemberAnnotationExpr(
new Name("lombok.EqualsAndHashCode"),
new NameExpr("callSuper = true")));
}
clz.addAnnotation("lombok.Setter");

clz.addAnnotation(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,20 @@
import io.fabric8.kubernetes.api.model.apiextensions.v1.CustomResourceDefinition;
import io.fabric8.kubernetes.client.utils.Serialization;
import org.approvaltests.Approvals;
import org.junit.jupiter.api.Test;
import org.approvaltests.namer.NamedEnvironment;
import org.approvaltests.namer.NamerFactory;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

import static com.google.common.truth.Truth.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;

class ApprovalTest {

CRGeneratorRunner runner = new CRGeneratorRunner(new Config());

CustomResourceDefinition getCRD(String name) {
return Serialization.unmarshal(
this.getClass().getClassLoader().getResourceAsStream(name),
Expand All @@ -42,95 +44,41 @@ String getJavaClass(List<GeneratorResult.ClassResult> classResults, String name)
return cr.getCompilationUnit().toString();
}

@Test
void testCrontabCrd() {
// Arrange
CustomResourceDefinition crd = getCRD("crontab-crd.yml");

// Act
List<WritableCRCompilationUnit> writables = runner.generate(crd, runner.getPackage("test.org"));

// Assert
assertEquals(1, writables.size());
assertThat(writables.size()).isEqualTo(1);

WritableCRCompilationUnit writable = writables.get(0);

List<String> underTest = new ArrayList<>();
List<GeneratorResult.ClassResult> crl = writable.getClassResults();
underTest.add(getJavaClass(crl, "CronTab"));
underTest.add(getJavaClass(crl, "CronTabSpec"));
underTest.add(getJavaClass(crl, "CronTabStatus"));

Approvals.verifyAll("CrontabJavaCr", underTest);
}

@Test
void testKeycloakCrd() {
// Arrange
CustomResourceDefinition crd = getCRD("keycloak-crd.yml");

// Act
List<WritableCRCompilationUnit> writables = runner.generate(crd, runner.getPackage("test.org"));

// Assert
assertEquals(1, writables.size());
assertThat(writables.size()).isEqualTo(1);

WritableCRCompilationUnit writable = writables.get(0);

List<String> underTest = new ArrayList<>();
List<GeneratorResult.ClassResult> crl = writable.getClassResults();
underTest.add(getJavaClass(crl, "Keycloak"));
underTest.add(getJavaClass(crl, "KeycloakSpec"));
underTest.add(getJavaClass(crl, "KeycloakStatus"));

Approvals.verifyAll("KeycloakJavaCr", underTest);
}

@Test
void testJokeCrd() {
// Arrange
CustomResourceDefinition crd = getCRD("jokerequests-crd.yml");

// Act
List<WritableCRCompilationUnit> writables = runner.generate(crd, runner.getPackage("test.org"));

// Assert
assertEquals(1, writables.size());
assertThat(writables.size()).isEqualTo(1);

WritableCRCompilationUnit writable = writables.get(0);

List<String> underTest = new ArrayList<>();
List<GeneratorResult.ClassResult> crl = writable.getClassResults();
underTest.add(getJavaClass(crl, "JokeRequest"));
underTest.add(getJavaClass(crl, "JokeRequestSpec"));
underTest.add(getJavaClass(crl, "JokeRequestStatus"));

Approvals.verifyAll("JokeRequestJavaCr", underTest);
private static Stream<Arguments> getCRDInput() {
return Stream.of(
Arguments.of("testCrontabCrd", "crontab-crd.yml", "CronTab", "CrontabJavaCr", new Config()),
Arguments.of("testCrontabExtraAnnotationsCrd", "crontab-crd.yml", "CronTab", "CrontabJavaExtraAnnotationsCr",
new Config(null, null, null, null, Boolean.TRUE, null)),
Arguments.of("testKeycloakCrd", "keycloak-crd.yml", "Keycloak", "KeycloakJavaCr", new Config()),
Arguments.of("testJokeCrd", "jokerequests-crd.yml", "JokeRequest", "JokeRequestJavaCr", new Config()),
Arguments.of("testAkkaMicroservicesCrd", "akka-microservices-crd.yml", "AkkaMicroservice", "AkkaMicroserviceJavaCr",
new Config()));
}

@Test
void testAkkaMicroservicesCrd() {
// Arrange
CustomResourceDefinition crd = getCRD("akka-microservices-crd.yml");
@ParameterizedTest
@MethodSource("getCRDInput")
void generate_withValidCrd_shouldGeneratePojos(String parameter, String crdYaml, String customResourceName,
String approvalLabel, Config config) {
try (NamedEnvironment en = NamerFactory.withParameters(parameter)) {
// Arrange
CRGeneratorRunner runner = new CRGeneratorRunner(config);
CustomResourceDefinition crd = getCRD(crdYaml);

// Act
List<WritableCRCompilationUnit> writables = runner.generate(crd, runner.getPackage("test.org"));
// Act
List<WritableCRCompilationUnit> writables = runner.generate(crd, runner.getPackage("test.org"));

// Assert
assertEquals(1, writables.size());
assertThat(writables.size()).isEqualTo(1);
// Assert
assertThat(writables).hasSize(1);

WritableCRCompilationUnit writable = writables.get(0);
WritableCRCompilationUnit writable = writables.get(0);

List<String> underTest = new ArrayList<>();
List<GeneratorResult.ClassResult> crl = writable.getClassResults();
underTest.add(getJavaClass(crl, "AkkaMicroservice"));
underTest.add(getJavaClass(crl, "AkkaMicroserviceSpec"));
underTest.add(getJavaClass(crl, "AkkaMicroserviceStatus"));
List<String> underTest = new ArrayList<>();
List<GeneratorResult.ClassResult> crl = writable.getClassResults();
underTest.add(getJavaClass(crl, customResourceName));
underTest.add(getJavaClass(crl, customResourceName + "Spec"));
underTest.add(getJavaClass(crl, customResourceName + "Status"));

Approvals.verifyAll("AkkaMicroserviceJavaCr", underTest);
Approvals.verifyAll(approvalLabel, underTest);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
CrontabJavaExtraAnnotationsCr[0] = package org.test.v1;

@io.fabric8.kubernetes.model.annotation.Version(value = "v1" , storage = true , served = true)
@io.fabric8.kubernetes.model.annotation.Group("stable.example.com")
@lombok.ToString()
@lombok.EqualsAndHashCode(callSuper = true)
@lombok.Setter()
@lombok.experimental.Accessors(prefix = {
"_",
""
})
@io.sundr.builder.annotations.Buildable(editableEnabled = false, validationEnabled = false, generateBuilderPackage = false, builderPackage = "io.fabric8.kubernetes.api.builder", refs = {
@io.sundr.builder.annotations.BuildableReference(io.fabric8.kubernetes.api.model.ObjectMeta.class),
@io.sundr.builder.annotations.BuildableReference(io.fabric8.kubernetes.api.model.ObjectReference.class),
@io.sundr.builder.annotations.BuildableReference(io.fabric8.kubernetes.api.model.LabelSelector.class),
@io.sundr.builder.annotations.BuildableReference(io.fabric8.kubernetes.api.model.Container.class),
@io.sundr.builder.annotations.BuildableReference(io.fabric8.kubernetes.api.model.EnvVar.class),
@io.sundr.builder.annotations.BuildableReference(io.fabric8.kubernetes.api.model.ContainerPort.class),
@io.sundr.builder.annotations.BuildableReference(io.fabric8.kubernetes.api.model.Volume.class),
@io.sundr.builder.annotations.BuildableReference(io.fabric8.kubernetes.api.model.VolumeMount.class)
})
public class CronTab extends io.fabric8.kubernetes.client.CustomResource<org.test.v1.CronTabSpec, org.test.v1.CronTabStatus> implements io.fabric8.kubernetes.api.model.Namespaced {
}

CrontabJavaExtraAnnotationsCr[1] = package org.test.v1;

@com.fasterxml.jackson.annotation.JsonInclude(com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL)
@com.fasterxml.jackson.annotation.JsonPropertyOrder({"cronSpec","image","replicas"})
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = com.fasterxml.jackson.databind.JsonDeserializer.None.class)
@lombok.ToString()
@lombok.EqualsAndHashCode()
@lombok.Setter()
@lombok.experimental.Accessors(prefix = {
"_",
""
})
@io.sundr.builder.annotations.Buildable(editableEnabled = false, validationEnabled = false, generateBuilderPackage = false, builderPackage = "io.fabric8.kubernetes.api.builder", refs = {
@io.sundr.builder.annotations.BuildableReference(io.fabric8.kubernetes.api.model.ObjectMeta.class),
@io.sundr.builder.annotations.BuildableReference(io.fabric8.kubernetes.api.model.ObjectReference.class),
@io.sundr.builder.annotations.BuildableReference(io.fabric8.kubernetes.api.model.LabelSelector.class),
@io.sundr.builder.annotations.BuildableReference(io.fabric8.kubernetes.api.model.Container.class),
@io.sundr.builder.annotations.BuildableReference(io.fabric8.kubernetes.api.model.EnvVar.class),
@io.sundr.builder.annotations.BuildableReference(io.fabric8.kubernetes.api.model.ContainerPort.class),
@io.sundr.builder.annotations.BuildableReference(io.fabric8.kubernetes.api.model.Volume.class),
@io.sundr.builder.annotations.BuildableReference(io.fabric8.kubernetes.api.model.VolumeMount.class)
})
public class CronTabSpec implements io.fabric8.kubernetes.api.model.KubernetesResource {

@com.fasterxml.jackson.annotation.JsonProperty("cronSpec")
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SKIP)
private String cronSpec;

public String getCronSpec() {
return cronSpec;
}

public void setCronSpec(String cronSpec) {
this.cronSpec = cronSpec;
}

@com.fasterxml.jackson.annotation.JsonProperty("image")
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SKIP)
private String image;

public String getImage() {
return image;
}

public void setImage(String image) {
this.image = image;
}

@com.fasterxml.jackson.annotation.JsonProperty("replicas")
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SKIP)
private Long replicas;

public Long getReplicas() {
return replicas;
}

public void setReplicas(Long replicas) {
this.replicas = replicas;
}
}

CrontabJavaExtraAnnotationsCr[2] = package org.test.v1;

@com.fasterxml.jackson.annotation.JsonInclude(com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL)
@com.fasterxml.jackson.annotation.JsonPropertyOrder({"labelSelector","replicas"})
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = com.fasterxml.jackson.databind.JsonDeserializer.None.class)
@lombok.ToString()
@lombok.EqualsAndHashCode()
@lombok.Setter()
@lombok.experimental.Accessors(prefix = {
"_",
""
})
@io.sundr.builder.annotations.Buildable(editableEnabled = false, validationEnabled = false, generateBuilderPackage = false, builderPackage = "io.fabric8.kubernetes.api.builder", refs = {
@io.sundr.builder.annotations.BuildableReference(io.fabric8.kubernetes.api.model.ObjectMeta.class),
@io.sundr.builder.annotations.BuildableReference(io.fabric8.kubernetes.api.model.ObjectReference.class),
@io.sundr.builder.annotations.BuildableReference(io.fabric8.kubernetes.api.model.LabelSelector.class),
@io.sundr.builder.annotations.BuildableReference(io.fabric8.kubernetes.api.model.Container.class),
@io.sundr.builder.annotations.BuildableReference(io.fabric8.kubernetes.api.model.EnvVar.class),
@io.sundr.builder.annotations.BuildableReference(io.fabric8.kubernetes.api.model.ContainerPort.class),
@io.sundr.builder.annotations.BuildableReference(io.fabric8.kubernetes.api.model.Volume.class),
@io.sundr.builder.annotations.BuildableReference(io.fabric8.kubernetes.api.model.VolumeMount.class)
})
public class CronTabStatus implements io.fabric8.kubernetes.api.model.KubernetesResource {

@com.fasterxml.jackson.annotation.JsonProperty("labelSelector")
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SKIP)
private String labelSelector;

public String getLabelSelector() {
return labelSelector;
}

public void setLabelSelector(String labelSelector) {
this.labelSelector = labelSelector;
}

@com.fasterxml.jackson.annotation.JsonProperty("replicas")
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SKIP)
private Long replicas;

public Long getReplicas() {
return replicas;
}

public void setReplicas(Long replicas) {
this.replicas = replicas;
}
}

0 comments on commit c0f7960

Please sign in to comment.