Skip to content

Commit 50d1b89

Browse files
committed
chore(schema): Split StructuralSchemaRewriter into multiple transformers
Signed-off-by: Nick Larsen <[email protected]>
1 parent 221510e commit 50d1b89

File tree

6 files changed

+128
-15
lines changed

6 files changed

+128
-15
lines changed

kube-core/src/schema/mod.rs

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,10 @@ use std::{
1818
sync::LazyLock,
1919
};
2020

21-
use crate::schema::{
22-
transform_any_of::hoist_any_of_subschema_with_a_nullable_variant,
23-
transform_one_of::hoist_one_of_enum_with_unit_variants,
24-
transform_optional_enum_with_null::remove_optional_enum_null_variant,
25-
transform_properties::hoist_properties_for_any_of_subschemas,
21+
pub use crate::schema::{
22+
transform_any_of::AnyOfSchemaRewriter, transform_one_of::OneOfSchemaRewriter,
23+
transform_optional_enum_with_null::OptionalEnumSchemaRewriter,
24+
transform_properties::PropertiesSchemaRewriter,
2625
};
2726

2827
/// This is the signature for the `null` variant produced by schemars.
@@ -47,6 +46,7 @@ static NULL_SCHEMA: LazyLock<Value> = LazyLock::new(|| {
4746
///
4847
/// The [`Visitor`] functions may panic if the transform could not be applied. For example,
4948
/// there must not be any overlapping properties between `oneOf` branches.
49+
// TODO (@NickLarsenNZ): Rename this transformer and update the docs above
5050
#[derive(Debug, Clone)]
5151
pub struct StructuralSchemaRewriter;
5252

@@ -282,12 +282,6 @@ impl Transform for StructuralSchemaRewriter {
282282
None => return,
283283
};
284284

285-
// Hoist parts of the schema to make it valid for k8s
286-
hoist_one_of_enum_with_unit_variants(&mut schema);
287-
hoist_any_of_subschema_with_a_nullable_variant(&mut schema);
288-
hoist_properties_for_any_of_subschemas(&mut schema);
289-
remove_optional_enum_null_variant(&mut schema);
290-
291285
// check for maps without with properties (i.e. flattened maps)
292286
// and allow these to persist dynamically
293287
if let Some(object) = &mut schema.object {

kube-core/src/schema/transform_any_of.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use std::ops::DerefMut;
22

3+
use schemars::transform::Transform;
4+
35
use crate::schema::{Schema, SchemaObject, SubschemaValidation, NULL_SCHEMA};
46

57
/// Replace the schema with the anyOf subschema and set to nullable when the
@@ -14,7 +16,34 @@ use crate::schema::{Schema, SchemaObject, SubschemaValidation, NULL_SCHEMA};
1416
/// `null` entry, and `nullable` set to true).
1517
///
1618
/// NOTE: This should work regardless of whether other hoisting has been performed or not.
17-
pub(crate) fn hoist_any_of_subschema_with_a_nullable_variant(kube_schema: &mut SchemaObject) {
19+
#[derive(Debug, Clone)]
20+
pub struct AnyOfSchemaRewriter;
21+
22+
impl Transform for AnyOfSchemaRewriter {
23+
fn transform(&mut self, transform_schema: &mut schemars::Schema) {
24+
// Apply this (self) transform to all subschemas
25+
schemars::transform::transform_subschemas(self, transform_schema);
26+
27+
let mut schema: SchemaObject = match serde_json::from_value(transform_schema.clone().to_value()).ok()
28+
{
29+
// TODO (@NickLarsenNZ): At this point, we are seeing duplicate `title` when printing schema as json.
30+
// This is because `title` is specified under both `extensions` and `other`.
31+
Some(schema) => schema,
32+
None => return,
33+
};
34+
35+
hoist_any_of_subschema_with_a_nullable_variant(&mut schema);
36+
37+
// Convert back to schemars::Schema
38+
if let Ok(schema) = serde_json::to_value(schema) {
39+
if let Ok(transformed) = serde_json::from_value(schema) {
40+
*transform_schema = transformed;
41+
}
42+
}
43+
}
44+
}
45+
46+
fn hoist_any_of_subschema_with_a_nullable_variant(kube_schema: &mut SchemaObject) {
1847
// Run some initial checks in case there is nothing to do
1948
let SchemaObject {
2049
subschemas: Some(subschemas),

kube-core/src/schema/transform_one_of.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use std::ops::DerefMut;
22

3+
use schemars::transform::Transform;
4+
35
use crate::schema::{Schema, SchemaObject, SubschemaValidation};
46

57
/// Merge oneOf subschema enums and consts into a schema level enum.
@@ -17,7 +19,34 @@ use crate::schema::{Schema, SchemaObject, SubschemaValidation};
1719
///
1820
/// NOTE: This should work regardless of whether other hoisting has been
1921
/// performed or not.
20-
pub(crate) fn hoist_one_of_enum_with_unit_variants(kube_schema: &mut SchemaObject) {
22+
#[derive(Debug, Clone)]
23+
pub struct OneOfSchemaRewriter;
24+
25+
impl Transform for OneOfSchemaRewriter {
26+
fn transform(&mut self, transform_schema: &mut schemars::Schema) {
27+
// Apply this (self) transform to all subschemas
28+
schemars::transform::transform_subschemas(self, transform_schema);
29+
30+
let mut schema: SchemaObject = match serde_json::from_value(transform_schema.clone().to_value()).ok()
31+
{
32+
// TODO (@NickLarsenNZ): At this point, we are seeing duplicate `title` when printing schema as json.
33+
// This is because `title` is specified under both `extensions` and `other`.
34+
Some(schema) => schema,
35+
None => return,
36+
};
37+
38+
hoist_one_of_enum_with_unit_variants(&mut schema);
39+
40+
// Convert back to schemars::Schema
41+
if let Ok(schema) = serde_json::to_value(schema) {
42+
if let Ok(transformed) = serde_json::from_value(schema) {
43+
*transform_schema = transformed;
44+
}
45+
}
46+
}
47+
}
48+
49+
fn hoist_one_of_enum_with_unit_variants(kube_schema: &mut SchemaObject) {
2150
// Run some initial checks in case there is nothing to do
2251
let SchemaObject {
2352
subschemas: Some(subschemas),

kube-core/src/schema/transform_optional_enum_with_null.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use schemars::transform::Transform;
12
use serde_json::Value;
23

34
use super::SchemaObject;
@@ -8,7 +9,34 @@ use super::SchemaObject;
89
///
910
/// NOTE: The trailing null is removed because it's not needed by Kubernetes
1011
/// and makes the CRD more compact by removing redundant information.
11-
pub(crate) fn remove_optional_enum_null_variant(kube_schema: &mut SchemaObject) {
12+
#[derive(Debug, Clone)]
13+
pub struct OptionalEnumSchemaRewriter;
14+
15+
impl Transform for OptionalEnumSchemaRewriter {
16+
fn transform(&mut self, transform_schema: &mut schemars::Schema) {
17+
// Apply this (self) transform to all subschemas
18+
schemars::transform::transform_subschemas(self, transform_schema);
19+
20+
let mut schema: SchemaObject = match serde_json::from_value(transform_schema.clone().to_value()).ok()
21+
{
22+
// TODO (@NickLarsenNZ): At this point, we are seeing duplicate `title` when printing schema as json.
23+
// This is because `title` is specified under both `extensions` and `other`.
24+
Some(schema) => schema,
25+
None => return,
26+
};
27+
28+
remove_optional_enum_null_variant(&mut schema);
29+
30+
// Convert back to schemars::Schema
31+
if let Ok(schema) = serde_json::to_value(schema) {
32+
if let Ok(transformed) = serde_json::from_value(schema) {
33+
*transform_schema = transformed;
34+
}
35+
}
36+
}
37+
}
38+
39+
fn remove_optional_enum_null_variant(kube_schema: &mut SchemaObject) {
1240
let SchemaObject {
1341
enum_values: Some(enum_values),
1442
extensions,

kube-core/src/schema/transform_properties.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use schemars::transform::Transform;
2+
13
use crate::schema::{InstanceType, Metadata, Schema, SchemaObject, SingleOrVec, NULL_SCHEMA};
24

35
/// Take oneOf or anyOf subschema properties and move them them into the schema
@@ -11,7 +13,34 @@ use crate::schema::{InstanceType, Metadata, Schema, SchemaObject, SingleOrVec, N
1113
/// - Each subschema has the type "object"
1214
///
1315
/// NOTE: This should work regardless of whether other hoisting has been performed or not.
14-
pub(crate) fn hoist_properties_for_any_of_subschemas(kube_schema: &mut SchemaObject) {
16+
#[derive(Debug, Clone)]
17+
pub struct PropertiesSchemaRewriter;
18+
19+
impl Transform for PropertiesSchemaRewriter {
20+
fn transform(&mut self, transform_schema: &mut schemars::Schema) {
21+
// Apply this (self) transform to all subschemas
22+
schemars::transform::transform_subschemas(self, transform_schema);
23+
24+
let mut schema: SchemaObject = match serde_json::from_value(transform_schema.clone().to_value()).ok()
25+
{
26+
// TODO (@NickLarsenNZ): At this point, we are seeing duplicate `title` when printing schema as json.
27+
// This is because `title` is specified under both `extensions` and `other`.
28+
Some(schema) => schema,
29+
None => return,
30+
};
31+
32+
hoist_properties_for_any_of_subschemas(&mut schema);
33+
34+
// Convert back to schemars::Schema
35+
if let Ok(schema) = serde_json::to_value(schema) {
36+
if let Ok(transformed) = serde_json::from_value(schema) {
37+
*transform_schema = transformed;
38+
}
39+
}
40+
}
41+
}
42+
43+
fn hoist_properties_for_any_of_subschemas(kube_schema: &mut SchemaObject) {
1544
// Run some initial checks in case there is nothing to do
1645
let SchemaObject {
1746
subschemas: Some(subschemas),

kube-derive/src/custom_resource.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,10 @@ pub(crate) fn derive(input: proc_macro2::TokenStream) -> proc_macro2::TokenStrea
634634
s.meta_schema = None;
635635
})
636636
.with_transform(#schemars::transform::AddNullable::default())
637+
.with_transform(#kube_core::schema::OneOfSchemaRewriter)
638+
.with_transform(#kube_core::schema::AnyOfSchemaRewriter)
639+
.with_transform(#kube_core::schema::PropertiesSchemaRewriter)
640+
.with_transform(#kube_core::schema::OptionalEnumSchemaRewriter)
637641
.with_transform(#kube_core::schema::StructuralSchemaRewriter)
638642
.into_generator();
639643
let schema = generate.into_root_schema_for::<Self>();

0 commit comments

Comments
 (0)