export schema now stores possible values to serialize in top 'one_of' field, support for Unit like structs serialization"
This commit is contained in:
parent
e07b19510c
commit
f5cb1c78b0
@ -27,9 +27,9 @@ use serde_json::{Map, Value};
|
|||||||
use crate::{
|
use crate::{
|
||||||
error_codes,
|
error_codes,
|
||||||
schemas::{
|
schemas::{
|
||||||
json_schema::{JsonSchemaBevyType, SchemaMarker},
|
json_schema::{JsonSchemaBevyType, JsonSchemaVariant, SchemaMarker},
|
||||||
open_rpc::OpenRpcDocument,
|
open_rpc::OpenRpcDocument,
|
||||||
reflect_info::TypeDefinitionBuilder,
|
reflect_info::{TypeDefinitionBuilder, TypeReferencePath},
|
||||||
},
|
},
|
||||||
BrpError, BrpResult,
|
BrpError, BrpResult,
|
||||||
};
|
};
|
||||||
@ -1437,6 +1437,35 @@ fn export_registry_types_typed(
|
|||||||
Some((schema_id, Box::new(schema)))
|
Some((schema_id, Box::new(schema)))
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
schema.one_of = schema
|
||||||
|
.definitions
|
||||||
|
.iter()
|
||||||
|
.flat_map(|(id, schema)| {
|
||||||
|
if !schema.reflect_type_data.contains(&"Serialize".into())
|
||||||
|
|| !schema.reflect_type_data.contains(&"Deserialize".into())
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let schema = JsonSchemaBevyType {
|
||||||
|
properties: [(
|
||||||
|
schema.type_path.clone(),
|
||||||
|
JsonSchemaBevyType {
|
||||||
|
ref_type: Some(TypeReferencePath::definition(id.clone())),
|
||||||
|
description: schema.description.clone(),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
)]
|
||||||
|
.into(),
|
||||||
|
schema_type: Some(crate::schemas::json_schema::SchemaType::Object.into()),
|
||||||
|
additional_properties: Some(JsonSchemaVariant::BoolValue(false)),
|
||||||
|
description: schema.description.clone(),
|
||||||
|
reflect_type_data: schema.reflect_type_data.clone(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
Some(schema.into())
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
Ok(schema)
|
Ok(schema)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1680,9 +1709,9 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
use bevy_ecs::component::Component;
|
use bevy_ecs::component::Component;
|
||||||
use bevy_reflect::Reflect;
|
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
|
||||||
|
|
||||||
use crate::schemas::{reflect_info::TypeReferenceId, SchemaTypesMetadata};
|
use crate::schemas::SchemaTypesMetadata;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@ -1709,23 +1738,113 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature = "bevy_math")]
|
fn reflection_serialization_tests() {
|
||||||
fn export_schema_test() {
|
use bevy_ecs::resource::Resource;
|
||||||
#[derive(Reflect, Default, Deserialize, Serialize)]
|
|
||||||
|
#[derive(Reflect, Default, Deserialize, Serialize, Resource)]
|
||||||
|
#[reflect(Resource, Serialize, Deserialize)]
|
||||||
|
pub struct ResourceStruct {
|
||||||
|
/// FIELD DOC
|
||||||
|
pub field: String,
|
||||||
|
pub second_field: Option<(u8, Option<u16>)>,
|
||||||
|
}
|
||||||
|
#[derive(Reflect, Default, Deserialize, Serialize, Component)]
|
||||||
|
#[reflect(Component, Serialize, Deserialize)]
|
||||||
pub struct OtherStruct {
|
pub struct OtherStruct {
|
||||||
/// FIELD DOC
|
/// FIELD DOC
|
||||||
pub field: String,
|
pub field: String,
|
||||||
pub second_field: Option<(u8, u8)>,
|
pub second_field: Option<(u8, Option<u16>)>,
|
||||||
}
|
}
|
||||||
/// STRUCT DOC
|
/// STRUCT DOC
|
||||||
#[derive(Reflect, Default, Deserialize, Serialize)]
|
#[derive(Reflect, Default, Deserialize, Serialize, Component)]
|
||||||
pub struct SecondStruct {
|
#[reflect(Component, Serialize, Deserialize)]
|
||||||
pub field: String,
|
pub struct SecondStruct;
|
||||||
/// FIELD DOC
|
/// STRUCT DOC
|
||||||
|
#[derive(Reflect, Default, Deserialize, Serialize, Component)]
|
||||||
|
#[reflect(Component, Serialize, Deserialize)]
|
||||||
|
pub struct ThirdStruct {
|
||||||
|
pub array_strings: Vec<String>,
|
||||||
|
pub array_structs: [OtherStruct; 5],
|
||||||
|
// pub map_strings: HashMap<String, i32>,
|
||||||
|
}
|
||||||
|
#[derive(Reflect, Default, Deserialize, Serialize, Component)]
|
||||||
|
#[reflect(Component, Serialize, Deserialize)]
|
||||||
|
pub struct NestedStruct {
|
||||||
pub other: OtherStruct,
|
pub other: OtherStruct,
|
||||||
|
pub second: SecondStruct,
|
||||||
|
/// DOC FOR FIELD
|
||||||
|
pub third: ThirdStruct,
|
||||||
|
}
|
||||||
|
let atr = AppTypeRegistry::default();
|
||||||
|
{
|
||||||
|
let mut register = atr.write();
|
||||||
|
register.register::<NestedStruct>();
|
||||||
|
register.register::<ResourceStruct>();
|
||||||
|
}
|
||||||
|
let value = NestedStruct {
|
||||||
|
other: OtherStruct {
|
||||||
|
field: "S".into(),
|
||||||
|
second_field: Some((0, None)),
|
||||||
|
},
|
||||||
|
second: SecondStruct,
|
||||||
|
third: ThirdStruct {
|
||||||
|
array_strings: ["s".into(), "SS".into()].into(),
|
||||||
|
array_structs: [
|
||||||
|
OtherStruct::default(),
|
||||||
|
OtherStruct::default(),
|
||||||
|
OtherStruct::default(),
|
||||||
|
OtherStruct::default(),
|
||||||
|
OtherStruct::default(),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let mut world = World::new();
|
||||||
|
world.insert_resource(atr.clone());
|
||||||
|
world.insert_resource(SchemaTypesMetadata::default());
|
||||||
|
let response = export_registry_types_ext(BrpJsonSchemaQueryFilter::default(), &world);
|
||||||
|
eprintln!(
|
||||||
|
"{}",
|
||||||
|
serde_json::to_string_pretty(&response).unwrap_or_default()
|
||||||
|
);
|
||||||
|
let schema_value = serde_json::to_value(response).expect("Failed to serialize schema");
|
||||||
|
let type_registry = atr.read();
|
||||||
|
let serializer = ReflectSerializer::new(&value, &type_registry);
|
||||||
|
let res = ResourceStruct {
|
||||||
|
field: "SET".into(),
|
||||||
|
second_field: Some((45, None)),
|
||||||
|
};
|
||||||
|
let serializer_2 = ReflectSerializer::new(&res, &type_registry);
|
||||||
|
let json = serde_json::to_value(&serializer).expect("msg");
|
||||||
|
let validator = jsonschema::options()
|
||||||
|
.with_draft(jsonschema::Draft::Draft202012)
|
||||||
|
.build(&schema_value)
|
||||||
|
.expect("Failed to validate json schema");
|
||||||
|
assert!(
|
||||||
|
validator.validate(&json).is_ok(),
|
||||||
|
"Validation failed: {}",
|
||||||
|
serde_json::to_string_pretty(&serializer).expect("msg")
|
||||||
|
);
|
||||||
|
let json = serde_json::to_value(&serializer_2).expect("msg");
|
||||||
|
assert!(validator.validate(&json).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "bevy_math")]
|
||||||
|
fn export_schema_test() {
|
||||||
|
#[derive(Reflect, Default, Deserialize, Serialize, Component)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct OtherStruct {
|
||||||
|
/// FIELD DOC
|
||||||
|
pub field: String,
|
||||||
|
pub second_field: Option<(u8, Option<u16>)>,
|
||||||
}
|
}
|
||||||
/// STRUCT DOC
|
/// STRUCT DOC
|
||||||
#[derive(Reflect, Default, Deserialize, Serialize)]
|
#[derive(Reflect, Default, Deserialize, Serialize, Component)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct SecondStruct;
|
||||||
|
/// STRUCT DOC
|
||||||
|
#[derive(Reflect, Default, Deserialize, Serialize, Component)]
|
||||||
|
#[reflect(Component)]
|
||||||
pub struct ThirdStruct {
|
pub struct ThirdStruct {
|
||||||
pub array_strings: Vec<String>,
|
pub array_strings: Vec<String>,
|
||||||
pub array_structs: [OtherStruct; 5],
|
pub array_structs: [OtherStruct; 5],
|
||||||
@ -1740,7 +1859,6 @@ mod tests {
|
|||||||
pub third: ThirdStruct,
|
pub third: ThirdStruct,
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut world = World::new();
|
|
||||||
let atr = AppTypeRegistry::default();
|
let atr = AppTypeRegistry::default();
|
||||||
{
|
{
|
||||||
use crate::schemas::ReflectJsonSchemaForceAsArray;
|
use crate::schemas::ReflectJsonSchemaForceAsArray;
|
||||||
@ -1750,6 +1868,7 @@ mod tests {
|
|||||||
register.register::<bevy_math::Vec3>();
|
register.register::<bevy_math::Vec3>();
|
||||||
register.register_type_data::<bevy_math::Vec3, ReflectJsonSchemaForceAsArray>();
|
register.register_type_data::<bevy_math::Vec3, ReflectJsonSchemaForceAsArray>();
|
||||||
}
|
}
|
||||||
|
let mut world = World::new();
|
||||||
world.insert_resource(atr);
|
world.insert_resource(atr);
|
||||||
world.insert_resource(SchemaTypesMetadata::default());
|
world.insert_resource(SchemaTypesMetadata::default());
|
||||||
let response = export_registry_types_ext(BrpJsonSchemaQueryFilter::default(), &world);
|
let response = export_registry_types_ext(BrpJsonSchemaQueryFilter::default(), &world);
|
||||||
@ -1780,6 +1899,8 @@ mod tests {
|
|||||||
response.definitions.keys()
|
response.definitions.keys()
|
||||||
);
|
);
|
||||||
{
|
{
|
||||||
|
use crate::schemas::reflect_info::TypeReferenceId;
|
||||||
|
|
||||||
let first = response.definitions.iter().next().expect("Should have one");
|
let first = response.definitions.iter().next().expect("Should have one");
|
||||||
assert_eq!(first.0, &TypeReferenceId::from("glam::Vec3"));
|
assert_eq!(first.0, &TypeReferenceId::from("glam::Vec3"));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1043,7 +1043,17 @@ impl From<&InternalSchemaType> for Option<SchemaTypeVariant> {
|
|||||||
..
|
..
|
||||||
} => Some(SchemaTypeVariant::Single(*primitive)),
|
} => Some(SchemaTypeVariant::Single(*primitive)),
|
||||||
InternalSchemaType::Array { .. } => Some(SchemaTypeVariant::Single(SchemaType::Array)),
|
InternalSchemaType::Array { .. } => Some(SchemaTypeVariant::Single(SchemaType::Array)),
|
||||||
InternalSchemaType::FieldsHolder(fields) => match fields.fields_type {
|
InternalSchemaType::FieldsHolder(fields) => match &fields.fields_type {
|
||||||
|
s if fields.fields.is_empty() => {
|
||||||
|
let first = if s.eq(&FieldType::Named) {
|
||||||
|
SchemaType::Object
|
||||||
|
} else {
|
||||||
|
SchemaType::Array
|
||||||
|
};
|
||||||
|
Some(SchemaTypeVariant::Multiple(
|
||||||
|
[first, SchemaType::Null].into(),
|
||||||
|
))
|
||||||
|
}
|
||||||
FieldType::Named => Some(SchemaTypeVariant::Single(SchemaType::Object)),
|
FieldType::Named => Some(SchemaTypeVariant::Single(SchemaType::Object)),
|
||||||
FieldType::Unnamed if fields.fields.len() == 1 => {
|
FieldType::Unnamed if fields.fields.len() == 1 => {
|
||||||
let schema: SchemaType = fields.fields[0].type_id.into();
|
let schema: SchemaType = fields.fields[0].type_id.into();
|
||||||
@ -1270,6 +1280,9 @@ pub(crate) trait TypeDefinitionBuilder {
|
|||||||
schema: &mut JsonSchemaBevyType,
|
schema: &mut JsonSchemaBevyType,
|
||||||
info: &FieldsInformation,
|
info: &FieldsInformation,
|
||||||
) {
|
) {
|
||||||
|
if info.fields.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
match &info.fields_type {
|
match &info.fields_type {
|
||||||
FieldType::Named => {
|
FieldType::Named => {
|
||||||
schema.additional_properties = Some(JsonSchemaVariant::BoolValue(false));
|
schema.additional_properties = Some(JsonSchemaVariant::BoolValue(false));
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user