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}