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

Add crd-generator and java-generator support for Format annotation #5885

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
* Fix #5867: (crd-generator) Imply schemaFrom via JsonFormat shape (SchemaFrom takes precedence)
* Fix #5867: (java-generator) Add JsonFormat shape to date-time
* Fix #5954: (crd-generator) Sort required properties to ensure deterministic output
* Fix #5867: (crd-generator) Add support to define `format` from `@Format` annotation
* Fix #5867: (java-generator) Add support to define `@Format` annotation from `format`

#### Dependency Upgrade

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ public abstract class AbstractJsonSchema<T, B> {
public static final String ANNOTATION_DEFAULT = "io.fabric8.generator.annotation.Default";
public static final String ANNOTATION_MIN = "io.fabric8.generator.annotation.Min";
public static final String ANNOTATION_MAX = "io.fabric8.generator.annotation.Max";
public static final String ANNOTATION_FORMAT = "io.fabric8.generator.annotation.Format";
public static final String ANNOTATION_PATTERN = "io.fabric8.generator.annotation.Pattern";
public static final String ANNOTATION_NULLABLE = "io.fabric8.generator.annotation.Nullable";
public static final String ANNOTATION_REQUIRED = "io.fabric8.generator.annotation.Required";
Expand Down Expand Up @@ -177,6 +178,7 @@ protected static class SchemaPropsOptions {
final String defaultValue;
final Double min;
final Double max;
final String format;
final String pattern;
final boolean nullable;
final boolean required;
Expand All @@ -187,19 +189,21 @@ protected static class SchemaPropsOptions {
defaultValue = null;
min = null;
max = null;
format = null;
pattern = null;
nullable = false;
required = false;
preserveUnknownFields = false;
validationRules = null;
}

public SchemaPropsOptions(String defaultValue, Double min, Double max, String pattern,
public SchemaPropsOptions(String defaultValue, Double min, Double max, String format, String pattern,
List<KubernetesValidationRule> validationRules,
boolean nullable, boolean required, boolean preserveUnknownFields) {
this.defaultValue = defaultValue;
this.min = min;
this.max = max;
this.format = format;
this.pattern = pattern;
this.nullable = nullable;
this.required = required;
Expand All @@ -219,6 +223,10 @@ public Optional<Double> getMax() {
return Optional.ofNullable(max);
}

public Optional<String> getFormat() {
return Optional.ofNullable(format);
}

public Optional<String> getPattern() {
return Optional.ofNullable(pattern);
}
Expand Down Expand Up @@ -383,6 +391,7 @@ private T internalFromImpl(TypeDef definition, LinkedHashMap<String, String> vis
facade.defaultValue,
facade.min,
facade.max,
facade.format,
facade.pattern,
facade.validationRules,
facade.nullable,
Expand Down Expand Up @@ -425,6 +434,7 @@ private static class PropertyOrAccessor {
private String defaultValue;
private Double min;
private Double max;
private String format;
private String pattern;
private List<KubernetesValidationRule> validationRules;
private boolean nullable;
Expand Down Expand Up @@ -464,6 +474,9 @@ public void process() {
case ANNOTATION_MIN:
min = (Double) a.getParameters().get(VALUE);
break;
case ANNOTATION_FORMAT:
format = (String) a.getParameters().get(VALUE);
break;
case ANNOTATION_PATTERN:
pattern = (String) a.getParameters().get(VALUE);
break;
Expand Down Expand Up @@ -526,6 +539,10 @@ public Optional<Double> getMin() {
return Optional.ofNullable(min);
}

public Optional<String> getFormat() {
return Optional.ofNullable(format);
}

public Optional<String> getPattern() {
return Optional.ofNullable(pattern);
}
Expand Down Expand Up @@ -579,6 +596,7 @@ private static class PropertyFacade {
private String defaultValue;
private Double min;
private Double max;
private String format;
private String pattern;
private boolean nullable;
private boolean required;
Expand Down Expand Up @@ -611,6 +629,7 @@ public PropertyFacade(Property property, Map<String, Method> potentialAccessors,
defaultValue = null;
min = null;
max = null;
format = null;
pattern = null;
validationRules = new LinkedList<>();
}
Expand Down Expand Up @@ -641,6 +660,7 @@ public Property process() {
defaultValue = p.getDefault().orElse(defaultValue);
min = p.getMin().orElse(min);
max = p.getMax().orElse(max);
format = p.getFormat().orElse(format);
pattern = p.getPattern().orElse(pattern);
p.getValidationRules().ifPresent(rules -> validationRules.addAll(rules));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ public void addProperty(Property property, JSONSchemaPropsBuilder builder,
});
options.getMin().ifPresent(schema::setMinimum);
options.getMax().ifPresent(schema::setMaximum);
options.getFormat().ifPresent(schema::setFormat);
options.getPattern().ifPresent(schema::setPattern);

List<ValidationRule> validationRulesFromProperty = options.getValidationRules().stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ public void addProperty(Property property, JSONSchemaPropsBuilder builder,
});
options.getMin().ifPresent(schema::setMinimum);
options.getMax().ifPresent(schema::setMaximum);
options.getFormat().ifPresent(schema::setFormat);
options.getPattern().ifPresent(schema::setPattern);

List<ValidationRule> validationRulesFromProperty = options.getValidationRules().stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import io.fabric8.generator.annotation.Default;
import io.fabric8.generator.annotation.Format;
import io.fabric8.generator.annotation.Max;
import io.fabric8.generator.annotation.Min;
import io.fabric8.generator.annotation.Nullable;
Expand Down Expand Up @@ -57,6 +58,7 @@ public class AnnotatedSpec {
private String numInt;
private String numFloat;
private ZonedDateTime issuedAt;
private String password;

@JsonIgnore
private int ignoredFoo;
Expand Down Expand Up @@ -97,6 +99,11 @@ public int getMin() {
return 1;
}

@Format("password")
public String getPassword() {
return password;
}

@Pattern("\\b[1-9]\\b")
public String getSingleDigit() {
return "1";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ void shouldAugmentPropertiesSchemaFromAnnotations() throws JsonProcessingExcepti
assertNotNull(schema);
Map<String, JSONSchemaProps> properties = assertSchemaHasNumberOfProperties(schema, 2);
final JSONSchemaProps specSchema = properties.get("spec");
Map<String, JSONSchemaProps> spec = assertSchemaHasNumberOfProperties(specSchema, 20);
Map<String, JSONSchemaProps> spec = assertSchemaHasNumberOfProperties(specSchema, 21);

// check descriptions are present
assertTrue(spec.containsKey("from-field"));
Expand All @@ -125,6 +125,7 @@ void shouldAugmentPropertiesSchemaFromAnnotations() throws JsonProcessingExcepti
Function<String, JSONSchemaPropsBuilder> type = t -> new JSONSchemaPropsBuilder().withType(t);
assertEquals(type.apply("integer").withMinimum(-5.0).build(), spec.get("min"));
assertEquals(type.apply("integer").withMaximum(5.0).build(), spec.get("max"));
assertEquals(type.apply("string").withFormat("password").build(), spec.get("password"));
assertEquals(type.apply("string").withPattern("\\b[1-9]\\b").build(), spec.get("singleDigit"));
assertEquals(type.apply("string").withNullable(true).build(), spec.get("nullable"));
assertEquals(type.apply("string").withDefault(TextNode.valueOf("my-value")).build(), spec.get("defaultValue"));
Expand Down
24 changes: 24 additions & 0 deletions doc/CRD-generator.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,30 @@ The field will have the `maximum` property in the generated CRD, such as:
type: object
```

### io.fabric8.generator.annotation.Format

If a field or one of its accessors is annotated with `io.fabric8.generator.annotation.Format`

```java
public class ExampleSpec {
@Format("password")
String someValue;
}
```

The field will have the `format` property in the generated CRD, such as:

```yaml
spec:
properties:
someValue:
format: password
type: string
required:
- someValue
type: object
```

### io.fabric8.generator.annotation.Pattern

If a field or one of its accessors is annotated with `io.fabric8.generator.annotation.Pattern`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* 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.generator.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Java representation of the {@code format} field of JSONSchemaProps.
matteriben marked this conversation as resolved.
Show resolved Hide resolved
*
* <p>
* The following formats are validated by Kubernetes:
* </p>
* <ul>
* <li>{@code bsonobjectid}: a bson object ID, i.e. a 24 characters hex string</li>
* <li>{@code uri}: an URI as parsed by Golang net/url.ParseRequestURI</li>
* <li>{@code email}: an email address as parsed by Golang net/mail.ParseAddress</li>
* <li>{@code hostname}: a valid representation for an Internet host name, as defined by RFC 1034, section 3.1 [RFC1034].</li>
* <li>{@code ipv4}: an IPv4 IP as parsed by Golang net.ParseIP</li>
* <li>{@code ipv6}: an IPv6 IP as parsed by Golang net.ParseIP</li>
* <li>{@code cidr}: a CIDR as parsed by Golang net.ParseCIDR</li>
* <li>{@code mac}: a MAC address as parsed by Golang net.ParseMAC</li>
* <li>{@code uuid}: an UUID that allows uppercase defined by the regex
* {@code (?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$}</li>
* <li>{@code uuid3}: an UUID3 that allows uppercase defined by the regex
* {@code (?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?3[0-9a-f]{3}-?[0-9a-f]{4}-?[0-9a-f]{12}$}</li>
* <li>{@code uuid4}: an UUID4 that allows uppercase defined by the regex
* {@code (?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?4[0-9a-f]{3}-?[89ab][0-9a-f]{3}-?[0-9a-f]{12}$}</li>
* <li>{@code uuid5}: an UUID5 that allows uppercase defined by the regex
* {@code (?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?5[0-9a-f]{3}-?[89ab][0-9a-f]{3}-?[0-9a-f]{12}$}</li>
* <li>{@code isbn}: an ISBN10 or ISBN13 number string like "0321751043" or "978-0321751041"</li>
* <li>{@code isbn10}: an ISBN10 number string like "0321751043"</li>
* <li>{@code isbn13}: an ISBN13 number string like "978-0321751041"</li>
* <li>{@code creditcard}: a credit card number defined by the regex
* {@code ^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$}
* with any non digit characters mixed in</li>
* <li>{@code ssn}: a U.S. social security number following the regex {@code ^\d{3}[- ]?\d{2}[- ]?\d{4}$}</li>
* <li>{@code hexcolor}: an hexadecimal color code like "#FFFFFF: following the regex
* {@code ^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$}</li>
* <li>{@code rgbcolor}: an RGB color code like rgb like "rgb(255,255,2559"</li>
* <li>{@code byte}: base64 encoded binary data</li>
* <li>{@code password}: any kind of string</li>
* <li>{@code date}: a date string like "2006-01-02" as defined by full-date in RFC3339</li>
* <li>{@code duration}: a duration string like "22 ns" as parsed by Golang time.ParseDuration or compatible with Scala duration
* format</li>
* <li>{@code date-time}: a date time string like "2014-12-15T19:30:20.000Z" as defined by date-time in RFC3339.</li>
* </ul>
* <p>
* Unknown formats are ignored by Kubernetes and if another consumer is unaware of the meaning of the format,
* they shall fall back to using the basic type without format.
* </p>
*
* @see <a href=
* "https://kubernetes.io/docs/reference/kubernetes-api/extend-resources/custom-resource-definition-v1/#JSONSchemaProps">
* Kubernetes Docs - API Reference - CRD v1 - JSONSchemaProps
* </a>
*/
@Target({ ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Format {
String value();
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public static final AnnotationExpr newGeneratedAnnotation() {

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

public Double getMaximum() {
Expand All @@ -66,6 +67,10 @@ public Double getMinimum() {
return minimum;
}

public String getFormat() {
return format;
}

public String getPattern() {
return pattern;
}
Expand Down Expand Up @@ -109,6 +114,7 @@ protected AbstractJSONSchema2Pojo(Config config, String description, final boole
if (validationProperties != null) {
this.maximum = validationProperties.getMaximum();
this.minimum = validationProperties.getMinimum();
this.format = validationProperties.getFormat();
this.pattern = validationProperties.getPattern();
}
}
Expand Down Expand Up @@ -268,6 +274,7 @@ private static AbstractJSONSchema2Pojo fromJsonSchema(
ValidationProperties.Builder.getInstance()
.withMaximum(prop.getMaximum())
.withMinimum(prop.getMinimum())
.withFormat(prop.getFormat())
.withPattern(prop.getPattern())
.build());
case ARRAY:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,12 @@ public GeneratorResult generateJava() {
new Name("io.fabric8.generator.annotation.Min"),
new DoubleLiteralExpr(prop.getMinimum())));
}
if (prop.getFormat() != null) {
objField.addAnnotation(
new SingleMemberAnnotationExpr(
new Name("io.fabric8.generator.annotation.Format"),
new StringLiteralExpr(StringEscapeUtils.escapeJava(prop.getFormat()))));
}
if (prop.getPattern() != null) {
objField.addAnnotation(
new SingleMemberAnnotationExpr(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@
public class ValidationProperties {
private final Double maximum;
private final Double minimum;
private final String format;
private final String pattern;

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

Expand All @@ -38,13 +40,18 @@ public Double getMinimum() {
return minimum;
}

public String getFormat() {
return format;
}

public String getPattern() {
return pattern;
}

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

private Builder() {
Expand All @@ -64,13 +71,18 @@ public Builder withMinimum(final Double minimum) {
return this;
}

public Builder withFormat(final String format) {
this.format = format;
return this;
}

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

public ValidationProperties build() {
return new ValidationProperties(maximum, minimum, pattern);
return new ValidationProperties(maximum, minimum, format, pattern);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,7 @@ public class AkkaMicroserviceStatus implements io.fabric8.kubernetes.api.model.K
* Total number of available pods targeted by this AkkaMicroservice.
*/
@com.fasterxml.jackson.annotation.JsonProperty("availableReplicas")
@io.fabric8.generator.annotation.Format("int32")
@com.fasterxml.jackson.annotation.JsonPropertyDescription("Total number of available pods targeted by this AkkaMicroservice.")
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SKIP)
private Integer availableReplicas;
Expand Down
Loading
Loading