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 {
|
impl Plugin for RemotePlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
|
app.register_type_data::<schemas::open_rpc::OpenRpcDocument, schemas::ReflectJsonSchema>();
|
||||||
|
|
||||||
let mut remote_methods = RemoteMethods::new();
|
let mut remote_methods = RemoteMethods::new();
|
||||||
|
|
||||||
let plugin_methods = &mut *self.methods.write().unwrap();
|
let plugin_methods = &mut *self.methods.write().unwrap();
|
||||||
|
|||||||
@ -11,7 +11,7 @@ use serde_json::Value;
|
|||||||
|
|
||||||
use crate::schemas::{
|
use crate::schemas::{
|
||||||
reflect_info::{SchemaInfoReflect, SchemaNumber},
|
reflect_info::{SchemaInfoReflect, SchemaNumber},
|
||||||
SchemaTypesMetadata,
|
ReflectJsonSchema, SchemaTypesMetadata,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Helper trait for converting `TypeRegistration` to `JsonSchemaBevyType`
|
/// Helper trait for converting `TypeRegistration` to `JsonSchemaBevyType`
|
||||||
@ -68,6 +68,9 @@ impl TypeRegistrySchemaReader for TypeRegistry {
|
|||||||
impl From<(&TypeRegistration, &SchemaTypesMetadata)> for JsonSchemaBevyType {
|
impl From<(&TypeRegistration, &SchemaTypesMetadata)> for JsonSchemaBevyType {
|
||||||
fn from(value: (&TypeRegistration, &SchemaTypesMetadata)) -> Self {
|
fn from(value: (&TypeRegistration, &SchemaTypesMetadata)) -> Self {
|
||||||
let (reg, metadata) = value;
|
let (reg, metadata) = value;
|
||||||
|
if let Some(s) = reg.data::<ReflectJsonSchema>() {
|
||||||
|
return s.0.clone();
|
||||||
|
}
|
||||||
let type_info = reg.type_info();
|
let type_info = reg.type_info();
|
||||||
let base_schema = type_info.build_schema();
|
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>
|
/// 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
|
/// 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")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct JsonSchemaBevyType {
|
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.
|
/// Bevy specific field, short path of the type.
|
||||||
#[serde(skip_serializing_if = "String::is_empty", default)]
|
#[serde(skip_serializing_if = "String::is_empty", default)]
|
||||||
pub short_path: String,
|
pub short_path: String,
|
||||||
@ -193,6 +201,7 @@ pub struct JsonSchemaBevyType {
|
|||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
/// Default value for the schema.
|
/// Default value for the schema.
|
||||||
#[serde(skip_serializing_if = "Option::is_none", default, rename = "default")]
|
#[serde(skip_serializing_if = "Option::is_none", default, rename = "default")]
|
||||||
|
#[reflect(ignore)]
|
||||||
pub default_value: Option<Value>,
|
pub default_value: Option<Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,7 +260,7 @@ impl JsonSchemaVariant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Kind of json schema, maps [`TypeInfo`] type
|
/// 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 {
|
pub enum SchemaKind {
|
||||||
/// Struct
|
/// Struct
|
||||||
#[default]
|
#[default]
|
||||||
@ -528,6 +537,48 @@ mod tests {
|
|||||||
assert!(schema.one_of.len() == 3, "Should have 3 possible schemas");
|
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]
|
#[test]
|
||||||
fn reflect_export_tuple_struct() {
|
fn reflect_export_tuple_struct() {
|
||||||
#[derive(Reflect, Component, Default, Deserialize, Serialize)]
|
#[derive(Reflect, Component, Default, Deserialize, Serialize)]
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
//! Module with schemas used for various BRP endpoints
|
//! Module with schemas used for various BRP endpoints
|
||||||
|
use bevy_derive::Deref;
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
reflect::{ReflectComponent, ReflectResource},
|
reflect::{ReflectComponent, ReflectResource},
|
||||||
resource::Resource,
|
resource::Resource,
|
||||||
@ -10,6 +11,8 @@ use bevy_reflect::{
|
|||||||
};
|
};
|
||||||
use core::any::TypeId;
|
use core::any::TypeId;
|
||||||
|
|
||||||
|
use crate::schemas::json_schema::JsonSchemaBevyType;
|
||||||
|
|
||||||
pub mod json_schema;
|
pub mod json_schema;
|
||||||
pub mod open_rpc;
|
pub mod open_rpc;
|
||||||
pub mod reflect_info;
|
pub mod reflect_info;
|
||||||
@ -23,6 +26,22 @@ pub struct SchemaTypesMetadata {
|
|||||||
pub type_data_map: HashMap<TypeId, String>,
|
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 {
|
impl Default for SchemaTypesMetadata {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let mut data_types = Self {
|
let mut data_types = Self {
|
||||||
|
|||||||
@ -1,15 +1,16 @@
|
|||||||
//! Module with trimmed down `OpenRPC` document structs.
|
//! Module with trimmed down `OpenRPC` document structs.
|
||||||
//! It tries to follow this standard: <https://spec.open-rpc.org>
|
//! It tries to follow this standard: <https://spec.open-rpc.org>
|
||||||
use bevy_platform::collections::HashMap;
|
use bevy_platform::collections::HashMap;
|
||||||
|
use bevy_reflect::{FromType, Reflect};
|
||||||
use bevy_utils::default;
|
use bevy_utils::default;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::RemoteMethods;
|
use crate::{schemas::ReflectJsonSchema, RemoteMethods};
|
||||||
|
|
||||||
use super::json_schema::JsonSchemaBevyType;
|
use super::json_schema::JsonSchemaBevyType;
|
||||||
|
|
||||||
/// Represents an `OpenRPC` document as defined by the `OpenRPC` specification.
|
/// Represents an `OpenRPC` document as defined by the `OpenRPC` specification.
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize, Reflect)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct OpenRpcDocument {
|
pub struct OpenRpcDocument {
|
||||||
/// The version of the `OpenRPC` specification being used.
|
/// The version of the `OpenRPC` specification being used.
|
||||||
@ -22,8 +23,24 @@ pub struct OpenRpcDocument {
|
|||||||
pub servers: Option<Vec<ServerObject>>,
|
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.
|
/// Contains metadata information about the `OpenRPC` document.
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug, Reflect)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct InfoObject {
|
pub struct InfoObject {
|
||||||
/// The title of the API or document.
|
/// The title of the API or document.
|
||||||
@ -35,6 +52,7 @@ pub struct InfoObject {
|
|||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
/// A collection of custom extension fields.
|
/// A collection of custom extension fields.
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
#[reflect(ignore)]
|
||||||
pub extensions: HashMap<String, serde_json::Value>,
|
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.
|
/// 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")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct ServerObject {
|
pub struct ServerObject {
|
||||||
/// The name of the server.
|
/// The name of the server.
|
||||||
@ -62,11 +80,12 @@ pub struct ServerObject {
|
|||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
/// Additional custom extension fields.
|
/// Additional custom extension fields.
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
#[reflect(ignore)]
|
||||||
pub extensions: HashMap<String, serde_json::Value>,
|
pub extensions: HashMap<String, serde_json::Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents an RPC method in the `OpenRPC` document.
|
/// Represents an RPC method in the `OpenRPC` document.
|
||||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
#[derive(Serialize, Deserialize, Debug, Default, Reflect)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct MethodObject {
|
pub struct MethodObject {
|
||||||
/// The method name (e.g., "/bevy/get")
|
/// The method name (e.g., "/bevy/get")
|
||||||
@ -85,11 +104,12 @@ pub struct MethodObject {
|
|||||||
// pub result: Option<Parameter>,
|
// pub result: Option<Parameter>,
|
||||||
/// Additional custom extension fields.
|
/// Additional custom extension fields.
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
#[reflect(ignore)]
|
||||||
pub extensions: HashMap<String, serde_json::Value>,
|
pub extensions: HashMap<String, serde_json::Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents an RPC method parameter in the `OpenRPC` document.
|
/// Represents an RPC method parameter in the `OpenRPC` document.
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug, Reflect)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Parameter {
|
pub struct Parameter {
|
||||||
/// Parameter name
|
/// Parameter name
|
||||||
@ -101,6 +121,7 @@ pub struct Parameter {
|
|||||||
pub schema: JsonSchemaBevyType,
|
pub schema: JsonSchemaBevyType,
|
||||||
/// Additional custom extension fields.
|
/// Additional custom extension fields.
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
#[reflect(ignore)]
|
||||||
pub extensions: HashMap<String, serde_json::Value>,
|
pub extensions: HashMap<String, serde_json::Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user