Skip to main content

roder_protocol/
schema.rs

1use serde_json::{Value, json};
2
3use crate::methods::{AppServerMethodManifest, app_server_method_manifest};
4
5pub fn app_server_manifest_json() -> Value {
6    serde_json::to_value(app_server_method_manifest()).expect("manifest serializes")
7}
8
9pub fn app_server_json_schema() -> Value {
10    let manifest = app_server_method_manifest();
11    json!({
12        "$schema": "https://json-schema.org/draft/2020-12/schema",
13        "$id": "https://roder.dev/schemas/app-server/roder-app-server.v1.json",
14        "title": "Roder App Server Manifest",
15        "type": "object",
16        "required": ["schemaVersion", "unknownMethodsAllowed", "methods"],
17        "properties": {
18            "schemaVersion": { "const": manifest.schema_version },
19            "unknownMethodsAllowed": { "type": "boolean" },
20            "methods": {
21                "type": "array",
22                "items": method_spec_schema(&manifest),
23            },
24        },
25        "additionalProperties": false,
26    })
27}
28
29fn method_spec_schema(manifest: &AppServerMethodManifest) -> Value {
30    let methods = manifest
31        .methods
32        .iter()
33        .map(|spec| spec.method)
34        .collect::<Vec<_>>();
35    json!({
36        "type": "object",
37        "required": [
38            "method",
39            "paramsType",
40            "resultType",
41            "stability",
42            "featureGroup",
43            "idempotency",
44            "sideEffect",
45        ],
46        "properties": {
47            "method": { "type": "string", "enum": methods },
48            "paramsType": { "type": "string" },
49            "resultType": { "type": "string" },
50            "stability": { "enum": ["stable", "experimental"] },
51            "featureGroup": { "type": "string" },
52            "idempotency": { "enum": ["idempotent", "nonIdempotent"] },
53            "sideEffect": { "enum": ["readOnly", "localState", "externalProcess"] },
54            "notifications": {
55                "type": "array",
56                "items": { "type": "string" },
57            },
58        },
59        "additionalProperties": false,
60    })
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    #[test]
68    fn schema_contains_manifest_version_and_method_enum() {
69        let schema = app_server_json_schema();
70        assert_eq!(schema["properties"]["schemaVersion"]["const"], 1);
71        let method_enum = schema["properties"]["methods"]["items"]["properties"]["method"]["enum"]
72            .as_array()
73            .expect("method enum");
74        assert!(method_enum.iter().any(|value| value == "thread/start"));
75        assert!(method_enum.iter().any(|value| value == "memory/query"));
76    }
77
78    #[test]
79    fn manifest_json_allows_unknown_methods_for_raw_clients() {
80        let manifest = app_server_manifest_json();
81        assert_eq!(manifest["unknownMethodsAllowed"], true);
82        assert!(manifest["methods"].as_array().unwrap().len() > 100);
83    }
84
85    #[test]
86    fn checked_app_server_schema_file_matches_generator() {
87        let checked: Value = serde_json::from_str(include_str!(
88            "../../../schemas/app-server/methods.schema.json"
89        ))
90        .expect("checked schema json");
91        assert_eq!(checked, app_server_json_schema());
92    }
93
94    #[test]
95    fn checked_app_server_manifest_file_matches_generator() {
96        let checked: Value = serde_json::from_str(include_str!(
97            "../../../schemas/app-server/roder-app-server.v1.json"
98        ))
99        .expect("checked manifest json");
100        assert_eq!(checked, app_server_manifest_json());
101    }
102}