Skip to content

Commit

Permalink
Introduce specific annotations for the generators (fabric8io#4348)
Browse files Browse the repository at this point in the history
* Introduce specific annotations for the generators

* add javadocs
  • Loading branch information
andreaTP authored and euberseder-hubspot committed Dec 21, 2023
1 parent b3f7a81 commit 93f7454
Show file tree
Hide file tree
Showing 16 changed files with 552 additions and 59 deletions.
5 changes: 5 additions & 0 deletions crd-generator/api/pom.xml
Expand Up @@ -36,6 +36,11 @@
<scope>compile</scope>
</dependency>

<dependency>
<groupId>io.fabric8</groupId>
<artifactId>generator-annotations</artifactId>
</dependency>

<dependency>
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-model-common</artifactId>
Expand Down
Expand Up @@ -17,7 +17,6 @@

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import io.fabric8.crd.generator.annotation.SchemaSwap;
import io.fabric8.crd.generator.utils.Types;
import io.fabric8.kubernetes.api.model.Duration;
import io.fabric8.kubernetes.api.model.IntOrString;
Expand Down Expand Up @@ -65,6 +64,8 @@ public abstract class AbstractJsonSchema<T, B> {
protected static final TypeDef DATE = TypeDef.forName(Date.class.getName());
protected static final TypeRef DATE_REF = DATE.toReference();

private static final String VALUE = "value";

private static final String INT_OR_STRING_MARKER = "int_or_string";
private static final String STRING_MARKER = "string";
private static final String INTEGER_MARKER = "integer";
Expand All @@ -83,6 +84,11 @@ public abstract class AbstractJsonSchema<T, B> {
public static final String ANNOTATION_JSON_IGNORE = "com.fasterxml.jackson.annotation.JsonIgnore";
public static final String ANNOTATION_JSON_ANY_GETTER = "com.fasterxml.jackson.annotation.JsonAnyGetter";
public static final String ANNOTATION_JSON_ANY_SETTER = "com.fasterxml.jackson.annotation.JsonAnySetter";
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_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";
public static final String ANNOTATION_NOT_NULL = "javax.validation.constraints.NotNull";
public static final String ANNOTATION_SCHEMA_FROM = "io.fabric8.crd.generator.annotation.SchemaFrom";
public static final String ANNOTATION_SCHEMA_SWAP = "io.fabric8.crd.generator.annotation.SchemaSwap";
Expand Down Expand Up @@ -115,6 +121,51 @@ public static String getSchemaTypeFor(TypeRef typeRef) {
return type;
}

protected static class SchemaPropsOptions {
final Optional<Double> min;
final Optional<Double> max;
final Optional<String> pattern;
final boolean nullable;
final boolean required;

SchemaPropsOptions() {
min = Optional.empty();
max = Optional.empty();
pattern = Optional.empty();
nullable = false;
required = false;
}

public SchemaPropsOptions(Optional<Double> min, Optional<Double> max, Optional<String> pattern,
boolean nullable, boolean required) {
this.min = min;
this.max = max;
this.pattern = pattern;
this.nullable = nullable;
this.required = required;
}

public Optional<Double> getMin() {
return min;
}

public Optional<Double> getMax() {
return max;
}

public Optional<String> getPattern() {
return pattern;
}

public boolean isNullable() {
return nullable;
}

public boolean getRequired() {
return nullable;
}
}

/**
* Creates the JSON schema for the particular {@link TypeDef}. This is template method where
* sub-classes are supposed to provide specific implementations of abstract methods.
Expand Down Expand Up @@ -269,7 +320,15 @@ private T internalFromImpl(TypeDef definition, Set<String> visited, List<Interna
} else {
possiblyUpdatedSchema = addDescription(schema, description);
}
addProperty(possiblyRenamedProperty, builder, possiblyUpdatedSchema);

SchemaPropsOptions options = new SchemaPropsOptions(
facade.min,
facade.max,
facade.pattern,
facade.nullable,
facade.required);

addProperty(possiblyRenamedProperty, builder, possiblyUpdatedSchema, options);
}

validateRemainingSchemaSwaps("unmatched field", currentSchemaSwaps.stream().collect(Collectors.toList()));
Expand All @@ -292,6 +351,10 @@ private static class PropertyOrAccessor {
private final String propertyName;
private final String type;
private String renamedTo;
private Optional<Double> min;
private Optional<Double> max;
private Optional<String> pattern;
private boolean nullable;
private boolean required;
private boolean ignored;
private boolean preserveUnknownFields;
Expand All @@ -303,6 +366,10 @@ private PropertyOrAccessor(Collection<AnnotationRef> annotations, String name, S
this.name = name;
this.propertyName = propertyName;
type = isMethod ? "accessor" : "field";

min = Optional.empty();
max = Optional.empty();
pattern = Optional.empty();
}

static PropertyOrAccessor fromProperty(Property property) {
Expand All @@ -316,17 +383,34 @@ static PropertyOrAccessor fromMethod(Method method, String propertyName) {
public void process() {
annotations.forEach(a -> {
switch (a.getClassRef().getFullyQualifiedName()) {
case ANNOTATION_NULLABLE:
nullable = true;
break;
case ANNOTATION_MAX:
max = Optional.of((Double) a.getParameters().get(VALUE));
break;
case ANNOTATION_MIN:
min = Optional.of((Double) a.getParameters().get(VALUE));
break;
case ANNOTATION_PATTERN:
pattern = Optional.of((String) a.getParameters().get(VALUE));
break;
case ANNOTATION_NOT_NULL:
LOGGER.warn("Annotation: {} on property: {} is deprecated. Please use: {} instead", ANNOTATION_NOT_NULL, name,
ANNOTATION_REQUIRED);
required = true;
break;
case ANNOTATION_REQUIRED:
required = true;
break;
case ANNOTATION_JSON_PROPERTY:
final String nameFromAnnotation = (String) a.getParameters().get("value");
final String nameFromAnnotation = (String) a.getParameters().get(VALUE);
if (!Strings.isNullOrEmpty(nameFromAnnotation) && !propertyName.equals(nameFromAnnotation)) {
renamedTo = nameFromAnnotation;
}
break;
case ANNOTATION_JSON_PROPERTY_DESCRIPTION:
final String descriptionFromAnnotation = (String) a.getParameters().get("value");
final String descriptionFromAnnotation = (String) a.getParameters().get(VALUE);
if (!Strings.isNullOrEmpty(descriptionFromAnnotation)) {
description = descriptionFromAnnotation;
}
Expand All @@ -349,6 +433,22 @@ public String getRenamedTo() {
return renamedTo;
}

public boolean isNullable() {
return nullable;
}

public Optional<Double> getMax() {
return max;
}

public Optional<Double> getMin() {
return min;
}

public Optional<String> getPattern() {
return pattern;
}

public boolean isRequired() {
return required;
}
Expand Down Expand Up @@ -393,6 +493,10 @@ private static class PropertyFacade {
private final Set<InternalSchemaSwap> matchedSchemaSwaps;
private String renamedTo;
private String description;
private Optional<Double> min;
private Optional<Double> max;
private Optional<String> pattern;
private boolean nullable;
private boolean required;
private boolean ignored;
private boolean preserveUnknownFields;
Expand Down Expand Up @@ -420,6 +524,9 @@ public PropertyFacade(Property property, Map<String, Method> potentialAccessors,
if (method != null) {
propertyOrAccessors.add(PropertyOrAccessor.fromMethod(method, name));
}
min = Optional.empty();
max = Optional.empty();
pattern = Optional.empty();
}

public Property process() {
Expand Down Expand Up @@ -456,6 +563,22 @@ public Property process() {
}
}

if (p.getMin().isPresent()) {
min = p.getMin();
}

if (p.getMax().isPresent()) {
max = p.getMax();
}

if (p.getPattern().isPresent()) {
pattern = p.getPattern();
}

if (p.isNullable()) {
nullable = true;
}

if (p.isRequired()) {
required = true;
} else if (p.isIgnored()) {
Expand Down Expand Up @@ -526,7 +649,7 @@ private String extractUpdatedNameFromJacksonPropertyIfPresent(Property property)
* @param builder the builder representing the schema being built
* @param schema the built schema for the property being added
*/
public abstract void addProperty(Property property, B builder, T schema);
public abstract void addProperty(Property property, B builder, T schema, SchemaPropsOptions options);

/**
* Finishes up the process by actually building the final JSON schema based on the provided
Expand Down
Expand Up @@ -17,7 +17,10 @@

import java.lang.annotation.*;

@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD})
/*
* Used to tweak the behavior of the crd-generator
*/
@Target({ ElementType.ANNOTATION_TYPE, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface SchemaFrom {
Class<?> type() default void.class;
Expand Down
Expand Up @@ -17,10 +17,15 @@

import java.lang.annotation.*;

@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE_USE, ElementType.TYPE})
/*
* Used to tweak the behavior of the crd-generator
*/
@Target({ ElementType.ANNOTATION_TYPE, ElementType.TYPE_USE, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface SchemaSwap {
Class<?> originalType();

String fieldName();

Class<?> targetType() default void.class;
}
Expand Up @@ -22,18 +22,19 @@
import io.sundr.model.Property;
import io.sundr.model.TypeDef;
import io.sundr.model.TypeRef;

import java.util.List;

public class JsonSchema extends AbstractJsonSchema<JSONSchemaProps, JSONSchemaPropsBuilder> {

private static final JsonSchema instance = new JsonSchema();

private static final JSONSchemaProps JSON_SCHEMA_INT_OR_STRING = new JSONSchemaPropsBuilder()
.withXKubernetesIntOrString(true)
.withAnyOf(
new JSONSchemaPropsBuilder().withType("integer").build(),
new JSONSchemaPropsBuilder().withType("string").build())
.build();
.withXKubernetesIntOrString(true)
.withAnyOf(
new JSONSchemaPropsBuilder().withType("integer").build(),
new JSONSchemaPropsBuilder().withType("string").build())
.build();

/**
* Creates the JSON schema for the particular {@link TypeDef}.
Expand All @@ -55,8 +56,16 @@ public JSONSchemaPropsBuilder newBuilder() {

@Override
public void addProperty(Property property, JSONSchemaPropsBuilder builder,
JSONSchemaProps schema) {
JSONSchemaProps schema, SchemaPropsOptions options) {
if (schema != null) {
options.getMin().ifPresent(schema::setMinimum);
options.getMax().ifPresent(schema::setMaximum);
options.getPattern().ifPresent(schema::setPattern);

if (options.isNullable()) {
schema.setNullable(true);
}

builder.addToProperties(property.getName(), schema);
}
}
Expand All @@ -73,21 +82,21 @@ public JSONSchemaProps build(JSONSchemaPropsBuilder builder, List<String> requir
@Override
protected JSONSchemaProps arrayLikeProperty(JSONSchemaProps schema) {
return new JSONSchemaPropsBuilder()
.withType("array")
.withNewItems()
.withSchema(schema)
.and()
.build();
.withType("array")
.withNewItems()
.withSchema(schema)
.and()
.build();
}

@Override
protected JSONSchemaProps mapLikeProperty(JSONSchemaProps schema) {
return new JSONSchemaPropsBuilder()
.withType("object")
.withNewAdditionalProperties()
.withSchema(schema)
.endAdditionalProperties()
.build();
.withType("object")
.withNewAdditionalProperties()
.withSchema(schema)
.endAdditionalProperties()
.build();
}

@Override
Expand All @@ -108,7 +117,7 @@ protected JSONSchemaProps enumProperty(JsonNode... enumValues) {
@Override
protected JSONSchemaProps addDescription(JSONSchemaProps schema, String description) {
return new JSONSchemaPropsBuilder(schema)
.withDescription(description)
.build();
.withDescription(description)
.build();
}
}

0 comments on commit 93f7454

Please sign in to comment.