Skip to content

Commit

Permalink
[java-gen] - Generating Fabric8 k8s client validation annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
Fabio Burzigotti committed Sep 14, 2022
1 parent 0742e94 commit 13bbb10
Show file tree
Hide file tree
Showing 18 changed files with 424 additions and 86 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
* Fix #4348: Introduce specific annotations for the generators
* Fix #4365: The Watch retry logic will handle more cases, as well as perform an exceptional close for events that are not properly handled. Informers can directly provide those exceptional outcomes via the SharedIndexInformer.stopped CompletableFuture.
* Fix #4396: Provide more error context when @Group/@Version annotations are missing
* Fix #4384: The Java generator now supports the generation of specific annotations (min, max, pattern, etc.), as defined by #4348

#### Dependency Upgrade
* Fix #4243: Update Tekton pipeline model to v0.39.0
Expand All @@ -21,6 +22,7 @@
#### _**Note**_: Breaking changes in the API
* Fix #4350: SchemaSwap's fieldName parameter now expects a field name only, not a method or a constructor.
* Module `io.fabric8:tekton-model-triggers` which contained Tekton triggers v1alpha1 model has been removed. We have introduced separate modules `io.fabric8:tekton-model-v1alpha1` and `io.fabric8:tekton-model-v1beta1` for Tekton triggers v1alpha1 and v1beta1 apigroups respectively. Users who are using `io.fabric8:tekton-client` dependency directly should be unaffected by this change.
* Fix #4384: javax.validation.* annotations are no longer added by the Java generator.

### 6.1.1 (2022-09-01)

