1use serde::{Deserialize, Serialize};
14
15#[derive(Debug, Clone, Default, Serialize, Deserialize)]
17pub struct ToolkitListParams {
18 #[serde(skip_serializing_if = "Option::is_none")]
20 pub category: Option<String>,
21
22 #[serde(skip_serializing_if = "Option::is_none")]
24 pub cursor: Option<String>,
25
26 #[serde(skip_serializing_if = "Option::is_none")]
28 pub limit: Option<u32>,
29
30 #[serde(skip_serializing_if = "Option::is_none")]
32 pub sort_by: Option<SortBy>,
33
34 #[serde(skip_serializing_if = "Option::is_none")]
36 pub managed_by: Option<ManagedBy>,
37
38 #[serde(skip_serializing_if = "Option::is_none")]
40 pub search: Option<String>,
41
42 #[serde(skip_serializing_if = "Option::is_none")]
44 pub show_deprecated: Option<bool>,
45}
46
47#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
49#[serde(rename_all = "lowercase")]
50pub enum SortBy {
51 Usage,
53 Alphabetically,
55}
56
57#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
59#[serde(rename_all = "lowercase")]
60pub enum ManagedBy {
61 Composio,
63 All,
65 Project,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct ToolkitListResponse {
72 pub items: Vec<ToolkitItem>,
74
75 #[serde(skip_serializing_if = "Option::is_none")]
77 pub next_cursor: Option<String>,
78
79 #[serde(skip_serializing_if = "Option::is_none")]
81 pub total_pages: Option<u32>,
82
83 #[serde(skip_serializing_if = "Option::is_none")]
85 pub current_page: Option<u32>,
86
87 #[serde(skip_serializing_if = "Option::is_none")]
89 pub total_items: Option<u32>,
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
94pub struct ToolkitItem {
95 pub slug: String,
97
98 pub name: String,
100
101 #[serde(skip_serializing_if = "Option::is_none")]
103 pub description: Option<String>,
104
105 #[serde(skip_serializing_if = "Option::is_none")]
107 pub logo: Option<String>,
108
109 #[serde(default)]
111 pub auth_schemes: Vec<String>,
112
113 #[serde(default)]
115 pub composio_managed_auth_schemes: Vec<String>,
116
117 #[serde(default)]
119 pub no_auth: bool,
120
121 #[serde(skip_serializing_if = "Option::is_none")]
123 pub meta: Option<ToolkitMeta>,
124
125 #[serde(default)]
127 pub is_local_toolkit: bool,
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize)]
132pub struct ToolkitMeta {
133 #[serde(skip_serializing_if = "Option::is_none")]
135 pub description: Option<String>,
136
137 #[serde(skip_serializing_if = "Option::is_none")]
139 pub logo: Option<String>,
140
141 #[serde(default)]
143 pub categories: Vec<String>,
144
145 #[serde(skip_serializing_if = "Option::is_none")]
147 pub tools_count: Option<u32>,
148
149 #[serde(skip_serializing_if = "Option::is_none")]
151 pub triggers_count: Option<u32>,
152
153 #[serde(skip_serializing_if = "Option::is_none")]
155 pub version: Option<String>,
156}
157
158#[derive(Debug, Clone, Default, Serialize, Deserialize)]
160pub struct ToolkitRetrieveParams {
161 #[serde(skip_serializing_if = "Option::is_none")]
163 pub version: Option<String>,
164}
165
166#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct ToolkitRetrieveResponse {
169 pub slug: String,
171
172 pub name: String,
174
175 #[serde(skip_serializing_if = "Option::is_none")]
177 pub description: Option<String>,
178
179 #[serde(skip_serializing_if = "Option::is_none")]
181 pub logo: Option<String>,
182
183 #[serde(default)]
185 pub auth_schemes: Vec<String>,
186
187 #[serde(default)]
189 pub composio_managed_auth_schemes: Vec<String>,
190
191 #[serde(default)]
193 pub no_auth: bool,
194
195 #[serde(skip_serializing_if = "Option::is_none")]
197 pub meta: Option<ToolkitMeta>,
198
199 #[serde(skip_serializing_if = "Option::is_none")]
201 pub auth_config_details: Option<Vec<AuthConfigDetail>>,
202
203 #[serde(skip_serializing_if = "Option::is_none")]
205 pub base_url: Option<String>,
206
207 #[serde(skip_serializing_if = "Option::is_none")]
209 pub get_current_user_endpoint: Option<String>,
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
214pub struct AuthConfigDetail {
215 pub mode: String,
217
218 pub fields: AuthConfigFields,
220
221 #[serde(default)]
223 pub is_default: bool,
224}
225
226#[derive(Debug, Clone, Serialize, Deserialize)]
228pub struct AuthConfigFields {
229 pub connected_account_initiation: AuthFieldSet,
231
232 pub auth_config_creation: AuthFieldSet,
234}
235
236#[derive(Debug, Clone, Serialize, Deserialize)]
238pub struct AuthFieldSet {
239 #[serde(default)]
241 pub required: Vec<AuthField>,
242
243 #[serde(default)]
245 pub optional: Vec<AuthField>,
246}
247
248#[derive(Debug, Clone, Serialize, Deserialize)]
250pub struct AuthField {
251 pub name: String,
253
254 #[serde(skip_serializing_if = "Option::is_none")]
256 pub display_name: Option<String>,
257
258 #[serde(skip_serializing_if = "Option::is_none")]
260 pub description: Option<String>,
261
262 #[serde(rename = "type")]
264 #[serde(skip_serializing_if = "Option::is_none")]
265 pub field_type: Option<String>,
266
267 #[serde(default)]
269 pub required: bool,
270
271 #[serde(skip_serializing_if = "Option::is_none")]
273 pub default: Option<serde_json::Value>,
274
275 #[serde(skip_serializing_if = "Option::is_none")]
277 pub expected_values: Option<Vec<String>>,
278}
279
280#[derive(Debug, Clone, Serialize, Deserialize)]
282pub struct ToolkitCategoriesResponse {
283 pub items: Vec<ToolkitCategory>,
285}
286
287#[derive(Debug, Clone, Serialize, Deserialize)]
289pub struct ToolkitCategory {
290 pub name: String,
292
293 #[serde(skip_serializing_if = "Option::is_none")]
295 pub display_name: Option<String>,
296
297 #[serde(skip_serializing_if = "Option::is_none")]
299 pub count: Option<u32>,
300}
301
302#[derive(Debug, Clone, Serialize, Deserialize)]
304pub struct AuthorizeParams {
305 pub user_id: String,
307
308 pub toolkit: String,
310
311 #[serde(skip_serializing_if = "Option::is_none")]
313 pub auth_config_id: Option<String>,
314}
315
316#[cfg(test)]
317mod tests {
318 use super::*;
319
320 #[test]
321 fn test_toolkit_list_params_default() {
322 let params = ToolkitListParams::default();
323 assert!(params.category.is_none());
324 assert!(params.cursor.is_none());
325 assert!(params.limit.is_none());
326 }
327
328 #[test]
329 fn test_sort_by_serialization() {
330 let sort = SortBy::Usage;
331 let json = serde_json::to_string(&sort).unwrap();
332 assert_eq!(json, "\"usage\"");
333
334 let sort = SortBy::Alphabetically;
335 let json = serde_json::to_string(&sort).unwrap();
336 assert_eq!(json, "\"alphabetically\"");
337 }
338
339 #[test]
340 fn test_managed_by_serialization() {
341 let managed = ManagedBy::Composio;
342 let json = serde_json::to_string(&managed).unwrap();
343 assert_eq!(json, "\"composio\"");
344
345 let managed = ManagedBy::All;
346 let json = serde_json::to_string(&managed).unwrap();
347 assert_eq!(json, "\"all\"");
348
349 let managed = ManagedBy::Project;
350 let json = serde_json::to_string(&managed).unwrap();
351 assert_eq!(json, "\"project\"");
352 }
353
354 #[test]
355 fn test_toolkit_item_deserialization() {
356 let json = r#"{
357 "slug": "github",
358 "name": "GitHub",
359 "description": "GitHub integration",
360 "logo": "https://example.com/logo.png",
361 "auth_schemes": ["OAUTH2"],
362 "composio_managed_auth_schemes": ["OAUTH2"],
363 "no_auth": false,
364 "is_local_toolkit": false
365 }"#;
366
367 let item: ToolkitItem = serde_json::from_str(json).unwrap();
368 assert_eq!(item.slug, "github");
369 assert_eq!(item.name, "GitHub");
370 assert_eq!(item.auth_schemes.len(), 1);
371 assert!(!item.no_auth);
372 }
373
374 #[test]
375 fn test_toolkit_meta() {
376 let meta = ToolkitMeta {
377 description: Some("Test toolkit".to_string()),
378 logo: Some("https://example.com/logo.png".to_string()),
379 categories: vec!["development".to_string()],
380 tools_count: Some(50),
381 triggers_count: Some(10),
382 version: Some("1.0.0".to_string()),
383 };
384
385 assert_eq!(meta.tools_count, Some(50));
386 assert_eq!(meta.triggers_count, Some(10));
387 assert_eq!(meta.categories.len(), 1);
388 }
389
390 #[test]
391 fn test_auth_field() {
392 let field = AuthField {
393 name: "client_id".to_string(),
394 display_name: Some("Client ID".to_string()),
395 description: Some("OAuth client ID".to_string()),
396 field_type: Some("string".to_string()),
397 required: true,
398 default: None,
399 expected_values: None,
400 };
401
402 assert_eq!(field.name, "client_id");
403 assert!(field.required);
404 }
405
406 #[test]
407 fn test_auth_field_set() {
408 let field_set = AuthFieldSet {
409 required: vec![AuthField {
410 name: "api_key".to_string(),
411 display_name: None,
412 description: None,
413 field_type: Some("string".to_string()),
414 required: true,
415 default: None,
416 expected_values: None,
417 }],
418 optional: vec![],
419 };
420
421 assert_eq!(field_set.required.len(), 1);
422 assert_eq!(field_set.optional.len(), 0);
423 }
424
425 #[test]
426 fn test_toolkit_retrieve_params_serialization() {
427 let params = ToolkitRetrieveParams {
428 version: Some("20250906_01".to_string()),
429 };
430
431 let value = serde_json::to_value(¶ms).unwrap();
432 assert_eq!(value["version"], "20250906_01");
433 }
434
435 #[test]
436 fn test_toolkit_retrieve_response_deserialization() {
437 let json = r#"{
438 "slug": "github",
439 "name": "GitHub",
440 "auth_schemes": ["OAUTH2"],
441 "composio_managed_auth_schemes": ["OAUTH2"],
442 "no_auth": false
443 }"#;
444
445 let response: ToolkitRetrieveResponse = serde_json::from_str(json).unwrap();
446 assert_eq!(response.slug, "github");
447 assert_eq!(response.name, "GitHub");
448 assert_eq!(response.auth_schemes.len(), 1);
449 }
450
451 #[test]
452 fn test_authorize_params() {
453 let params = AuthorizeParams {
454 user_id: "user_123".to_string(),
455 toolkit: "github".to_string(),
456 auth_config_id: Some("ac_456".to_string()),
457 };
458
459 assert_eq!(params.user_id, "user_123");
460 assert_eq!(params.toolkit, "github");
461 assert!(params.auth_config_id.is_some());
462 }
463}