Skip to content

Commit

Permalink
Allow to deserialize more valid RFC3339 date-time and make the format…
Browse files Browse the repository at this point in the history
… customizable

(cherry picked from commit 07ddb89)
Signed-off-by: Marc Nuri <[email protected]>
  • Loading branch information
andreaTP authored and manusa committed Aug 14, 2023
1 parent 6b4c2d3 commit efef316
Show file tree
Hide file tree
Showing 27 changed files with 638 additions and 68 deletions.
9 changes: 6 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
## CHANGELOG

### 6.8.1 (2023-08-14)

#### Bugs
* Fix #5382: [java-generator] Allow to deserialize more valid RFC3339 date-time and make the format customizable

### 6.8.0 (2023-07-24)

#### Bugs
Expand Down Expand Up @@ -28,15 +33,13 @@
#### New Features
* Fix #5133: Support for using TokenRequest for existing ServiceAccount

#### Deprecations
* Deprecating `io.fabric8.kubernetes.model.annotation.PrinterColumn` in favor of: `io.fabric8.crd.generator.annotation.PrinterColumn`

#### _**Note**_: Breaking changes
* Fix #2718: KubernetesResourceUtil.isResourceReady was deprecated. Use `client.resource(item).isReady()` or `Readiness.getInstance().isReady(item)` instead.
* Fix #5171: Removed Camel-K extension, use [`org.apache.camel.k:camel-k-crds`](https://central.sonatype.com/artifact/org.apache.camel.k/camel-k-crds) instead.
* Fix #5262: Built-in resources were in-consistent with respect to their serialization or empty collections. In many circumstances this was confusing behavior. In order to be consistent all built-in resources will omit empty collections by default. This is a breaking change if you are relying on an empty collection in a json merge or a strategic merge where the list has a patchStrategy of atomic. In these circumstances the empty collection will no longer be serialized. You may instead use a json patch, server side apply instead, or modify the serialized form of the patch.
* Fix #5279: (java-generator) Add native support for `date-time` fields, they are now mapped to native `java.time.ZonedDateTime`
* Fix #5315: kubernetes-junit-jupiter no longer registers the NamespaceExtension and KubernetesExtension extensions to be used in combination with junit-platform.properties>`junit.jupiter.extensions.autodetection.enabled=true`configuration. If you wish to use these extensions and autodetect them, change your dependency to `kubernetes-junit-jupiter-autodetect`.
* Deprecating `io.fabric8.kubernetes.model.annotation.PrinterColumn` in favor of: `io.fabric8.crd.generator.annotation.PrinterColumn`
* Resource classes in `resource.k8s.io/v1alpha1` have been moved to `resource.k8s.io/v1alpha2` apiGroup in Kubernetes 1.27. Users are required to change package of the following classes:
- `io.fabric8.kubernetes.api.model.resource.v1alpha1.PodSchedulingContext` -> - `io.fabric8.kubernetes.api.model.resource.v1alpha2.PodSchedulingContext`
- `io.fabric8.kubernetes.api.model.resource.v1alpha1.ResourceClaim` -> - `io.fabric8.kubernetes.api.model.resource.v1alpha2.ResourceClaim`
Expand Down
21 changes: 19 additions & 2 deletions doc/java-generation-from-CRD.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,18 @@ The full list of options of the CLI is (output of `--help`):

```
Usage: java-gen [-hV] [-add-extra-annotations] [-enum-uppercase]
[-dt=<downloadTarget>] [-s=<source>]
[-suffix-strategy=<suffixStrategy>] -t=<target>
[-deserialization-datetime-format=<deserializationDateTimeFormat
>] [-dt=<downloadTarget>] [-s=<source>]
[-serialization-datetime-format=<serializationDateTimeFormat>]
-t=<target> [-files-suffixes=<filesSuffixes>]...
[-package-overrides=<String=String>]... [-u=<urls>]...
-add-extra-annotations, --add-extra-annotations
Add extra lombok and sundrio annotation to the
generated classes
-deserialization-datetime-format,
--deserialization-datetime-format=<deserializationDateTimeFormat>
DateTime format used for Deserialization of fields of
type `date-time`
-dt, --download-target=<downloadTarget>
The folder to be used as a target for the downloaded
crds
Expand All @@ -98,6 +104,10 @@ Usage: java-gen [-hV] [-add-extra-annotations] [-enum-uppercase]
Apply the overrides to the package names
-s, --source=<source> The source(file or folder) with the
CustomResourceDefinition(s) to use
-serialization-datetime-format,
--serialization-datetime-format=<serializationDateTimeFormat>
DateTime format used for Serialization of fields of
type `date-time`
-t, --target=<target> The folder to write the generated sources
-u, --urls=<urls> The source urls with the CustomResourceDefinition(s)
to use
Expand All @@ -107,6 +117,13 @@ Usage: java-gen [-hV] [-add-extra-annotations] [-enum-uppercase]
And the corresponding configurations of the Maven plugin are (output of `mvn help:describe -DgroupId=io.fabric8 -DartifactId=java-generator-maven-plugin -Dversion=<version> -Ddetail`):

```
datetimeDeserializationFormat
User property: fabric8.java-generator.datetime-deserialization-format
DateTime format used for Deserialization of fields of type `date-time`
datetimeSerializationFormat
User property: fabric8.java-generator.datetime-serialization-format
DateTime format used for Serialization of fields of type `date-time`
downloadTarget (Default: ${basedir}/target/manifests)
User property: fabric8.java-generator.download-target
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ public class GenerateJavaSources implements Runnable {
"--files-suffixes" }, description = "Filter the source files with the specific suffixes", required = false)
List<String> filesSuffixes = null;

