Skip to content

Commit 2024d02

Browse files
authored
fix(crd-generator): allow defaults to be full json or yaml (6903)
fix: allow defaults to be full json or yaml closes: #6908 Signed-off-by: Steve Hawkins <shawkins@redhat.com> --- Merge branch 'main' into iss6900 --- not requiring the JsonProperty annotation to be valid Signed-off-by: Steve Hawkins <shawkins@redhat.com>
1 parent e7a8e1e commit 2024d02

File tree

6 files changed

+61
-54
lines changed

6 files changed

+61
-54
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#### Bugs
66
* Fix #6892: rolling().restart() doesn't remove preexistent pod template annotations
7+
* Fix #6908: The Default annotation and JsonProperty default value should accept JSON values
78
* Fix #6906: Knative VolatileTime should be serialized as String
89
* Fix #6930: Add support for Boolean enums in the java-generator
910
* Fix #6886: Remove invalid JUnit 4 references

crd-generator/api-v2/src/main/java/io/fabric8/crdv2/generator/AbstractJsonSchema.java

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@
8585
import java.util.stream.Collectors;
8686
import java.util.stream.Stream;
8787

88-
import static io.fabric8.crdv2.generator.CRDUtils.toTargetType;
8988
import static java.util.Optional.ofNullable;
9089

