BRP registry JSON schema endpoint#16882
Conversation
|
Do you think it will be possible to do the inverse as well eventually? Take a serialized map and load it into the registry? Maybe @MrGVSV knows. |
| "typeInfo": "Struct", | ||
| "long_name": t.type_path(), | ||
| "properties": properties, |
There was a problem hiding this comment.
Nit: Personally, I think these should follow the naming conventions in bevy_reflect.
So for example, typeInfo would be kind, long_name would be type_path, properties would be fields, etc.
| "typeInfo": "Struct", | ||
| "long_name": t.type_path(), | ||
| "properties": properties, | ||
| "additionalProperties": false, |
There was a problem hiding this comment.
I think this endpoint could use some documentation. At first glance, I'm not really sure what additionalProperties means.
There was a problem hiding this comment.
This one is described here: https://json-schema.org/draft/2020-12/json-schema-core#section-10.3.2.3
But I am not sure about one thing- does it apply only when patternProperties are present, or is it apply also when only properties are.
| "required": info | ||
| .iter() | ||
| .filter(|field| !field.type_path().starts_with("core::option::Option")) | ||
| .map(NamedField::name) | ||
| .collect::<Vec<_>>(), |
There was a problem hiding this comment.
Nit: Could we store a required property on the field itself rather than duplicating field names here?
There was a problem hiding this comment.
It is part of the standard: https://json-schema.org/draft/2020-12/json-schema-validation#section-6.5.3
| "additionalProperties": false, | ||
| "required": info | ||
| .iter() | ||
| .filter(|field| !field.type_path().starts_with("core::option::Option")) |
There was a problem hiding this comment.
I do think Option tends to imply optionality, but this might not always be true, especially when used by APIs to determine if a value can be "skipped" or not.
Ideally, Bevy would expose a common Required attribute that could be applied to required fields (or vice-versa if we want the default to be that all fields are required) like #[reflect(@Required)].
There was a problem hiding this comment.
What I would prefer to have right now is being sure that when we are not providing field etc. for value Rust is able to infer the default value, like when you specify the #[serde(default)] for a field. But I am not sure yet how to do it here.
| "long_name": t.type_path(), | ||
| "oneOf": info |
There was a problem hiding this comment.
Question: why are some property names camelCase and others snake_case?
| .map(|variant| match variant { | ||
| VariantInfo::Unit(v) => v.name(), | ||
| _ => unreachable!(), | ||
| }) |
There was a problem hiding this comment.
Nit: If you wanted you could also use variant.as_unit_variant().expect("variant is unit variant""). Although, it could be argued that the use of unreachable! is a bit clearer.
| "long_name": v.name(), | ||
| "short_name": v.name().split("::").last().unwrap_or(v.name()), |
There was a problem hiding this comment.
I could be wrong, but I think name is always just an identifier. Is this split actually necessary?
There was a problem hiding this comment.
Yeah, it doesn't seems to make sense, could be a result of moving code from some of the previous versions?
There was a problem hiding this comment.
Updated, now it's the other way around, I am adding base type path to the variant and combine them:
"typePath": format!("{}::{}", type_path, v.name()),
"shortPath": v.name(),| } | ||
|
|
||
| #[test] | ||
| fn reflect_export_test() { |
There was a problem hiding this comment.
It'd be great if we had some more tests for different kinds of types (e.g. enums, tuples, etc.).
Would also be helpful if one of the tests also tested the JSON output (i.e. compared the entire output to a string). This also helps to ensure that everything not only behaves correctly, but looks right too (and it's nice for for future devs to see what the result is actually supposed to look like).
There was a problem hiding this comment.
Agree, I will try to add some more tests.
I mean, maybe? I don't have too big knowledge about reflect in bevy but AFAIK there is a option to register type by providing |
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
I actually like the idea of having this sort of endpoint be separate from Creating the registry the other way around is going to be a bit more complex, involves working it into
So yeah, because of the complexity involved, I think it's better to work out solutions in |
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
|
The reason I ask is because bsn is reflection-backed and (as far as I can tell) can't even be parsed when it uses a type that's not defined locally. Alice was wondering if this would let us get around that. From that point of view, doing stuff entirely on BRP won't be useful unless bsn is backed by BRP instead of reflection. And that isn't on the table. |
|
@MrGVSV For naming I think we should just follow https://json-schema.org/understanding-json-schema/keywords when possible. But we are providing here some values that are not definied in JSON Schema, this is mostly the ones with snake case, so I think for those we could try to follow Bevy naming scheme, but make them camel case so they are consistent with the rest of the fields. |
I see. We can definitely work towards it, but I wasn't planning on trying to get these sort of major changes into 0.16 since reflection just had a massive overhaul in 0.15, and this would likely be another major change. My current goal for 0.16 is just cleanup work, general improvements/fixes, and maybe some cool mostly-additive features (maybe some relatively small breaking changes). We could try to do it, but ideally most of the work would be completed and merged early into the cycle, and we're already a month in with no progress made yet 😅 |
|
Yeah no worries, just wondering about how much effort it would take. After some further thought, it's probably simpler just to write a second bsn parser that does use brp/schemas instead of reflection, as you suggested. |
…sResource when value is false
|
I did update branch quite a bit, most important changes:
|
|
I've updated structs documentation, added filtering options to the query, removed Example: #[derive(
bevy_reflect::Reflect, bevy_ecs::system::Resource, Default, Deserialize, Serialize,
)]
#[reflect(Resource, Default, Serialize, Deserialize)]
struct Foo {
a: f32,
}Type schema: {
"shortPath": "Foo",
"typePath": "bevy_remote::builtin_methods::tests::Foo",
"modulePath": "bevy_remote::builtin_methods::tests",
"crateName": "bevy_remote",
"reflectTypes": [
"Resource",
"Default",
"Serialize",
"Deserialize"
],
"kind": "Struct",
"type": "object",
"additionalProperties": false,
"properties": {
"a": {
"type": {
"$ref": "#/$defs/f32"
}
}
},
"required": [
"a"
]
} |
|
@MrGVSV IMHO now besides the more tests it is quite done, let me know how you feel about it. |
# Objective Resolve bevyengine#16745 ## Solution Provide a way to map `AppTypeRegistry` types into a JSON Schema that can be used in other applications. I took code from https://github.com/kaosat-dev/Blenvy as a starting point, cleaned up and adapter more for `bevy_remote` needs. Based on feedback and needs it could be improved, I could add some filtering options, etc. ## Testing - I was comparing results with the ones from code in `blenvy` - There is added unit test, could be added more - I was testing it in my game with this code: ```rust fn types_to_file(world: &mut World) { use bevy_remote::builtin_methods::export_registry_types; let Ok(Ok(types_schema)) = world.run_system_cached_with(export_registry_types, None) else { return; }; let registry_save_path = std::path::Path::new("assets").join("registry.json"); let writer = std::fs::File::create(registry_save_path).expect("should have created schema file"); serde_json::to_writer_pretty(writer, &types_schema).expect("Failed to save types to file"); } ``` It can be run by adding it at startup ```rust app.add_systems(Startup, types_to_file); ``` --------- Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
# Objective Resolve bevyengine#16745 ## Solution Provide a way to map `AppTypeRegistry` types into a JSON Schema that can be used in other applications. I took code from https://github.com/kaosat-dev/Blenvy as a starting point, cleaned up and adapter more for `bevy_remote` needs. Based on feedback and needs it could be improved, I could add some filtering options, etc. ## Testing - I was comparing results with the ones from code in `blenvy` - There is added unit test, could be added more - I was testing it in my game with this code: ```rust fn types_to_file(world: &mut World) { use bevy_remote::builtin_methods::export_registry_types; let Ok(Ok(types_schema)) = world.run_system_cached_with(export_registry_types, None) else { return; }; let registry_save_path = std::path::Path::new("assets").join("registry.json"); let writer = std::fs::File::create(registry_save_path).expect("should have created schema file"); serde_json::to_writer_pretty(writer, &types_schema).expect("Failed to save types to file"); } ``` It can be run by adding it at startup ```rust app.add_systems(Startup, types_to_file); ``` --------- Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
Objective
Resolve #16745
Solution
Provide a way to map
AppTypeRegistrytypes into a JSON Schema that can be used in other applications. I took code from https://github.com/kaosat-dev/Blenvy as a starting point, cleaned up and adapter more forbevy_remoteneeds. Based on feedback and needs it could be improved, I could add some filtering options, etc.Testing
blenvyIt can be run by adding it at startup