Skip to content

Commit ef6da4f

Browse files
committed
fix: Remove trailing nulls for optional enums
1 parent e3172d5 commit ef6da4f

File tree

2 files changed

+67
-0
lines changed

2 files changed

+67
-0
lines changed

kube-core/src/schema/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
55
mod transform_any_of;
66
mod transform_one_of;
7+
mod transform_optional_enum_with_null;
78
mod transform_properties;
89

910
// Used in docs
@@ -13,6 +14,7 @@ use schemars::{transform::Transform, JsonSchema};
1314
use serde::{Deserialize, Serialize};
1415
use serde_json::Value;
1516
use std::collections::{BTreeMap, BTreeSet};
17+
use transform_optional_enum_with_null::remove_optional_enum_null_variant;
1618

1719
use crate::schema::{
1820
transform_any_of::hoist_any_of_subschema_with_a_nullable_variant,
@@ -278,6 +280,7 @@ impl Transform for StructuralSchemaRewriter {
278280
hoist_one_of_enum_with_unit_variants(&mut schema);
279281
hoist_any_of_subschema_with_a_nullable_variant(&mut schema);
280282
hoist_properties_for_any_of_subschemas(&mut schema);
283+
remove_optional_enum_null_variant(&mut schema);
281284

282285
// check for maps without with properties (i.e. flattened maps)
283286
// and allow these to persist dynamically
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
use serde_json::Value;
2+
3+
use super::SchemaObject;
4+
5+
#[cfg(test)]
6+
#[test]
7+
fn optional_enum_with_null() {
8+
let original_schema_object_value = serde_json::json!({
9+
"description": "A very simple enum with unit variants without descriptions",
10+
"enum": [
11+
"A",
12+
"B",
13+
"C",
14+
"D",
15+
null
16+
],
17+
"nullable": true
18+
});
19+
20+
let expected_converted_schema_object_value = serde_json::json!({
21+
"description": "A very simple enum with unit variants without descriptions",
22+
"enum": [
23+
"A",
24+
"B",
25+
"C",
26+
"D"
27+
],
28+
"nullable": true
29+
});
30+
31+
let original_schema_object: SchemaObject =
32+
serde_json::from_value(original_schema_object_value).expect("valid JSON");
33+
let expected_converted_schema_object: SchemaObject =
34+
serde_json::from_value(expected_converted_schema_object_value).expect("valid JSON");
35+
36+
let mut actual_converted_schema_object = original_schema_object.clone();
37+
remove_optional_enum_null_variant(&mut actual_converted_schema_object);
38+
39+
assert_json_diff::assert_json_eq!(actual_converted_schema_object, expected_converted_schema_object);
40+
}
41+
42+
/// A Option<Enum> without any descriptions has `nullable` set to `true` as well as a trailing
43+
/// `null` enum variant. We remove the trailing enum variant as it's not needed for Kubernetes and
44+
/// makes the CRD more compact by removing duplicated information.
45+
pub(crate) fn remove_optional_enum_null_variant(kube_schema: &mut SchemaObject) {
46+
let SchemaObject {
47+
enum_values: Some(enum_values),
48+
extensions,
49+
..
50+
} = kube_schema
51+
else {
52+
return;
53+
};
54+
55+
// It only makes sense to remove `null` enum values in case the enum is
56+
// nullable (thus optional).
57+
if let Some(Value::Bool(true)) = extensions.get("nullable")
58+
// Don't remove the single last enum variant. This often happens for
59+
// `Option<XXX>`, which is represented as
60+
// `"anyOf": [XXX, {"enum": [null], "optional": true}]`
61+
&& enum_values.len() > 1 {
62+
enum_values.retain(|enum_value| enum_value != &Value::Null);
63+
}
64+
}

0 commit comments

Comments
 (0)