Expand Down
6 changes: 2 additions & 4 deletions doc/java-generation-from-CRD.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,8 @@ The generated code depends on a few dependencies to succesfully compile:
<artifactId>kubernetes-client</artifactId>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
<scope>provided</scope>
<groupId>io.fabric8</groupId>
<artifactId>generator-annotations</artifactId>
</dependency>
```

Expand Down
5 changes: 2 additions & 3 deletions java-generator/core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,8 @@
</dependency>

<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<scope>provided</scope>
<groupId>io.fabric8</groupId>
<artifactId>generator-annotations</artifactId>
</dependency>

<!-- Testing -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,12 @@
package io.fabric8.java.generator.nodes;

import com.fasterxml.jackson.databind.JsonNode;
import com.github.javaparser.ast.expr.Expression;
import com.github.javaparser.ast.expr.NameExpr;
import com.github.javaparser.ast.stmt.BlockStmt;
import com.github.javaparser.ast.stmt.ReturnStmt;
import io.fabric8.java.generator.Config;
import io.fabric8.java.generator.exceptions.JavaGeneratorException;
import io.fabric8.kubernetes.api.model.apiextensions.v1.JSONSchemaProps;
import io.fabric8.kubernetes.client.utils.Serialization;

import java.util.Locale;
import java.util.Optional;
import java.util.function.Function;

import static io.fabric8.java.generator.nodes.Keywords.JAVA_KEYWORDS;
Expand All @@ -48,6 +44,22 @@ public abstract class AbstractJSONSchema2Pojo {
protected final boolean isNullable;
protected final JsonNode defaultValue;

protected Double maximum;
protected Double minimum;
protected String pattern;

public Double getMaximum() {
return maximum;
}

public Double getMinimum() {
return minimum;
}

public String getPattern() {
return pattern;
}

public abstract String getType();

public abstract GeneratorResult generateJava();
Expand All @@ -69,11 +81,17 @@ protected String getClassType() {
return getType();
}

protected AbstractJSONSchema2Pojo(Config config, String description, final boolean isNullable, JsonNode defaultValue) {
protected AbstractJSONSchema2Pojo(Config config, String description, final boolean isNullable, JsonNode defaultValue,
final ValidationProperties validationProperties) {
this.config = config;
this.description = description;
this.isNullable = isNullable;
this.defaultValue = defaultValue;
if (validationProperties != null) {
this.maximum = validationProperties.getMaximum();
this.minimum = validationProperties.getMinimum();
this.pattern = validationProperties.getPattern();
}
}

/** Takes a random string and manipulate it to be a valid Java identifier */
Expand Down Expand Up @@ -185,7 +203,17 @@ private static AbstractJSONSchema2Pojo fromJsonSchema(
final boolean isNullable = Boolean.TRUE.equals(prop.getNullable());
switch (nt.getType()) {
case PRIMITIVE:
return new JPrimitive(nt.getName(), config, prop.getDescription(), isNullable, prop.getDefault());
return new JPrimitive(
nt.getName(),
config,
prop.getDescription(),
isNullable,
prop.getDefault(),
ValidationProperties.Builder.getInstance()
.withMaximum(prop.getMaximum())
.withMinimum(prop.getMinimum())
.withPattern(prop.getPattern())
.build());
case ARRAY:
return new JArray(
fromJsonSchema(
Expand Down Expand Up @@ -227,7 +255,13 @@ private static AbstractJSONSchema2Pojo fromJsonSchema(
isNullable,
prop.getDefault());
case ENUM:
return new JEnum(key, prop.getEnum(), config, prop.getDescription(), isNullable, prop.getDefault());
return new JEnum(
key,
prop.getEnum(),
config,
prop.getDescription(),
isNullable,
prop.getDefault());
default:
throw new JavaGeneratorException("Unreachable " + nt.getType());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class JArray extends AbstractJSONSchema2Pojo {

public JArray(AbstractJSONSchema2Pojo nested, Config config, String description, final boolean isNullable,
JsonNode defaultValue) {
super(config, description, isNullable, defaultValue);
super(config, description, isNullable, defaultValue, null);
this.type = new ClassOrInterfaceType()
.setName(JAVA_UTIL_LIST)
.setTypeArguments(new ClassOrInterfaceType().setName(nested.getType()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public JCRObject(
boolean storage,
boolean served,
Config config) {
super(config, null, false, null);
super(config, null, false, null, null);

this.pkg = (pkg == null) ? "" : pkg.trim();
this.type = (this.pkg.isEmpty()) ? type : pkg + "." + type;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public class JEnum extends AbstractJSONSchema2Pojo {

public JEnum(String type, List<JsonNode> values, Config config, String description, final boolean isNullable,
JsonNode defaultValue) {
super(config, description, isNullable, defaultValue);
super(config, description, isNullable, defaultValue, null);
this.type = AbstractJSONSchema2Pojo.sanitizeString(
type.substring(0, 1).toUpperCase() + type.substring(1));
this.values = values.stream().map(JsonNode::asText).collect(Collectors.toList());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public class JMap extends AbstractJSONSchema2Pojo {

public JMap(AbstractJSONSchema2Pojo nested, Config config, String description, final boolean isNullable,
JsonNode defaultValue) {
super(config, description, isNullable, defaultValue);
super(config, description, isNullable, defaultValue, null);
this.type = new ClassOrInterfaceType()
.setName(JAVA_UTIL_MAP)
.setTypeArguments(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
import io.fabric8.kubernetes.client.utils.Serialization;
import io.fabric8.kubernetes.client.utils.Utils;

import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -66,7 +65,7 @@ public JObject(
Config config,
String description,
final boolean isNullable, JsonNode defaultValue) {
super(config, description, isNullable, defaultValue);
super(config, description, isNullable, defaultValue, null);
this.required = new HashSet<>(Optional.ofNullable(required).orElse(Collections.emptyList()));
this.fields = new HashMap<>();
this.preserveUnknownFields = preserveUnknownFields;
Expand Down Expand Up @@ -197,7 +196,25 @@ public GeneratorResult generateJava() {
new StringLiteralExpr(originalFieldName)));

if (isRequired) {
objField.addAnnotation("javax.validation.constraints.NotNull");
objField.addAnnotation("io.fabric8.generator.annotation.Required");
}
if (prop.getMaximum() != null) {
objField.addAnnotation(
new SingleMemberAnnotationExpr(
new Name("io.fabric8.generator.annotation.Max"),
new DoubleLiteralExpr(prop.getMaximum())));
}
if (prop.getMinimum() != null) {
objField.addAnnotation(
new SingleMemberAnnotationExpr(
new Name("io.fabric8.generator.annotation.Min"),
new DoubleLiteralExpr(prop.getMinimum())));
}
if (prop.getPattern() != null) {
objField.addAnnotation(
new SingleMemberAnnotationExpr(
new Name("io.fabric8.generator.annotation.Pattern"),
new StringLiteralExpr(StringEscapeUtils.escapeJava(prop.getPattern()))));
}

objField.createGetter();
Expand Down Expand Up @@ -229,6 +246,7 @@ public GeneratorResult generateJava() {
new SingleMemberAnnotationExpr(
new Name("com.fasterxml.jackson.annotation.JsonSetter"),
new NameExpr("nulls = com.fasterxml.jackson.annotation.Nulls.SET")));
objField.addAnnotation("io.fabric8.generator.annotation.Nullable");
}

if (prop.getDefaultValue() != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ public class JPrimitive extends AbstractJSONSchema2Pojo {

private static final GeneratorResult empty = new GeneratorResult(new ArrayList<>(), new ArrayList<>());

public JPrimitive(String type, Config config, String description, final boolean isNullable, JsonNode defaultValue) {
super(config, description, isNullable, defaultValue);
public JPrimitive(String type, Config config, String description, final boolean isNullable, JsonNode defaultValue,
final ValidationProperties validationProperties) {
super(config, description, isNullable, defaultValue, validationProperties);
this.type = type;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/**
* Copyright (C) 2015 Red Hat, Inc.
*
* 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 io.fabric8.java.generator.nodes;

/**
* This class instances store the values for a generic JSON schema validation related properties, like `minimum`,
* `maximum`, etc.
*/
public class ValidationProperties {
private final Double maximum;
private final Double minimum;
private final String pattern;

private ValidationProperties(final Double maximum, final Double minimum, final String pattern) {
this.maximum = maximum;
this.minimum = minimum;
this.pattern = pattern;
}

public Double getMaximum() {
return maximum;
}

public Double getMinimum() {
return minimum;
}

public String getPattern() {
return pattern;
}

public static final class Builder {
private Double maximum;
private Double minimum;
private String pattern;

private Builder() {
}

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

public Builder withMaximum(final Double maximum) {
this.maximum = maximum;
return this;
}

public Builder withMinimum(final Double minimum) {
this.minimum = minimum;
return this;
}

public Builder withPattern(final String pattern) {
this.pattern = pattern;
return this;
}

public ValidationProperties build() {
return new ValidationProperties(maximum, minimum, pattern);
}
}
}

0 comments on commit 13bbb10

Please sign in to comment.