trailbase_schema/
registry.rs1use jsonschema::Validator;
2use lazy_static::lazy_static;
3use schemars::schema_for;
4use std::collections::HashMap;
5use std::sync::Arc;
6use trailbase_extension::jsonschema::SchemaEntry;
7
8use crate::error::Error;
9use crate::file::{FileUpload, FileUploads};
10
11fn builtin_schemas() -> &'static HashMap<String, SchemaEntry> {
12 fn validate_mime_type(value: &serde_json::Value, extra_args: Option<&str>) -> bool {
13 let Some(valid_mime_types) = extra_args else {
14 return true;
15 };
16
17 if let serde_json::Value::Object(map) = value {
18 if let Some(serde_json::Value::String(mime_type)) = map.get("mime_type") {
19 if valid_mime_types.contains(mime_type) {
20 return true;
21 }
22 }
23 }
24
25 return false;
26 }
27
28 lazy_static! {
29 static ref builtins: HashMap<String, SchemaEntry> = HashMap::<String, SchemaEntry>::from([
30 (
31 "std.FileUpload".to_string(),
32 SchemaEntry::from(
33 serde_json::to_value(schema_for!(FileUpload)).expect("infallible"),
34 Some(Arc::new(validate_mime_type))
35 )
36 .expect("infallible")
37 ),
38 (
39 "std.FileUploads".to_string(),
40 SchemaEntry::from(
41 serde_json::to_value(schema_for!(FileUploads)).expect("infallible"),
42 None
43 )
44 .expect("infallible"),
45 )
46 ]);
47 }
48
49 return &builtins;
50}
51
52#[derive(Debug, Clone)]
53pub struct Schema {
54 pub name: String,
55 pub schema: serde_json::Value,
56 pub builtin: bool,
57}
58
59pub fn get_schema(name: &str) -> Option<Schema> {
60 let builtins = builtin_schemas();
61
62 trailbase_extension::jsonschema::get_schema(name).map(|s| Schema {
63 name: name.to_string(),
64 schema: s,
65 builtin: builtins.contains_key(name),
66 })
67}
68
69pub fn get_compiled_schema(name: &str) -> Option<Arc<Validator>> {
70 trailbase_extension::jsonschema::get_compiled_schema(name)
71}
72
73pub fn get_schemas() -> Vec<Schema> {
74 let builtins = builtin_schemas();
75 return trailbase_extension::jsonschema::get_schemas()
76 .into_iter()
77 .map(|(name, value)| {
78 let builtin = builtins.contains_key(&name);
79 return Schema {
80 name,
81 schema: value,
82 builtin,
83 };
84 })
85 .collect();
86}
87
88pub fn set_user_schema(name: &str, pattern: Option<serde_json::Value>) -> Result<(), Error> {
89 let builtins = builtin_schemas();
90 if builtins.contains_key(name) {
91 return Err(Error::BuiltinSchema);
92 }
93
94 if let Some(p) = pattern {
95 let entry = SchemaEntry::from(p, None).map_err(|err| Error::JsonSchema(err.into()))?;
96 trailbase_extension::jsonschema::set_schema(name, Some(entry));
97 } else {
98 trailbase_extension::jsonschema::set_schema(name, None);
99 }
100
101 return Ok(());
102}
103
104lazy_static! {
105 static ref INIT: parking_lot::Mutex<bool> = parking_lot::Mutex::new(false);
106}
107
108pub fn set_user_schemas(schemas: Vec<(String, serde_json::Value)>) -> Result<(), Error> {
109 let mut entries: Vec<(String, SchemaEntry)> = vec![];
110 for (name, entry) in builtin_schemas() {
111 entries.push((name.clone(), entry.clone()));
112 }
113
114 for (name, schema) in schemas {
115 entries.push((
116 name,
117 SchemaEntry::from(schema, None).map_err(|err| Error::JsonSchema(err.into()))?,
118 ));
119 }
120
121 trailbase_extension::jsonschema::set_schemas(Some(entries));
122
123 *INIT.lock() = true;
124
125 return Ok(());
126}
127
128pub fn try_init_schemas() {
129 let mut init = INIT.lock();
130
131 if !*init {
132 let entries = builtin_schemas()
133 .iter()
134 .map(|(name, entry)| (name.clone(), entry.clone()))
135 .collect::<Vec<_>>();
136
137 trailbase_extension::jsonschema::set_schemas(Some(entries));
138 *init = true;
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use serde_json::json;
145
146 use super::*;
147
148 #[test]
149 fn test_builtin_schemas() {
150 assert!(builtin_schemas().len() > 0);
151
152 for (name, schema) in builtin_schemas() {
153 trailbase_extension::jsonschema::set_schema(&name, Some(schema.clone()));
154 }
155
156 {
157 let schema = get_schema("std.FileUpload").unwrap();
158 let compiled_schema = Validator::new(&schema.schema).unwrap();
159 let input = json!({
160 "id": "foo",
161 "mime_type": "my_foo",
162 });
163 if let Err(err) = compiled_schema.validate(&input) {
164 panic!("{err:?}");
165 };
166 }
167
168 {
169 let schema = get_schema("std.FileUploads").unwrap();
170 let compiled_schema = Validator::new(&schema.schema).unwrap();
171 assert!(
172 compiled_schema
173 .validate(&json!([
174 {
175 "id": "foo0",
176 "mime_type": "my_foo0",
177 },
178 {
179 "id": "foo1",
180 "mime_type": "my_foo1",
181 },
182 ]))
183 .is_ok()
184 );
185 }
186 }
187}