-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
fix(crd-generator): default values for CRD fields can be numeric or boolean #6666
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,175 @@ | ||||||||||||||||||||||||||
/* | ||||||||||||||||||||||||||
* 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.crdv2.generator.v1; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
import com.fasterxml.jackson.annotation.JsonProperty; | ||||||||||||||||||||||||||
import com.fasterxml.jackson.databind.JsonNode; | ||||||||||||||||||||||||||
import com.fasterxml.jackson.databind.node.BooleanNode; | ||||||||||||||||||||||||||
import com.fasterxml.jackson.databind.node.DoubleNode; | ||||||||||||||||||||||||||
import com.fasterxml.jackson.databind.node.LongNode; | ||||||||||||||||||||||||||
import io.fabric8.generator.annotation.Default; | ||||||||||||||||||||||||||
import org.assertj.core.api.InstanceOfAssertFactories; | ||||||||||||||||||||||||||
import org.junit.jupiter.api.DisplayName; | ||||||||||||||||||||||||||
import org.junit.jupiter.api.Test; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
import static org.assertj.core.api.Assertions.assertThat; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
class JsonSchemaDefaultValueTest { | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
private static final class ClassInTest { | ||||||||||||||||||||||||||
@JsonProperty(defaultValue = "precedence-from-json-property") | ||||||||||||||||||||||||||
@Default("precedence-from-default-annotation") | ||||||||||||||||||||||||||
String precedence; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
@JsonProperty | ||||||||||||||||||||||||||
@Default("string-from-default-annotation") | ||||||||||||||||||||||||||
String defaultAnnotationForString; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
@JsonProperty(defaultValue = "true") | ||||||||||||||||||||||||||
Boolean defaultValueForBoxedBoolean; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
@JsonProperty | ||||||||||||||||||||||||||
@Default("true") | ||||||||||||||||||||||||||
Boolean defaultAnnotationForBoxedBoolean; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
@JsonProperty(defaultValue = "true") | ||||||||||||||||||||||||||
boolean defaultValueForBoolean; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
@JsonProperty | ||||||||||||||||||||||||||
@Default("true") | ||||||||||||||||||||||||||
boolean defaultAnnotationForBoolean; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
@JsonProperty(defaultValue = "1337") | ||||||||||||||||||||||||||
int defaultValueForInt; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
@JsonProperty(defaultValue = "1337L") | ||||||||||||||||||||||||||
long defaultValueForLong; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
@JsonProperty(defaultValue = "13.37") | ||||||||||||||||||||||||||
float defaultValueForFloat; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
@JsonProperty(defaultValue = "13.37d") | ||||||||||||||||||||||||||
double defaultValueForDouble; | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
@Test | ||||||||||||||||||||||||||
@DisplayName("JsonProperty default value should take precedence over Default annotation") | ||||||||||||||||||||||||||
void precedence() { | ||||||||||||||||||||||||||
assertThat(JsonSchema.from(ClassInTest.class).getProperties()) | ||||||||||||||||||||||||||
.extracting("precedence._default") | ||||||||||||||||||||||||||
.asInstanceOf(InstanceOfAssertFactories.type(JsonNode.class)) | ||||||||||||||||||||||||||
.extracting(JsonNode::asText) | ||||||||||||||||||||||||||
.isEqualTo("precedence-from-json-property"); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
@Test | ||||||||||||||||||||||||||
@DisplayName("Default annotation for String") | ||||||||||||||||||||||||||
void stringFromDefaultAnnotation() { | ||||||||||||||||||||||||||
assertThat(JsonSchema.from(ClassInTest.class).getProperties()) | ||||||||||||||||||||||||||
.extracting("defaultAnnotationForString._default") | ||||||||||||||||||||||||||
.asInstanceOf(InstanceOfAssertFactories.type(JsonNode.class)) | ||||||||||||||||||||||||||
.extracting(JsonNode::asText) | ||||||||||||||||||||||||||
.isEqualTo("string-from-default-annotation"); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
@Test | ||||||||||||||||||||||||||
@DisplayName("JsonProperty defaultValue annotation for boxed Boolean") | ||||||||||||||||||||||||||
void booleanBoxedFromJsonPropertyAnnotation() { | ||||||||||||||||||||||||||
assertThat(JsonSchema.from(ClassInTest.class).getProperties()) | ||||||||||||||||||||||||||
.extracting("defaultValueForBoxedBoolean._default") | ||||||||||||||||||||||||||
.asInstanceOf(InstanceOfAssertFactories.type(JsonNode.class)) | ||||||||||||||||||||||||||
.isInstanceOf(BooleanNode.class) | ||||||||||||||||||||||||||
.extracting(JsonNode::asBoolean) | ||||||||||||||||||||||||||
.isEqualTo(true); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
@Test | ||||||||||||||||||||||||||
@DisplayName("Default annotation for boxed Boolean") | ||||||||||||||||||||||||||
void booleanBoxedFromDefaultAnnotation() { | ||||||||||||||||||||||||||
assertThat(JsonSchema.from(ClassInTest.class).getProperties()) | ||||||||||||||||||||||||||
.extracting("defaultAnnotationForBoxedBoolean._default") | ||||||||||||||||||||||||||
.asInstanceOf(InstanceOfAssertFactories.type(JsonNode.class)) | ||||||||||||||||||||||||||
.isInstanceOf(BooleanNode.class) | ||||||||||||||||||||||||||
.extracting(JsonNode::asBoolean) | ||||||||||||||||||||||||||
.isEqualTo(true); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
@Test | ||||||||||||||||||||||||||
@DisplayName("JsonProperty defaultValue annotation for boolean") | ||||||||||||||||||||||||||
void booleanFromJsonPropertyAnnotation() { | ||||||||||||||||||||||||||
assertThat(JsonSchema.from(ClassInTest.class).getProperties()) | ||||||||||||||||||||||||||
.extracting("defaultValueForBoolean._default") | ||||||||||||||||||||||||||
.asInstanceOf(InstanceOfAssertFactories.type(JsonNode.class)) | ||||||||||||||||||||||||||
.isInstanceOf(BooleanNode.class) | ||||||||||||||||||||||||||
.extracting(JsonNode::asBoolean) | ||||||||||||||||||||||||||
.isEqualTo(true); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
@Test | ||||||||||||||||||||||||||
@DisplayName("Default annotation for boolean") | ||||||||||||||||||||||||||
void booleanFromDefaultAnnotation() { | ||||||||||||||||||||||||||
assertThat(JsonSchema.from(ClassInTest.class).getProperties()) | ||||||||||||||||||||||||||
.extracting("defaultAnnotationForBoolean._default") | ||||||||||||||||||||||||||
.asInstanceOf(InstanceOfAssertFactories.type(JsonNode.class)) | ||||||||||||||||||||||||||
.isInstanceOf(BooleanNode.class) | ||||||||||||||||||||||||||
.extracting(JsonNode::asBoolean) | ||||||||||||||||||||||||||
.isEqualTo(true); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
@Test | ||||||||||||||||||||||||||
@DisplayName("JsonProperty defaultValue annotation for int") | ||||||||||||||||||||||||||
void intFromJsonPropertyAnnotation() { | ||||||||||||||||||||||||||
assertThat(JsonSchema.from(ClassInTest.class).getProperties()) | ||||||||||||||||||||||||||
.extracting("defaultValueForInt._default") | ||||||||||||||||||||||||||
.asInstanceOf(InstanceOfAssertFactories.type(JsonNode.class)) | ||||||||||||||||||||||||||
.isInstanceOf(LongNode.class) | ||||||||||||||||||||||||||
.extracting(JsonNode::asInt) | ||||||||||||||||||||||||||
.isEqualTo(1337); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
@Test | ||||||||||||||||||||||||||
@DisplayName("JsonProperty defaultValue annotation for long") | ||||||||||||||||||||||||||
void longFromJsonPropertyAnnotation() { | ||||||||||||||||||||||||||
assertThat(JsonSchema.from(ClassInTest.class).getProperties()) | ||||||||||||||||||||||||||
.extracting("defaultValueForLong._default") | ||||||||||||||||||||||||||
.asInstanceOf(InstanceOfAssertFactories.type(JsonNode.class)) | ||||||||||||||||||||||||||
.isInstanceOf(LongNode.class) | ||||||||||||||||||||||||||
.extracting(JsonNode::asLong) | ||||||||||||||||||||||||||
.isEqualTo(1337L); | ||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be possible to also generate the resulting YAML file? I'm interested in checking the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The suffixes here I added them to highlight that these values are equal to the expected type, to make it clear that a long is a long and a double is not a double. There's also an approval test modified that you can check: Lines 25 to 36 in bee2f01
I noticed that I didn't add a specific test for doubles, but the expected value should be an unquoted number with a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
@Test | ||||||||||||||||||||||||||
@DisplayName("JsonProperty defaultValue annotation for float") | ||||||||||||||||||||||||||
void floatFromJsonPropertyAnnotation() { | ||||||||||||||||||||||||||
assertThat(JsonSchema.from(ClassInTest.class).getProperties()) | ||||||||||||||||||||||||||
.extracting("defaultValueForFloat._default") | ||||||||||||||||||||||||||
.asInstanceOf(InstanceOfAssertFactories.type(JsonNode.class)) | ||||||||||||||||||||||||||
.isInstanceOf(DoubleNode.class) | ||||||||||||||||||||||||||
.extracting(JsonNode::asDouble) | ||||||||||||||||||||||||||
.isEqualTo(13.37); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
@Test | ||||||||||||||||||||||||||
@DisplayName("JsonProperty defaultValue annotation for double") | ||||||||||||||||||||||||||
void doubleFromJsonPropertyAnnotation() { | ||||||||||||||||||||||||||
assertThat(JsonSchema.from(ClassInTest.class).getProperties()) | ||||||||||||||||||||||||||
.extracting("defaultValueForDouble._default") | ||||||||||||||||||||||||||
.asInstanceOf(InstanceOfAssertFactories.type(JsonNode.class)) | ||||||||||||||||||||||||||
.isInstanceOf(DoubleNode.class) | ||||||||||||||||||||||||||
.extracting(JsonNode::asDouble) | ||||||||||||||||||||||||||
.isEqualTo(13.37); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -42,6 +42,15 @@ public class AnnotatedSpec { | |||
private String defaultValue; | ||||
@Default("my-value2") | ||||
private String defaultValue2; | ||||
@JsonProperty(defaultValue = "true") | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens when the two annotations contain different values? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's a precedence test for this, you can check it out ;) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
oh, really? I'm surprised! Should this behavior be documented? somewhere There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have really no idea, I deduced this from this Line 281 in bee2f01
I thought this had been discussed, but maybe not. TBH this is the first time I'm actually looking into the CRD generator, up until now I had been delegating any task related to the CRD generator to you folks. So I might be disconnected. In any case, a deprecation would happen in 7.x and not in 7.0.0 |
||||
@Default("true") // compatibility with CRDGenerator-v1 | ||||
private boolean defaultBoolean; | ||||
@JsonProperty(defaultValue = "1337") | ||||
@Default("1337") // compatibility with CRDGenerator-v1 | ||||
private Integer defaultInteger; | ||||
@JsonProperty(defaultValue = "1337") | ||||
@Default("1337") // compatibility with CRDGenerator-v1 | ||||
private int defaultInt; | ||||
@Required | ||||
private boolean emptySetter; | ||||
@Required | ||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A little surprised this is a LongNode
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has to do with the Number parser which considers this to be of long precision.
It really doesn't matter, since what we want is the number to be considered a number and not a String.
Of course, there must be corner cases such as users declaring a field of type in and setting a default value of
999999999999999999999
. My expectation is that the cluster won't accept that CRD.We could try and consider those corner cases, but if we introduce validations for the default values too, the complexity will be kind of crazy for such a simple feature.
Fixing this is just a matter of using the specific parser for the target type. i.e. adding more if-else clauses.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good enough for me, if we get any complaints over this we should remember to add checks for the boundaries (i.e.
Integer.MAX_VALUE
) etc.