@Option(names = { "-serialization-datetime-format",
"--serialization-datetime-format" }, description = "DateTime format used for Serialization of fields of type `date-time`", required = false)
String serializationDateTimeFormat = null;

@Option(names = { "-deserialization-datetime-format",
"--deserialization-datetime-format" }, description = "DateTime format used for Deserialization of fields of type `date-time`", required = false)
String deserializationDateTimeFormat = null;

@Override
public void run() {
final Boolean noGeneratedAnnotations = (skipGeneratedAnnotations != null) ? skipGeneratedAnnotations : false;
Expand All @@ -79,7 +87,9 @@ public void run() {
addExtraAnnotations,
!noGeneratedAnnotations,
packageOverrides,
filesSuffixes);
filesSuffixes,
serializationDateTimeFormat,
deserializationDateTimeFormat);

List<JavaGenerator> runners = new ArrayList<>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,22 @@
@Builder(toBuilder = true)
@NoArgsConstructor
public class Config {
private static final boolean DEFAULT_UPPERCASE_ENUM = true;
private static final boolean DEFAULT_ADD_EXTRA_ANNOTATIONS = false;
private static final boolean DEFAULT_ADD_GENERATED_ANNOTATIONS = true;
private static final Map<String, String> DEFAULT_PACKAGE_OVERRIDES = new HashMap<>();
private static final List<String> DEFAULT_FILES_SUFFIXES = Arrays.asList(".yaml", ".yml", ".json");
public static final boolean DEFAULT_UPPERCASE_ENUM = true;
public static final boolean DEFAULT_ADD_EXTRA_ANNOTATIONS = false;
public static final boolean DEFAULT_ADD_GENERATED_ANNOTATIONS = true;
public static final Map<String, String> DEFAULT_PACKAGE_OVERRIDES = new HashMap<>();
public static final List<String> DEFAULT_FILES_SUFFIXES = Arrays.asList(".yaml", ".yml", ".json");
// RFC 3339 - from: https://swagger.io/docs/specification/data-models/data-types/
public static final String DEFAULT_SER_DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ssVV";
public static final String DEFAULT_DESER_DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss[XXX][VV]";

private Boolean uppercaseEnums = DEFAULT_UPPERCASE_ENUM;
private Boolean objectExtraAnnotations = DEFAULT_ADD_EXTRA_ANNOTATIONS;
private Boolean generatedAnnotations = DEFAULT_ADD_GENERATED_ANNOTATIONS;
private Map<String, String> packageOverrides = DEFAULT_PACKAGE_OVERRIDES;
private List<String> filesSuffixes = DEFAULT_FILES_SUFFIXES;
private String serDatetimeFormat = DEFAULT_SER_DATETIME_FORMAT;
private String deserDatetimeFormat = DEFAULT_DESER_DATETIME_FORMAT;

public Config(
Boolean uppercaseEnums,
Expand Down Expand Up @@ -80,6 +85,37 @@ public Config(
}
}

public Config(
Boolean uppercaseEnums,
Boolean objectExtraAnnotations,
Boolean generatedAnnotations,
Map<String, String> packageOverrides,
List<String> filesSuffixes,
String serDatetimeFormat,
String deserDatetimeFormat) {
if (uppercaseEnums != null) {
this.uppercaseEnums = uppercaseEnums;
}
if (objectExtraAnnotations != null) {
this.objectExtraAnnotations = objectExtraAnnotations;
}
if (generatedAnnotations != null) {
this.generatedAnnotations = generatedAnnotations;
}
if (packageOverrides != null) {
this.packageOverrides = packageOverrides;
}
if (filesSuffixes != null) {
this.filesSuffixes = filesSuffixes;
}
if (serDatetimeFormat != null) {
this.serDatetimeFormat = serDatetimeFormat;
}
if (deserDatetimeFormat != null) {
this.deserDatetimeFormat = deserDatetimeFormat;
}
}

public boolean isUppercaseEnums() {
return (uppercaseEnums == null) ? DEFAULT_UPPERCASE_ENUM : uppercaseEnums;
}
Expand Down Expand Up @@ -107,4 +143,16 @@ public List<String> getFilesSuffixes() {
? DEFAULT_FILES_SUFFIXES
: filesSuffixes;
}

public String getSerDatetimeFormat() {
return (serDatetimeFormat == null || serDatetimeFormat.isEmpty())
? DEFAULT_SER_DATETIME_FORMAT
: serDatetimeFormat;
}

public String getDeserDatetimeFormat() {
return (deserDatetimeFormat == null || deserDatetimeFormat.isEmpty())
? DEFAULT_DESER_DATETIME_FORMAT
: deserDatetimeFormat;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,6 @@ public static final AnnotationExpr newGeneratedAnnotation() {
new StringLiteralExpr("io.fabric8.java.generator.CRGeneratorRunner"));
}

// RFC 3339 - from: https://swagger.io/docs/specification/data-models/data-types/
public static final String DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ssX";

protected final String description;
protected final Config config;
protected final boolean isNullable;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,10 +217,16 @@ public GeneratorResult generateJava() {
new Name("com.fasterxml.jackson.annotation.JsonProperty"),
new StringLiteralExpr(originalFieldName)));

