Skip to content

Commit

Permalink
Introduce specific annotations for the generators
Browse files Browse the repository at this point in the history
  • Loading branch information
andreaTP committed Aug 25, 2022
1 parent 3296ece commit e2cfb20
Show file tree
Hide file tree
Showing 14 changed files with 523 additions and 58 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 @@ -60,6 +59,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 @@ -78,6 +79,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 @@ -110,6 +116,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 @@ -264,7 +315,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 @@ -286,6 +345,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 @@ -297,6 +360,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 @@ -310,17 +377,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 @@ -343,6 +427,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 @@ -387,6 +487,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 @@ -414,6 +518,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 @@ -450,6 +557,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 @@ -502,7 +625,7 @@ private String extractUpdatedNameFromJacksonPropertyIfPresent(Property property)
.findAny()
// if we found an annotated accessor, override the property's name if needed
.map(a -> {
final String fromAnnotation = (String) a.getParameters().get("value");
final String fromAnnotation = (String) a.getParameters().get(VALUE);
if (!Strings.isNullOrEmpty(fromAnnotation) && !name.equals(fromAnnotation)) {
return fromAnnotation;
} else {
Expand All @@ -527,7 +650,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 @@ -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 e2cfb20

Please sign in to comment.