1use serde::Deserialize;
4
5use super::enums::AuthScheme;
6use super::request::SessionConfig;
7
8#[derive(Debug, Clone, Deserialize)]
10pub struct SessionResponse {
11 pub session_id: String,
12 pub mcp: McpInfo,
13 pub tool_router_tools: Vec<String>,
14 pub config: SessionConfig,
15 #[serde(skip_serializing_if = "Option::is_none")]
16 pub assistive_prompt: Option<String>,
17}
18
19#[derive(Debug, Clone, Deserialize)]
21pub struct McpInfo {
22 pub url: String,
23}
24
25#[derive(Debug, Clone, Deserialize)]
27pub struct ToolSchema {
28 pub slug: String,
29 pub name: String,
30 pub description: String,
31 pub toolkit: String,
32 pub input_parameters: serde_json::Value,
33 pub output_parameters: serde_json::Value,
34 #[serde(default)]
35 pub scopes: Vec<String>,
36 #[serde(default)]
37 pub tags: Vec<String>,
38 pub version: String,
39 #[serde(default)]
40 pub available_versions: Vec<String>,
41 #[serde(default)]
42 pub is_deprecated: bool,
43 #[serde(default)]
44 pub no_auth: bool,
45}
46
47#[derive(Debug, Clone, Deserialize)]
49pub struct ToolExecutionResponse {
50 pub data: serde_json::Value,
51 pub error: Option<String>,
52 pub log_id: String,
53}
54
55pub type MetaToolExecutionResponse = ToolExecutionResponse;
57
58#[derive(Debug, Clone, Deserialize)]
60pub struct ToolkitListResponse {
61 pub items: Vec<ToolkitInfo>,
62 pub next_cursor: Option<String>,
63 pub total_pages: u32,
64 pub current_page: u32,
65 pub total_items: u32,
66}
67
68#[derive(Debug, Clone, Deserialize)]
70pub struct ToolkitInfo {
71 pub name: String,
72 pub slug: String,
73 pub enabled: bool,
74 pub is_no_auth: bool,
75 pub composio_managed_auth_schemes: Vec<AuthScheme>,
76 pub meta: ToolkitMeta,
77 pub connected_account: Option<ConnectedAccountInfo>,
78}
79
80#[derive(Debug, Clone, Deserialize)]
82pub struct ToolkitMeta {
83 pub logo: String,
84 pub description: String,
85 #[serde(default)]
86 pub categories: Vec<String>,
87 #[serde(default)]
88 pub tools_count: u32,
89 #[serde(default)]
90 pub triggers_count: u32,
91 #[serde(default)]
92 pub version: String,
93}
94
95#[derive(Debug, Clone, Deserialize)]
97pub struct ConnectedAccountInfo {
98 pub id: String,
99 pub status: String,
100 pub created_at: String,
101}
102
103#[derive(Debug, Clone, Deserialize)]
105pub struct LinkResponse {
106 pub link_token: String,
107 pub redirect_url: String,
108 pub connected_account_id: Option<String>,
109}
110
111#[derive(Debug, Clone, Deserialize)]
113pub struct ErrorResponse {
114 pub message: String,
115 pub code: Option<String>,
116 pub slug: Option<String>,
117 pub status: u16,
118 pub request_id: Option<String>,
119 pub suggested_fix: Option<String>,
120 pub errors: Option<Vec<crate::error::ErrorDetail>>,
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126 use serde_json;
127
128 #[test]
129 fn test_session_response_deserialization() {
130 let json = r#"{
131 "session_id": "sess_abc123",
132 "mcp": {
133 "url": "https://mcp.composio.dev/sess_abc123"
134 },
135 "tool_router_tools": [
136 "COMPOSIO_SEARCH_TOOLS",
137 "COMPOSIO_MULTI_EXECUTE_TOOL"
138 ],
139 "config": {
140 "user_id": "user_123"
141 }
142 }"#;
143
144 let response: SessionResponse = serde_json::from_str(json).unwrap();
145 assert_eq!(response.session_id, "sess_abc123");
146 assert_eq!(response.mcp.url, "https://mcp.composio.dev/sess_abc123");
147 assert_eq!(response.tool_router_tools.len(), 2);
148 assert_eq!(response.config.user_id, "user_123");
149 assert!(response.assistive_prompt.is_none());
150 }
151
152 #[test]
153 fn test_session_response_with_assistive_prompt() {
154 let json = r#"{
155 "session_id": "sess_abc123",
156 "mcp": {
157 "url": "https://mcp.composio.dev/sess_abc123"
158 },
159 "tool_router_tools": [],
160 "config": {
161 "user_id": "user_123"
162 },
163 "assistive_prompt": "Use COMPOSIO_SEARCH_TOOLS to discover tools"
164 }"#;
165
166 let response: SessionResponse = serde_json::from_str(json).unwrap();
167 assert_eq!(
168 response.assistive_prompt,
169 Some("Use COMPOSIO_SEARCH_TOOLS to discover tools".to_string())
170 );
171 }
172
173 #[test]
174 fn test_mcp_info_deserialization() {
175 let json = r#"{
176 "url": "https://mcp.composio.dev/session_123"
177 }"#;
178
179 let mcp: McpInfo = serde_json::from_str(json).unwrap();
180 assert_eq!(mcp.url, "https://mcp.composio.dev/session_123");
181 }
182
183 #[test]
184 fn test_tool_schema_deserialization() {
185 let json = r#"{
186 "slug": "GITHUB_CREATE_ISSUE",
187 "name": "Create Issue",
188 "description": "Create a new issue in a GitHub repository",
189 "toolkit": "github",
190 "input_parameters": {
191 "type": "object",
192 "properties": {
193 "owner": {"type": "string"},
194 "repo": {"type": "string"},
195 "title": {"type": "string"}
196 }
197 },
198 "output_parameters": {
199 "type": "object",
200 "properties": {
201 "id": {"type": "number"}
202 }
203 },
204 "scopes": ["repo"],
205 "tags": ["write"],
206 "version": "1.0.0",
207 "available_versions": ["1.0.0", "0.9.0"],
208 "is_deprecated": false,
209 "no_auth": false
210 }"#;
211
212 let schema: ToolSchema = serde_json::from_str(json).unwrap();
213 assert_eq!(schema.slug, "GITHUB_CREATE_ISSUE");
214 assert_eq!(schema.name, "Create Issue");
215 assert_eq!(schema.toolkit, "github");
216 assert_eq!(schema.scopes.len(), 1);
217 assert_eq!(schema.tags.len(), 1);
218 assert_eq!(schema.version, "1.0.0");
219 assert_eq!(schema.available_versions.len(), 2);
220 assert!(!schema.is_deprecated);
221 assert!(!schema.no_auth);
222 }
223
224 #[test]
225 fn test_tool_schema_minimal_deserialization() {
226 let json = r#"{
227 "slug": "SIMPLE_TOOL",
228 "name": "Simple Tool",
229 "description": "A simple tool",
230 "toolkit": "simple",
231 "input_parameters": {},
232 "output_parameters": {},
233 "version": "1.0.0"
234 }"#;
235
236 let schema: ToolSchema = serde_json::from_str(json).unwrap();
237 assert_eq!(schema.slug, "SIMPLE_TOOL");
238 assert!(schema.scopes.is_empty());
239 assert!(schema.tags.is_empty());
240 assert!(schema.available_versions.is_empty());
241 assert!(!schema.is_deprecated);
242 assert!(!schema.no_auth);
243 }
244
245 #[test]
246 fn test_tool_execution_response_deserialization() {
247 let json = r#"{
248 "data": {
249 "issue_id": 123,
250 "url": "https://github.com/owner/repo/issues/123"
251 },
252 "error": null,
253 "log_id": "log_xyz789"
254 }"#;
255
256 let response: ToolExecutionResponse = serde_json::from_str(json).unwrap();
257 assert!(response.data.is_object());
258 assert_eq!(response.data["issue_id"], 123);
259 assert!(response.error.is_none());
260 assert_eq!(response.log_id, "log_xyz789");
261 }
262
263 #[test]
264 fn test_tool_execution_response_with_error() {
265 let json = r#"{
266 "data": null,
267 "error": "Failed to create issue: Invalid repository",
268 "log_id": "log_error123"
269 }"#;
270
271 let response: ToolExecutionResponse = serde_json::from_str(json).unwrap();
272 assert!(response.data.is_null());
273 assert_eq!(
274 response.error,
275 Some("Failed to create issue: Invalid repository".to_string())
276 );
277 assert_eq!(response.log_id, "log_error123");
278 }
279
280 #[test]
281 fn test_toolkit_list_response_deserialization() {
282 let json = r#"{
283 "items": [
284 {
285 "name": "GitHub",
286 "slug": "github",
287 "enabled": true,
288 "is_no_auth": false,
289 "composio_managed_auth_schemes": ["OAUTH2"],
290 "meta": {
291 "logo": "https://logo.url",
292 "description": "GitHub integration",
293 "categories": ["development"],
294 "tools_count": 50,
295 "triggers_count": 10,
296 "version": "1.0.0"
297 },
298 "connected_account": {
299 "id": "ca_123",
300 "status": "ACTIVE",
301 "created_at": "2024-01-01T00:00:00Z"
302 }
303 }
304 ],
305 "next_cursor": "cursor_abc",
306 "total_pages": 5,
307 "current_page": 1,
308 "total_items": 100
309 }"#;
310
311 let response: ToolkitListResponse = serde_json::from_str(json).unwrap();
312 assert_eq!(response.items.len(), 1);
313 assert_eq!(response.next_cursor, Some("cursor_abc".to_string()));
314 assert_eq!(response.total_pages, 5);
315 assert_eq!(response.current_page, 1);
316 assert_eq!(response.total_items, 100);
317 }
318
319 #[test]
320 fn test_toolkit_info_deserialization() {
321 let json = r#"{
322 "name": "Gmail",
323 "slug": "gmail",
324 "enabled": true,
325 "is_no_auth": false,
326 "composio_managed_auth_schemes": ["OAUTH2"],
327 "meta": {
328 "logo": "https://gmail.logo",
329 "description": "Gmail integration",
330 "categories": ["communication"],
331 "tools_count": 30,
332 "triggers_count": 5,
333 "version": "2.0.0"
334 },
335 "connected_account": null
336 }"#;
337
338 let info: ToolkitInfo = serde_json::from_str(json).unwrap();
339 assert_eq!(info.name, "Gmail");
340 assert_eq!(info.slug, "gmail");
341 assert!(info.enabled);
342 assert!(!info.is_no_auth);
343 assert_eq!(info.composio_managed_auth_schemes.len(), 1);
344 assert!(info.connected_account.is_none());
345 }
346
347 #[test]
348 fn test_toolkit_meta_deserialization() {
349 let json = r#"{
350 "logo": "https://logo.url",
351 "description": "Test toolkit",
352 "categories": ["test", "development"],
353 "tools_count": 25,
354 "triggers_count": 3,
355 "version": "1.5.0"
356 }"#;
357
358 let meta: ToolkitMeta = serde_json::from_str(json).unwrap();
359 assert_eq!(meta.logo, "https://logo.url");
360 assert_eq!(meta.description, "Test toolkit");
361 assert_eq!(meta.categories.len(), 2);
362 assert_eq!(meta.tools_count, 25);
363 assert_eq!(meta.triggers_count, 3);
364 assert_eq!(meta.version, "1.5.0");
365 }
366
367 #[test]
368 fn test_toolkit_meta_minimal_deserialization() {
369 let json = r#"{
370 "logo": "https://logo.url",
371 "description": "Minimal toolkit"
372 }"#;
373
374 let meta: ToolkitMeta = serde_json::from_str(json).unwrap();
375 assert_eq!(meta.logo, "https://logo.url");
376 assert_eq!(meta.description, "Minimal toolkit");
377 assert!(meta.categories.is_empty());
378 assert_eq!(meta.tools_count, 0);
379 assert_eq!(meta.triggers_count, 0);
380 assert_eq!(meta.version, "");
381 }
382
383 #[test]
384 fn test_connected_account_info_deserialization() {
385 let json = r#"{
386 "id": "ca_abc123",
387 "status": "ACTIVE",
388 "created_at": "2024-01-15T10:30:00Z"
389 }"#;
390
391 let info: ConnectedAccountInfo = serde_json::from_str(json).unwrap();
392 assert_eq!(info.id, "ca_abc123");
393 assert_eq!(info.status, "ACTIVE");
394 assert_eq!(info.created_at, "2024-01-15T10:30:00Z");
395 }
396
397 #[test]
398 fn test_link_response_deserialization() {
399 let json = r#"{
400 "link_token": "lt_xyz789",
401 "redirect_url": "https://auth.composio.dev/link?token=lt_xyz789",
402 "connected_account_id": "ca_existing123"
403 }"#;
404
405 let response: LinkResponse = serde_json::from_str(json).unwrap();
406 assert_eq!(response.link_token, "lt_xyz789");
407 assert_eq!(response.redirect_url, "https://auth.composio.dev/link?token=lt_xyz789");
408 assert_eq!(response.connected_account_id, Some("ca_existing123".to_string()));
409 }
410
411 #[test]
412 fn test_link_response_without_connected_account() {
413 let json = r#"{
414 "link_token": "lt_new456",
415 "redirect_url": "https://auth.composio.dev/link?token=lt_new456",
416 "connected_account_id": null
417 }"#;
418
419 let response: LinkResponse = serde_json::from_str(json).unwrap();
420 assert_eq!(response.link_token, "lt_new456");
421 assert!(response.connected_account_id.is_none());
422 }
423
424 #[test]
425 fn test_error_response_deserialization() {
426 let json = r#"{
427 "message": "Validation failed",
428 "code": "VALIDATION_ERROR",
429 "slug": "validation-failed",
430 "status": 400,
431 "request_id": "req_abc123",
432 "suggested_fix": "Check your input parameters",
433 "errors": [
434 {
435 "field": "user_id",
436 "message": "User ID is required"
437 }
438 ]
439 }"#;
440
441 let response: ErrorResponse = serde_json::from_str(json).unwrap();
442 assert_eq!(response.message, "Validation failed");
443 assert_eq!(response.code, Some("VALIDATION_ERROR".to_string()));
444 assert_eq!(response.slug, Some("validation-failed".to_string()));
445 assert_eq!(response.status, 400);
446 assert_eq!(response.request_id, Some("req_abc123".to_string()));
447 assert_eq!(response.suggested_fix, Some("Check your input parameters".to_string()));
448 assert!(response.errors.is_some());
449 assert_eq!(response.errors.as_ref().unwrap().len(), 1);
450 }
451
452 #[test]
453 fn test_error_response_minimal_deserialization() {
454 let json = r#"{
455 "message": "Internal server error",
456 "status": 500
457 }"#;
458
459 let response: ErrorResponse = serde_json::from_str(json).unwrap();
460 assert_eq!(response.message, "Internal server error");
461 assert_eq!(response.status, 500);
462 assert!(response.code.is_none());
463 assert!(response.slug.is_none());
464 assert!(response.request_id.is_none());
465 assert!(response.suggested_fix.is_none());
466 assert!(response.errors.is_none());
467 }
468
469 #[test]
470 fn test_auth_scheme_deserialization() {
471 let json = r#"["OAUTH2", "API_KEY", "BEARER_TOKEN"]"#;
472 let schemes: Vec<AuthScheme> = serde_json::from_str(json).unwrap();
473
474 assert_eq!(schemes.len(), 3);
475 assert!(matches!(schemes[0], AuthScheme::Oauth2));
476 assert!(matches!(schemes[1], AuthScheme::ApiKey));
477 assert!(matches!(schemes[2], AuthScheme::BearerToken));
478 }
479
480 #[test]
481 fn test_meta_tool_execution_response_alias() {
482 let json = r#"{
483 "data": {"result": "success"},
484 "error": null,
485 "log_id": "log_meta123"
486 }"#;
487
488 let response: MetaToolExecutionResponse = serde_json::from_str(json).unwrap();
489 assert!(response.data.is_object());
490 assert!(response.error.is_none());
491 assert_eq!(response.log_id, "log_meta123");
492 }
493
494 #[test]
495 fn test_toolkit_list_response_empty_items() {
496 let json = r#"{
497 "items": [],
498 "next_cursor": null,
499 "total_pages": 0,
500 "current_page": 0,
501 "total_items": 0
502 }"#;
503
504 let response: ToolkitListResponse = serde_json::from_str(json).unwrap();
505 assert!(response.items.is_empty());
506 assert!(response.next_cursor.is_none());
507 assert_eq!(response.total_pages, 0);
508 assert_eq!(response.current_page, 0);
509 assert_eq!(response.total_items, 0);
510 }
511
512 #[test]
513 fn test_session_response_clone() {
514 let json = r#"{
515 "session_id": "sess_123",
516 "mcp": {"url": "https://mcp.url"},
517 "tool_router_tools": [],
518 "config": {"user_id": "user_123"}
519 }"#;
520
521 let response: SessionResponse = serde_json::from_str(json).unwrap();
522 let cloned = response.clone();
523
524 assert_eq!(response.session_id, cloned.session_id);
525 assert_eq!(response.mcp.url, cloned.mcp.url);
526 }
527
528 #[test]
529 fn test_tool_schema_debug() {
530 let json = r#"{
531 "slug": "TEST_TOOL",
532 "name": "Test",
533 "description": "Test tool",
534 "toolkit": "test",
535 "input_parameters": {},
536 "output_parameters": {},
537 "version": "1.0.0"
538 }"#;
539
540 let schema: ToolSchema = serde_json::from_str(json).unwrap();
541 let debug_str = format!("{:?}", schema);
542
543 assert!(debug_str.contains("TEST_TOOL"));
544 assert!(debug_str.contains("Test"));
545 }
546}