MethodDeclaration fieldGetter = objField.createGetter();
MethodDeclaration fieldSetter = objField.createSetter();

if (prop.getClassType().equals(DATETIME_NAME)) {
objField.addAnnotation(new SingleMemberAnnotationExpr(
fieldGetter.addAnnotation(new SingleMemberAnnotationExpr(
new Name("com.fasterxml.jackson.annotation.JsonFormat"),
new NameExpr("pattern = \"" + config.getSerDatetimeFormat() + "\"")));
fieldSetter.addAnnotation(new SingleMemberAnnotationExpr(
new Name("com.fasterxml.jackson.annotation.JsonFormat"),
new NameExpr("timezone = \"UTC\", pattern = \"" + DATETIME_FORMAT + "\"")));
new NameExpr("pattern = \"" + config.getDeserDatetimeFormat() + "\"")));
}

if (isRequired) {
Expand All @@ -245,9 +251,6 @@ public GeneratorResult generateJava() {
new StringLiteralExpr(StringEscapeUtils.escapeJava(prop.getPattern()))));
}

objField.createGetter();
objField.createSetter();

if (Utils.isNotNullOrEmpty(prop.getDescription())) {
objField.setJavadocComment(prop.getDescription().replace("*/", "&#042;&#047;"));

Expand Down Expand Up @@ -357,7 +360,7 @@ private Expression generatePrimitiveDefaultInitializerExpression(AbstractJSONSch
return new BooleanLiteralExpr(prop.getDefaultValue().booleanValue());
} else if (prop.getClassType().equals(DATETIME_NAME) && prop.getDefaultValue().isTextual()) {
return new NameExpr(DATETIME_NAME + ".parse(" + prop.getDefaultValue()
+ ", java.time.format.DateTimeFormatter.ofPattern(\"" + DATETIME_FORMAT + "\"))");
+ ", java.time.format.DateTimeFormatter.ofPattern(\"" + config.getDeserDatetimeFormat() + "\"))");
} else {
return new NameExpr(value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,39 +17,46 @@

import org.junit.jupiter.api.Test;

import java.util.HashMap;

import static org.assertj.core.api.Assertions.assertThat;

class ConfigTest {

@Test
void defaultValuesWithAllArgsConstructor() {
final Config result = new Config(null, null, null, null);
final Config result = new Config(null, null, null, null, null, null, null);
assertThat(result)
.returns(true, Config::isUppercaseEnums)
.returns(false, Config::isObjectExtraAnnotations)
.returns(true, Config::isGeneratedAnnotations)
.returns(new HashMap<>(), Config::getPackageOverrides);
.returns(Config.DEFAULT_UPPERCASE_ENUM, Config::isUppercaseEnums)
.returns(Config.DEFAULT_ADD_EXTRA_ANNOTATIONS, Config::isObjectExtraAnnotations)
.returns(Config.DEFAULT_ADD_GENERATED_ANNOTATIONS, Config::isGeneratedAnnotations)
.returns(Config.DEFAULT_PACKAGE_OVERRIDES, Config::getPackageOverrides)
.returns(Config.DEFAULT_FILES_SUFFIXES, Config::getFilesSuffixes)
.returns(Config.DEFAULT_SER_DATETIME_FORMAT, Config::getSerDatetimeFormat)
.returns(Config.DEFAULT_DESER_DATETIME_FORMAT, Config::getDeserDatetimeFormat);
}

@Test
void defaultValuesWithNoArgsConstructor() {
final Config result = new Config();
assertThat(result)
.returns(true, Config::isUppercaseEnums)
.returns(false, Config::isObjectExtraAnnotations)
.returns(true, Config::isGeneratedAnnotations)
.returns(new HashMap<>(), Config::getPackageOverrides);
.returns(Config.DEFAULT_UPPERCASE_ENUM, Config::isUppercaseEnums)
.returns(Config.DEFAULT_ADD_EXTRA_ANNOTATIONS, Config::isObjectExtraAnnotations)
.returns(Config.DEFAULT_ADD_GENERATED_ANNOTATIONS, Config::isGeneratedAnnotations)
.returns(Config.DEFAULT_PACKAGE_OVERRIDES, Config::getPackageOverrides)
.returns(Config.DEFAULT_FILES_SUFFIXES, Config::getFilesSuffixes)
.returns(Config.DEFAULT_SER_DATETIME_FORMAT, Config::getSerDatetimeFormat)
.returns(Config.DEFAULT_DESER_DATETIME_FORMAT, Config::getDeserDatetimeFormat);
}

@Test
void defaultValuesWithBuilder() {
final Config result = Config.builder().build();
assertThat(result)
.returns(true, Config::isUppercaseEnums)
.returns(false, Config::isObjectExtraAnnotations)
.returns(true, Config::isGeneratedAnnotations)
.returns(new HashMap<>(), Config::getPackageOverrides);
.returns(Config.DEFAULT_UPPERCASE_ENUM, Config::isUppercaseEnums)
.returns(Config.DEFAULT_ADD_EXTRA_ANNOTATIONS, Config::isObjectExtraAnnotations)
.returns(Config.DEFAULT_ADD_GENERATED_ANNOTATIONS, Config::isGeneratedAnnotations)
.returns(Config.DEFAULT_PACKAGE_OVERRIDES, Config::getPackageOverrides)
.returns(Config.DEFAULT_FILES_SUFFIXES, Config::getFilesSuffixes)
.returns(Config.DEFAULT_SER_DATETIME_FORMAT, Config::getSerDatetimeFormat)
.returns(Config.DEFAULT_DESER_DATETIME_FORMAT, Config::getDeserDatetimeFormat);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,15 @@ public class CronTabSpec implements io.fabric8.kubernetes.api.model.KubernetesRe
}

@com.fasterxml.jackson.annotation.JsonProperty("issuedAt")
@com.fasterxml.jackson.annotation.JsonFormat(timezone = "UTC", pattern = "yyyy-MM-dd'T'HH:mm:ssX")
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SKIP)
private java.time.ZonedDateTime issuedAt;

@com.fasterxml.jackson.annotation.JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssVV")
public java.time.ZonedDateTime getIssuedAt() {
return issuedAt;
}

@com.fasterxml.jackson.annotation.JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss[XXX][VV]")
public void setIssuedAt(java.time.ZonedDateTime issuedAt) {
this.issuedAt = issuedAt;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,15 @@ public class CronTabSpec implements io.fabric8.kubernetes.api.model.KubernetesRe
}

@com.fasterxml.jackson.annotation.JsonProperty("issuedAt")
@com.fasterxml.jackson.annotation.JsonFormat(timezone = "UTC", pattern = "yyyy-MM-dd'T'HH:mm:ssX")
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SKIP)
private java.time.ZonedDateTime issuedAt;

@com.fasterxml.jackson.annotation.JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssVV")
public java.time.ZonedDateTime getIssuedAt() {
return issuedAt;
}

@com.fasterxml.jackson.annotation.JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss[XXX][VV]")
public void setIssuedAt(java.time.ZonedDateTime issuedAt) {
this.issuedAt = issuedAt;
}
Expand Down
Loading

0 comments on commit efef316

Please sign in to comment.