9190
/**
@@ -232,7 +231,7 @@ class PropertyMetadata {
232231

233232
private boolean required;
234233
private final String description;
235-
private final Object defaultValue;
234+
private final JsonNode defaultValue;
236235
private Double min;
237236
private Boolean exclusiveMinimum;
238237
private Double max;
@@ -314,14 +313,27 @@ public PropertyMetadata(JsonSchema value, BeanProperty beanProperty) {
314313

315314
// TODO: should the following be deprecated?
316315
required = beanProperty.getAnnotation(Required.class) != null;
316+
defaultValue = toDefault(beanProperty);
317+
}
318+
319+
JsonNode toDefault(BeanProperty beanProperty) {
320+
Optional<String> defaultAnnotationValue = ofNullable(beanProperty.getAnnotation(Default.class)).map(Default::value);
321+
String value = defaultAnnotationValue.orElse(beanProperty.getMetadata().getDefaultValue());
317322

318-
if (beanProperty.getMetadata().getDefaultValue() != null) {
319-
defaultValue = toTargetType(beanProperty.getType(), beanProperty.getMetadata().getDefaultValue());
320-
} else if (ofNullable(beanProperty.getAnnotation(Default.class)).map(Default::value).isPresent()) {
321-
defaultValue = toTargetType(beanProperty.getType(),
322-
ofNullable(beanProperty.getAnnotation(Default.class)).map(Default::value).get());
323-
} else {
324-
defaultValue = null;
323+
if (value == null) {
324+
return null;
325+
}
326+
Optional<Class<?>> rawType = Optional.ofNullable(beanProperty.getType()).map(JavaType::getRawClass);
327+
try {
328+
Object typedValue = resolvingContext.kubernetesSerialization.unmarshal(value, rawType.orElse(Object.class));
329+
return resolvingContext.kubernetesSerialization.convertValue(typedValue, JsonNode.class);
330+
} catch (Exception e) {
331+
if (defaultAnnotationValue.isEmpty()) {
332+
LOGGER.warn("Cannot parse default value: '" + value
333+
+ "' from JsonProperty annotation as valid YAML or JSON, no default value will be used.");
334+
return null;
335+
}
336+
throw new IllegalArgumentException("Cannot parse default value: '" + value + "' as valid YAML or JSON.", e);
325337
}
326338
}
327339

@@ -355,14 +367,7 @@ private void setMinMax(BeanProperty beanProperty,
355367

356368
public void updateSchema(T schema) {
357369
schema.setDescription(description);
358-
359-
if (defaultValue != null) {
360-
try {
361-
schema.setDefault(resolvingContext.kubernetesSerialization.convertValue(defaultValue, JsonNode.class));
362-
} catch (IllegalArgumentException e) {
363-
throw new IllegalArgumentException("Cannot parse default value: '" + defaultValue + "' as valid YAML.", e);
364-
}
365-
}
370+
schema.setDefault(defaultValue);
366371
if (nullable) {
367372
schema.setNullable(true);
368373
}

crd-generator/api-v2/src/main/java/io/fabric8/crdv2/generator/CRDUtils.java

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,10 @@
1616
package io.fabric8.crdv2.generator;
1717

1818
import com.fasterxml.jackson.databind.BeanDescription;
19-
import com.fasterxml.jackson.databind.JavaType;
2019
import com.fasterxml.jackson.databind.ObjectMapper;
2120
import com.fasterxml.jackson.databind.SerializationConfig;
2221
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
2322

24-
import java.text.NumberFormat;
2523
import java.util.HashMap;
2624
import java.util.Map;
2725

@@ -94,23 +92,4 @@ public static Map<String, String> toMap(String[] arr) {
9492
return res;
9593
}
9694

97-
static Object toTargetType(JavaType type, String value) {
98-
if (type == null || value == null) {
99-
return null;
100-
}
101-
try {
102-
if (Number.class.isAssignableFrom(type.getRawClass()) || int.class.isAssignableFrom(type.getRawClass())
103-
|| long.class.isAssignableFrom(type.getRawClass()) || float.class.isAssignableFrom(type.getRawClass())
104-
|| double.class.isAssignableFrom(type.getRawClass())) {
105-
return NumberFormat.getInstance().parse(value);
106-
}
107-
if (Boolean.class.isAssignableFrom(type.getRawClass()) || boolean.class.isAssignableFrom(type.getRawClass())) {
108-
return Boolean.valueOf(value);
109-
}
110-
} catch (Exception ex) {
111-
// NO OP
112-
}
113-
return value;
114-
115-
}
11695
}

crd-generator/api-v2/src/test/java/io/fabric8/crdv2/generator/v1/JsonSchemaDefaultValueTest.java

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717

1818
import com.fasterxml.jackson.annotation.JsonProperty;
1919
import com.fasterxml.jackson.databind.JsonNode;
20+
import com.fasterxml.jackson.databind.node.ArrayNode;
2021
import com.fasterxml.jackson.databind.node.BooleanNode;
2122
import com.fasterxml.jackson.databind.node.DoubleNode;
23+
import com.fasterxml.jackson.databind.node.FloatNode;
24+
import com.fasterxml.jackson.databind.node.IntNode;
2225
import com.fasterxml.jackson.databind.node.LongNode;
2326
import io.fabric8.generator.annotation.Default;
2427
import org.assertj.core.api.InstanceOfAssertFactories;
@@ -55,24 +58,31 @@ private static final class ClassInTest {
5558
@JsonProperty(defaultValue = "1337")
5659
int defaultValueForInt;
5760

58-
@JsonProperty(defaultValue = "1337L")
61+
@JsonProperty(defaultValue = "1337")
5962
long defaultValueForLong;
6063

6164
@JsonProperty(defaultValue = "13.37")
6265
float defaultValueForFloat;
6366

64-
@JsonProperty(defaultValue = "13.37d")
67+
@JsonProperty(defaultValue = "13.37")
6568
double defaultValueForDouble;
69+
70+
@JsonProperty(defaultValue = "invalid")
71+
double invalidDefaultValueForDouble;
72+
73+
@JsonProperty
74+
@Default("[]")
75+
double[] defaultValueForDoubleArray;
6676
}
6777

6878
@Test
69-
@DisplayName("JsonProperty default value should take precedence over Default annotation")
79+
@DisplayName("Default annotation should take precedence over JsonProperty")
7080
void precedence() {
7181
assertThat(JsonSchema.from(ClassInTest.class).getProperties())
7282
.extracting("precedence._default")
7383
.asInstanceOf(InstanceOfAssertFactories.type(JsonNode.class))
7484
.extracting(JsonNode::asText)
75-
.isEqualTo("precedence-from-json-property");
85+
.isEqualTo("precedence-from-default-annotation");
7686
}
7787

7888
@Test
@@ -135,7 +145,7 @@ void intFromJsonPropertyAnnotation() {
135145
assertThat(JsonSchema.from(ClassInTest.class).getProperties())
136146
.extracting("defaultValueForInt._default")
137147
.asInstanceOf(InstanceOfAssertFactories.type(JsonNode.class))
138-
.isInstanceOf(LongNode.class)
148+
.isInstanceOf(IntNode.class)
139149
.extracting(JsonNode::asInt)
140150
.isEqualTo(1337);
141151
}
@@ -157,9 +167,9 @@ void floatFromJsonPropertyAnnotation() {
157167
assertThat(JsonSchema.from(ClassInTest.class).getProperties())
158168
.extracting("defaultValueForFloat._default")
159169
.asInstanceOf(InstanceOfAssertFactories.type(JsonNode.class))
160-
.isInstanceOf(DoubleNode.class)
161-
.extracting(JsonNode::asDouble)
162-
.isEqualTo(13.37);
170+
.isInstanceOf(FloatNode.class)
171+
.extracting(JsonNode::asText)
172+
.isEqualTo("13.37");
163173
}
164174

165175
@Test
@@ -172,4 +182,23 @@ void doubleFromJsonPropertyAnnotation() {
172182
.extracting(JsonNode::asDouble)
173183
.isEqualTo(13.37);
174184
}
185+
186+
@Test
187+
@DisplayName("JsonProperty defaultValue annotation for double array")
188+
void doubleArrayFromJsonPropertyAnnotation() {
189+
assertThat(JsonSchema.from(ClassInTest.class).getProperties())
190+
.extracting("defaultValueForDoubleArray._default")
191+
.asInstanceOf(InstanceOfAssertFactories.type(JsonNode.class))
192+
.isInstanceOf(ArrayNode.class)
193+
.extracting(JsonNode::toPrettyString)
194+
.isEqualTo("[ ]");
195+
}
196+
197+
@Test
198+
@DisplayName("JsonProperty invalid defaultValue annotation not used")
199+
void invalidDefaultFromJsonPropertyAnnotationNotUsed() {
200+
assertThat(JsonSchema.from(ClassInTest.class).getProperties())
201+
.extracting("invalidDefaultValueForDouble._default")
202+
.isNull();
203+
}
175204
}

doc/CRD-generator-migration-v2.md

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,7 @@ myMap:
100100
type: "object"
101101
```
102102
103-
## Default values for CRD fields can be numeric or boolean
104-
105-
Previously default values defined by `@Default` could only be used on string fields.
106-
With CRD Generator v2 defaults can be set on numeric and boolean fields, too.
107-
In the same way is `@JsonProperty(defaultValue)` now working.
108-
109103
## Post-processing CRDs before they are written out to disk
110104
111105
It is now possible to provide a `CRDPostProcessor` implementation when generating CRDs via the
112106
`CRDGenerator.detailedGenerate` method. This allows to process generated CRDs before they are written out to the disk.
113-

kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/KubernetesSerialization.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ public Type getType() {
369369
* Create a copy of the resource via serialization.
370370
*
371371
* @return a deep clone of the resource
372-
* @throws IllegalArgumentException if the cloning cannot be performed
372+
* @throws IllegalStateException if the cloning cannot be performed
373373
*/
374374
public <T> T clone(T resource) {
375375
// if full serialization seems too expensive, there is also

0 commit comments

Comments
 (0)