turul_http_mcp_server/middleware/
session_view_adapter.rs1use async_trait::async_trait;
8use serde_json::Value;
9use std::sync::Arc;
10use turul_mcp_session_storage::{BoxedSessionStorage, SessionView};
11
12pub struct StorageBackedSessionView {
45 session_id: String,
46 storage: Arc<BoxedSessionStorage>,
47}
48
49impl StorageBackedSessionView {
50 pub fn new(session_id: String, storage: Arc<BoxedSessionStorage>) -> Self {
57 Self {
58 session_id,
59 storage,
60 }
61 }
62}
63
64#[async_trait]
65impl SessionView for StorageBackedSessionView {
66 fn session_id(&self) -> &str {
67 &self.session_id
68 }
69
70 async fn get_state(&self, key: &str) -> Result<Option<Value>, String> {
71 let session = self
73 .storage
74 .get_session(&self.session_id)
75 .await
76 .map_err(|e| format!("Failed to get session: {}", e))?;
77
78 Ok(session.and_then(|s| s.state.get(key).cloned()))
80 }
81
82 async fn set_state(&self, key: &str, value: Value) -> Result<(), String> {
83 let mut session = self
85 .storage
86 .get_session(&self.session_id)
87 .await
88 .map_err(|e| format!("Failed to get session: {}", e))?
89 .ok_or_else(|| format!("Session '{}' not found", self.session_id))?;
90
91 session.state.insert(key.to_string(), value);
93
94 session.last_activity = chrono::Utc::now().timestamp_millis() as u64;
96
97 self.storage
99 .update_session(session)
100 .await
101 .map_err(|e| format!("Failed to update session: {}", e))
102 }
103
104 async fn get_metadata(&self, key: &str) -> Result<Option<Value>, String> {
105 let prefixed_key = format!("__meta__:{}", key);
108
109 let session = self
110 .storage
111 .get_session(&self.session_id)
112 .await
113 .map_err(|e| format!("Failed to get session: {}", e))?;
114
115 Ok(session.and_then(|s| s.metadata.get(&prefixed_key).cloned()))
116 }
117
118 async fn set_metadata(&self, key: &str, value: Value) -> Result<(), String> {
119 let prefixed_key = format!("__meta__:{}", key);
121
122 let mut session = self
123 .storage
124 .get_session(&self.session_id)
125 .await
126 .map_err(|e| format!("Failed to get session: {}", e))?
127 .ok_or_else(|| format!("Session '{}' not found", self.session_id))?;
128
129 session.metadata.insert(prefixed_key, value);
131
132 session.last_activity = chrono::Utc::now().timestamp_millis() as u64;
134
135 self.storage
137 .update_session(session)
138 .await
139 .map_err(|e| format!("Failed to update session: {}", e))
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146 use serde_json::json;
147 use turul_mcp_protocol::ServerCapabilities;
148 use turul_mcp_session_storage::{BoxedSessionStorage, InMemorySessionStorage};
149
150 #[tokio::test]
151 async fn test_session_view_state() {
152 let storage: Arc<BoxedSessionStorage> = Arc::new(InMemorySessionStorage::new());
153
154 let session_info = storage
156 .create_session(ServerCapabilities::default())
157 .await
158 .unwrap();
159
160 let session_id = session_info.session_id.clone();
161
162 let view = StorageBackedSessionView::new(session_id.clone(), Arc::clone(&storage));
164
165 assert_eq!(view.get_state("key1").await.unwrap(), None);
167
168 view.set_state("key1", json!("value1")).await.unwrap();
169 assert_eq!(
170 view.get_state("key1").await.unwrap(),
171 Some(json!("value1"))
172 );
173
174 view.set_state("key2", json!({"nested": "object"}))
175 .await
176 .unwrap();
177 assert_eq!(
178 view.get_state("key2").await.unwrap(),
179 Some(json!({"nested": "object"}))
180 );
181 }
182
183 #[tokio::test]
184 async fn test_session_view_metadata() {
185 let storage: Arc<BoxedSessionStorage> = Arc::new(InMemorySessionStorage::new());
186
187 let session_info = storage
188 .create_session(ServerCapabilities::default())
189 .await
190 .unwrap();
191
192 let session_id = session_info.session_id.clone();
193 let view = StorageBackedSessionView::new(session_id.clone(), Arc::clone(&storage));
194
195 assert_eq!(view.get_metadata("meta1").await.unwrap(), None);
197
198 view.set_metadata("meta1", json!("metadata_value"))
199 .await
200 .unwrap();
201 assert_eq!(
202 view.get_metadata("meta1").await.unwrap(),
203 Some(json!("metadata_value"))
204 );
205
206 let session = storage.get_session(&session_id).await.unwrap().unwrap();
208 assert_eq!(
209 session.metadata.get("__meta__:meta1"),
210 Some(&json!("metadata_value"))
211 );
212 }
213
214 #[tokio::test]
215 async fn test_session_view_session_id() {
216 let storage: Arc<BoxedSessionStorage> = Arc::new(InMemorySessionStorage::new());
217 let session_info = storage
218 .create_session(ServerCapabilities::default())
219 .await
220 .unwrap();
221
222 let view = StorageBackedSessionView::new(
223 session_info.session_id.clone(),
224 Arc::clone(&storage),
225 );
226
227 assert_eq!(view.session_id(), &session_info.session_id);
228 }
229
230 #[tokio::test]
231 async fn test_session_view_nonexistent_session() {
232 let storage: Arc<BoxedSessionStorage> = Arc::new(InMemorySessionStorage::new());
233 let view = StorageBackedSessionView::new("nonexistent".to_string(), Arc::clone(&storage));
234
235 assert_eq!(view.get_state("key").await.unwrap(), None);
237
238 let result = view.set_state("key", json!("value")).await;
240 assert!(result.is_err());
241 assert!(result.unwrap_err().contains("not found"));
242 }
243}