ReflectJsonSchema for custom schemas
This commit is contained in:
parent
8e4809f123
commit
77c9da49d6
@ -518,6 +518,8 @@ impl Default for RemotePlugin {
|
||||
|
||||
impl Plugin for RemotePlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.register_type_data::<schemas::open_rpc::OpenRpcDocument, schemas::ReflectJsonSchema>();
|
||||
|
||||
let mut remote_methods = RemoteMethods::new();
|
||||
|
||||
let plugin_methods = &mut *self.methods.write().unwrap();
|
||||
|
@ -11,7 +11,7 @@ use serde_json::Value;
|
||||
|
||||
use crate::schemas::{
|
||||
reflect_info::{SchemaInfoReflect, SchemaNumber},
|
||||
SchemaTypesMetadata,
|
||||
ReflectJsonSchema, SchemaTypesMetadata,
|
||||
};
|
||||
|
||||
/// Helper trait for converting `TypeRegistration` to `JsonSchemaBevyType`
|
||||
@ -68,6 +68,9 @@ impl TypeRegistrySchemaReader for TypeRegistry {
|
||||
impl From<(&TypeRegistration, &SchemaTypesMetadata)> for JsonSchemaBevyType {
|
||||
fn from(value: (&TypeRegistration, &SchemaTypesMetadata)) -> Self {
|
||||
let (reg, metadata) = value;
|
||||
if let Some(s) = reg.data::<ReflectJsonSchema>() {
|
||||
return s.0.clone();
|
||||
}
|
||||
let type_info = reg.type_info();
|
||||
let base_schema = type_info.build_schema();
|
||||
|
||||
@ -83,9 +86,14 @@ impl From<(&TypeRegistration, &SchemaTypesMetadata)> for JsonSchemaBevyType {
|
||||
/// It tries to follow this standard: <https://json-schema.org/specification>
|
||||
///
|
||||
/// To take the full advantage from info provided by Bevy registry it provides extra fields
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default, Reflect)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct JsonSchemaBevyType {
|
||||
/// JSON Schema specific field.
|
||||
/// This keyword is used to reference a statically identified schema.
|
||||
#[serde(rename = "$ref")]
|
||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||
pub ref_type: Option<String>,
|
||||
/// Bevy specific field, short path of the type.
|
||||
#[serde(skip_serializing_if = "String::is_empty", default)]
|
||||
pub short_path: String,
|
||||
@ -193,6 +201,7 @@ pub struct JsonSchemaBevyType {
|
||||
pub description: Option<String>,
|
||||
/// Default value for the schema.
|
||||
#[serde(skip_serializing_if = "Option::is_none", default, rename = "default")]
|
||||
#[reflect(ignore)]
|
||||
pub default_value: Option<Value>,
|
||||
}
|
||||
|
||||
@ -251,7 +260,7 @@ impl JsonSchemaVariant {
|
||||
}
|
||||
|
||||
/// Kind of json schema, maps [`TypeInfo`] type
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default, Reflect)]
|
||||
pub enum SchemaKind {
|
||||
/// Struct
|
||||
#[default]
|
||||
@ -528,6 +537,48 @@ mod tests {
|
||||
assert!(schema.one_of.len() == 3, "Should have 3 possible schemas");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reflect_export_with_custom_schema() {
|
||||
#[derive(Reflect, Component)]
|
||||
struct SomeType;
|
||||
|
||||
impl bevy_reflect::FromType<SomeType> for ReflectJsonSchema {
|
||||
fn from_type() -> Self {
|
||||
JsonSchemaBevyType {
|
||||
ref_type: Some(
|
||||
"https://raw.githubusercontent.com/open-rpc/meta-schema/master/schema.json"
|
||||
.into(),
|
||||
),
|
||||
description: Some("Custom type for testing purposes.".to_string()),
|
||||
..Default::default()
|
||||
}
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
let atr = AppTypeRegistry::default();
|
||||
{
|
||||
let mut register = atr.write();
|
||||
register.register::<SomeType>();
|
||||
register.register_type_data::<SomeType, ReflectJsonSchema>();
|
||||
}
|
||||
let type_registry = atr.read();
|
||||
let schema = type_registry
|
||||
.export_type_json_schema::<SomeType>(&SchemaTypesMetadata::default())
|
||||
.expect("Failed to export");
|
||||
assert!(
|
||||
!schema.reflect_types.contains(&"Component".to_owned()),
|
||||
"Should not be a component"
|
||||
);
|
||||
assert!(
|
||||
schema.ref_type.is_some_and(|t| !t.is_empty()),
|
||||
"Should have a reference type"
|
||||
);
|
||||
assert!(
|
||||
schema.description.is_some_and(|t| !t.is_empty()),
|
||||
"Should have a description"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn reflect_export_tuple_struct() {
|
||||
#[derive(Reflect, Component, Default, Deserialize, Serialize)]
|
||||
|
@ -1,4 +1,5 @@
|
||||
//! Module with schemas used for various BRP endpoints
|
||||
use bevy_derive::Deref;
|
||||
use bevy_ecs::{
|
||||
reflect::{ReflectComponent, ReflectResource},
|
||||
resource::Resource,
|
||||
@ -10,6 +11,8 @@ use bevy_reflect::{
|
||||
};
|
||||
use core::any::TypeId;
|
||||
|
||||
use crate::schemas::json_schema::JsonSchemaBevyType;
|
||||
|
||||
pub mod json_schema;
|
||||
pub mod open_rpc;
|
||||
pub mod reflect_info;
|
||||
@ -23,6 +26,22 @@ pub struct SchemaTypesMetadata {
|
||||
pub type_data_map: HashMap<TypeId, String>,
|
||||
}
|
||||
|
||||
/// Reflect-compatible custom JSON Schema for this type
|
||||
#[derive(Clone, Deref)]
|
||||
pub struct ReflectJsonSchema(pub JsonSchemaBevyType);
|
||||
|
||||
impl From<&JsonSchemaBevyType> for ReflectJsonSchema {
|
||||
fn from(schema: &JsonSchemaBevyType) -> Self {
|
||||
Self(schema.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<JsonSchemaBevyType> for ReflectJsonSchema {
|
||||
fn from(schema: JsonSchemaBevyType) -> Self {
|
||||
Self(schema)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SchemaTypesMetadata {
|
||||
fn default() -> Self {
|
||||
let mut data_types = Self {
|
||||
|
@ -1,15 +1,16 @@
|
||||
//! Module with trimmed down `OpenRPC` document structs.
|
||||
//! It tries to follow this standard: <https://spec.open-rpc.org>
|
||||
use bevy_platform::collections::HashMap;
|
||||
use bevy_reflect::{FromType, Reflect};
|
||||
use bevy_utils::default;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::RemoteMethods;
|
||||
use crate::{schemas::ReflectJsonSchema, RemoteMethods};
|
||||
|
||||
use super::json_schema::JsonSchemaBevyType;
|
||||
|
||||
/// Represents an `OpenRPC` document as defined by the `OpenRPC` specification.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize, Reflect)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct OpenRpcDocument {
|
||||
/// The version of the `OpenRPC` specification being used.
|
||||
@ -22,8 +23,24 @@ pub struct OpenRpcDocument {
|
||||
pub servers: Option<Vec<ServerObject>>,
|
||||
}
|
||||
|
||||
impl FromType<OpenRpcDocument> for ReflectJsonSchema {
|
||||
fn from_type() -> Self {
|
||||
JsonSchemaBevyType {
|
||||
ref_type: Some(
|
||||
"https://raw.githubusercontent.com/open-rpc/meta-schema/master/schema.json".into(),
|
||||
),
|
||||
description: Some(
|
||||
"Represents an `OpenRPC` document as defined by the `OpenRPC` specification."
|
||||
.to_string(),
|
||||
),
|
||||
..default()
|
||||
}
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains metadata information about the `OpenRPC` document.
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[derive(Serialize, Deserialize, Debug, Reflect)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct InfoObject {
|
||||
/// The title of the API or document.
|
||||
@ -35,6 +52,7 @@ pub struct InfoObject {
|
||||
pub description: Option<String>,
|
||||
/// A collection of custom extension fields.
|
||||
#[serde(flatten)]
|
||||
#[reflect(ignore)]
|
||||
pub extensions: HashMap<String, serde_json::Value>,
|
||||
}
|
||||
|
||||
@ -50,7 +68,7 @@ impl Default for InfoObject {
|
||||
}
|
||||
|
||||
/// Describes a server hosting the API as specified in the `OpenRPC` document.
|
||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||
#[derive(Serialize, Deserialize, Debug, Default, Reflect)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ServerObject {
|
||||
/// The name of the server.
|
||||
@ -62,11 +80,12 @@ pub struct ServerObject {
|
||||
pub description: Option<String>,
|
||||
/// Additional custom extension fields.
|
||||
#[serde(flatten)]
|
||||
#[reflect(ignore)]
|
||||
pub extensions: HashMap<String, serde_json::Value>,
|
||||
}
|
||||
|
||||
/// Represents an RPC method in the `OpenRPC` document.
|
||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||
#[derive(Serialize, Deserialize, Debug, Default, Reflect)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MethodObject {
|
||||
/// The method name (e.g., "/bevy/get")
|
||||
@ -85,11 +104,12 @@ pub struct MethodObject {
|
||||
// pub result: Option<Parameter>,
|
||||
/// Additional custom extension fields.
|
||||
#[serde(flatten)]
|
||||
#[reflect(ignore)]
|
||||
pub extensions: HashMap<String, serde_json::Value>,
|
||||
}
|
||||
|
||||
/// Represents an RPC method parameter in the `OpenRPC` document.
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[derive(Serialize, Deserialize, Debug, Reflect)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Parameter {
|
||||
/// Parameter name
|
||||
@ -101,6 +121,7 @@ pub struct Parameter {
|
||||
pub schema: JsonSchemaBevyType,
|
||||
/// Additional custom extension fields.
|
||||
#[serde(flatten)]
|
||||
#[reflect(ignore)]
|
||||
pub extensions: HashMap<String, serde_json::Value>,
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user