-
Notifications
You must be signed in to change notification settings - Fork 178
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
automatic generation of json schema for plugin config #537
Changes from all commits
77268f2
d2cf19a
3e8e5f3
46e9675
8477541
a3dd7b3
f7c0871
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,24 +12,27 @@ | |
// ZettaScale Zenoh Team, <[email protected]> | ||
// | ||
use derive_more::{AsMut, AsRef}; | ||
use schemars::JsonSchema; | ||
use serde_json::{Map, Value}; | ||
use std::convert::TryFrom; | ||
use std::time::Duration; | ||
use zenoh::{key_expr::keyexpr, prelude::OwnedKeyExpr, Result as ZResult}; | ||
use zenoh_result::{bail, zerror, Error}; | ||
|
||
#[derive(Debug, Clone, AsMut, AsRef)] | ||
#[derive(JsonSchema, Debug, Clone, AsMut, AsRef)] | ||
pub struct PluginConfig { | ||
pub name: String, | ||
pub required: bool, | ||
pub backend_search_dirs: Option<Vec<String>>, | ||
#[schemars(with = "Map<String, Value>")] | ||
pub volumes: Vec<VolumeConfig>, | ||
#[schemars(with = "Map<String, Value>")] | ||
pub storages: Vec<StorageConfig>, | ||
#[as_ref] | ||
#[as_mut] | ||
pub rest: Map<String, Value>, | ||
} | ||
#[derive(Debug, Clone, AsMut, AsRef)] | ||
#[derive(JsonSchema, Debug, Clone, AsMut, AsRef)] | ||
pub struct VolumeConfig { | ||
pub name: String, | ||
pub backend: Option<String>, | ||
|
@@ -39,7 +42,7 @@ pub struct VolumeConfig { | |
#[as_mut] | ||
pub rest: Map<String, Value>, | ||
} | ||
#[derive(Debug, Clone, PartialEq, Eq)] | ||
#[derive(JsonSchema, Debug, Clone, PartialEq, Eq)] | ||
pub struct StorageConfig { | ||
pub name: String, | ||
pub key_expr: OwnedKeyExpr, | ||
|
@@ -52,7 +55,7 @@ pub struct StorageConfig { | |
pub replica_config: Option<ReplicaConfig>, | ||
} | ||
// Note: All parameters should be same for replicas, else will result on huge overhead | ||
#[derive(Debug, Clone, PartialEq, Eq)] | ||
#[derive(JsonSchema, Debug, Clone, PartialEq, Eq)] | ||
pub struct ReplicaConfig { | ||
pub publication_interval: Duration, | ||
pub propagation_delay: Duration, | ||
|
@@ -78,7 +81,7 @@ impl Default for ReplicaConfig { | |
} | ||
|
||
// The configuration for periodic garbage collection of metadata in storage manager | ||
#[derive(Debug, Clone, PartialEq, Eq)] | ||
#[derive(JsonSchema, Debug, Clone, PartialEq, Eq)] | ||
pub struct GarbageCollectionConfig { | ||
// The duration between two garbage collection events | ||
// The garbage collection will be scheduled as a periodic event with this period | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,11 +11,35 @@ | |
// Contributors: | ||
// ZettaScale Zenoh Team, <[email protected]> | ||
// | ||
use schemars::schema_for; | ||
|
||
use crate::config::Config; | ||
|
||
#[path = "src/config.rs"] | ||
mod config; | ||
|
||
fn main() { | ||
// Add rustc version to zenohd | ||
let version_meta = rustc_version::version_meta().unwrap(); | ||
println!( | ||
"cargo:rustc-env=RUSTC_VERSION={}", | ||
version_meta.short_version_string | ||
); | ||
// Generate config schema | ||
let schema = schema_for!(Config); | ||
std::fs::write( | ||
"config_schema.json5", | ||
serde_json::to_string_pretty(&schema).unwrap(), | ||
) | ||
.unwrap(); | ||
// Check that the example config matches the schema | ||
let schema = std::fs::read_to_string("config_schema.json5").unwrap(); | ||
let schema: serde_json::Value = serde_json::from_str(&schema).unwrap(); | ||
let schema = jsonschema::JSONSchema::compile(&schema).unwrap(); | ||
let config = std::fs::read_to_string("config.json5").unwrap(); | ||
let config: serde_json::Value = serde_json::from_str(&config).unwrap(); | ||
if let Err(es) = schema.validate(&config) { | ||
let es = es.map(|e| format!("{}", e)).collect::<Vec<_>>().join("\n"); | ||
panic!("config.json5 schema validation error: {}", es); | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"http_port": "8080" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{ | ||
"$schema": "http://json-schema.org/draft-07/schema#", | ||
"title": "Config", | ||
"type": "object", | ||
"required": [ | ||
"http_port" | ||
], | ||
"properties": { | ||
"__path__": { | ||
"type": [ | ||
"string", | ||
"null" | ||
] | ||
}, | ||
"__required__": { | ||
"type": [ | ||
"boolean", | ||
"null" | ||
] | ||
}, | ||
"http_port": { | ||
"type": "string" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The generated schema here is incomplete: a number would also be accepted. Not sure it that's an issue for our purpose though There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This goes directly from definition of port field in zenoh-plugin-rest/config.rs. If we want to correct it, we should fix this type pub struct Config {
#[serde(deserialize_with = "deserialize_http_port")]
pub http_port: String,
__path__: Option<String>,
__required__: Option<bool>,
} |
||
} | ||
}, | ||
"additionalProperties": false | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,13 +11,14 @@ | |
// Contributors: | ||
// ZettaScale Zenoh Team, <[email protected]> | ||
// | ||
use schemars::JsonSchema; | ||
use serde::de::{Unexpected, Visitor}; | ||
use serde::{de, Deserialize, Deserializer}; | ||
use std::fmt; | ||
|
||
const DEFAULT_HTTP_INTERFACE: &str = "[::]"; | ||
|
||
#[derive(Deserialize, serde::Serialize, Clone, Debug)] | ||
#[derive(JsonSchema, Deserialize, serde::Serialize, Clone, Debug)] | ||
#[serde(deny_unknown_fields)] | ||
pub struct Config { | ||
#[serde(deserialize_with = "deserialize_http_port")] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,11 +11,31 @@ | |
// Contributors: | ||
// ZettaScale Zenoh Team, <[email protected]> | ||
// | ||
use schemars::schema_for; | ||
use zenoh_backend_traits::config::PluginConfig; | ||
|
||
fn main() { | ||
// Add rustc version to zenohd | ||
let version_meta = rustc_version::version_meta().unwrap(); | ||
println!( | ||
"cargo:rustc-env=RUSTC_VERSION={}", | ||
version_meta.short_version_string | ||
); | ||
// Generate default config schema | ||
let schema = schema_for!(PluginConfig); | ||
std::fs::write( | ||
"config_schema.json5", | ||
serde_json::to_string_pretty(&schema).unwrap(), | ||
) | ||
.unwrap(); | ||
// Check that the example config matches the schema | ||
let schema = std::fs::read_to_string("config_schema.json5").unwrap(); | ||
let schema: serde_json::Value = serde_json::from_str(&schema).unwrap(); | ||
let schema = jsonschema::JSONSchema::compile(&schema).unwrap(); | ||
let config = std::fs::read_to_string("config.json5").unwrap(); | ||
let config: serde_json::Value = serde_json::from_str(&config).unwrap(); | ||
if let Err(es) = schema.validate(&config) { | ||
let es = es.map(|e| format!("{}", e)).collect::<Vec<_>>().join("\n"); | ||
panic!("config.json5 schema validation error: {}", es); | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"volumes": {}, | ||
"storages": {}, | ||
"name" : "test", | ||
"required" : true, | ||
"rest": {} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
{ | ||
"$schema": "http://json-schema.org/draft-07/schema#", | ||
"title": "PluginConfig", | ||
"type": "object", | ||
"required": [ | ||
"name", | ||
"required", | ||
"rest", | ||
"storages", | ||
"volumes" | ||
], | ||
"properties": { | ||
"backend_search_dirs": { | ||
"type": [ | ||
"array", | ||
"null" | ||
], | ||
"items": { | ||
"type": "string" | ||
} | ||
}, | ||
"name": { | ||
"type": "string" | ||
}, | ||
"required": { | ||
"type": "boolean" | ||
}, | ||
"rest": { | ||
"type": "object", | ||
"additionalProperties": true | ||
}, | ||
"storages": { | ||
"type": "object", | ||
"additionalProperties": true | ||
}, | ||
"volumes": { | ||
"type": "object", | ||
"additionalProperties": true | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would have expected
{"type": "boolean", "required": false}
. Is this equivalent, or is the_required_
key now mandatory, but nullable?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need to check json schema standard, but at least example file without
__required__
field passes the check.I've added additional verification of sample config file against generated schema. Seems that serde is much less strict than formal schema validation. Sometimes it seems completely wrong: schema requires
volumes
andstorages
to be vectors, but our config sample shows them as objects: