use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ToolkitListParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub category: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cursor: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sort_by: Option<SortBy>,
#[serde(skip_serializing_if = "Option::is_none")]
pub managed_by: Option<ManagedBy>,
#[serde(skip_serializing_if = "Option::is_none")]
pub search: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub show_deprecated: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum SortBy {
Usage,
Alphabetically,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ManagedBy {
Composio,
All,
Project,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolkitListResponse {
pub items: Vec<ToolkitItem>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub total_pages: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub current_page: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub total_items: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolkitItem {
pub slug: String,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub logo: Option<String>,
#[serde(default)]
pub auth_schemes: Vec<String>,
#[serde(default)]
pub composio_managed_auth_schemes: Vec<String>,
#[serde(default)]
pub no_auth: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub meta: Option<ToolkitMeta>,
#[serde(default)]
pub is_local_toolkit: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolkitMeta {
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub logo: Option<String>,
#[serde(default)]
pub categories: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tools_count: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub triggers_count: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ToolkitRetrieveParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolkitRetrieveResponse {
pub slug: String,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub logo: Option<String>,
#[serde(default)]
pub auth_schemes: Vec<String>,
#[serde(default)]
pub composio_managed_auth_schemes: Vec<String>,
#[serde(default)]
pub no_auth: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub meta: Option<ToolkitMeta>,
#[serde(skip_serializing_if = "Option::is_none")]
pub auth_config_details: Option<Vec<AuthConfigDetail>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub base_url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub get_current_user_endpoint: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthConfigDetail {
pub mode: String,
pub fields: AuthConfigFields,
#[serde(default)]
pub is_default: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthConfigFields {
pub connected_account_initiation: AuthFieldSet,
pub auth_config_creation: AuthFieldSet,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthFieldSet {
#[serde(default)]
pub required: Vec<AuthField>,
#[serde(default)]
pub optional: Vec<AuthField>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthField {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub display_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(rename = "type")]
#[serde(skip_serializing_if = "Option::is_none")]
pub field_type: Option<String>,
#[serde(default)]
pub required: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub default: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expected_values: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolkitCategoriesResponse {
pub items: Vec<ToolkitCategory>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolkitCategory {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub display_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub count: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthorizeParams {
pub user_id: String,
pub toolkit: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub auth_config_id: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_toolkit_list_params_default() {
let params = ToolkitListParams::default();
assert!(params.category.is_none());
assert!(params.cursor.is_none());
assert!(params.limit.is_none());
}
#[test]
fn test_sort_by_serialization() {
let sort = SortBy::Usage;
let json = serde_json::to_string(&sort).unwrap();
assert_eq!(json, "\"usage\"");
let sort = SortBy::Alphabetically;
let json = serde_json::to_string(&sort).unwrap();
assert_eq!(json, "\"alphabetically\"");
}
#[test]
fn test_managed_by_serialization() {
let managed = ManagedBy::Composio;
let json = serde_json::to_string(&managed).unwrap();
assert_eq!(json, "\"composio\"");
let managed = ManagedBy::All;
let json = serde_json::to_string(&managed).unwrap();
assert_eq!(json, "\"all\"");
let managed = ManagedBy::Project;
let json = serde_json::to_string(&managed).unwrap();
assert_eq!(json, "\"project\"");
}
#[test]
fn test_toolkit_item_deserialization() {
let json = r#"{
"slug": "github",
"name": "GitHub",
"description": "GitHub integration",
"logo": "https://example.com/logo.png",
"auth_schemes": ["OAUTH2"],
"composio_managed_auth_schemes": ["OAUTH2"],
"no_auth": false,
"is_local_toolkit": false
}"#;
let item: ToolkitItem = serde_json::from_str(json).unwrap();
assert_eq!(item.slug, "github");
assert_eq!(item.name, "GitHub");
assert_eq!(item.auth_schemes.len(), 1);
assert!(!item.no_auth);
}
#[test]
fn test_toolkit_meta() {
let meta = ToolkitMeta {
description: Some("Test toolkit".to_string()),
logo: Some("https://example.com/logo.png".to_string()),
categories: vec!["development".to_string()],
tools_count: Some(50),
triggers_count: Some(10),
version: Some("1.0.0".to_string()),
};
assert_eq!(meta.tools_count, Some(50));
assert_eq!(meta.triggers_count, Some(10));
assert_eq!(meta.categories.len(), 1);
}
#[test]
fn test_auth_field() {
let field = AuthField {
name: "client_id".to_string(),
display_name: Some("Client ID".to_string()),
description: Some("OAuth client ID".to_string()),
field_type: Some("string".to_string()),
required: true,
default: None,
expected_values: None,
};
assert_eq!(field.name, "client_id");
assert!(field.required);
}
#[test]
fn test_auth_field_set() {
let field_set = AuthFieldSet {
required: vec![AuthField {
name: "api_key".to_string(),
display_name: None,
description: None,
field_type: Some("string".to_string()),
required: true,
default: None,
expected_values: None,
}],
optional: vec![],
};
assert_eq!(field_set.required.len(), 1);
assert_eq!(field_set.optional.len(), 0);
}
#[test]
fn test_toolkit_retrieve_params_serialization() {
let params = ToolkitRetrieveParams {
version: Some("20250906_01".to_string()),
};
let value = serde_json::to_value(¶ms).unwrap();
assert_eq!(value["version"], "20250906_01");
}
#[test]
fn test_toolkit_retrieve_response_deserialization() {
let json = r#"{
"slug": "github",
"name": "GitHub",
"auth_schemes": ["OAUTH2"],
"composio_managed_auth_schemes": ["OAUTH2"],
"no_auth": false
}"#;
let response: ToolkitRetrieveResponse = serde_json::from_str(json).unwrap();
assert_eq!(response.slug, "github");
assert_eq!(response.name, "GitHub");
assert_eq!(response.auth_schemes.len(), 1);
}
#[test]
fn test_authorize_params() {
let params = AuthorizeParams {
user_id: "user_123".to_string(),
toolkit: "github".to_string(),
auth_config_id: Some("ac_456".to_string()),
};
assert_eq!(params.user_id, "user_123");
assert_eq!(params.toolkit, "github");
assert!(params.auth_config_id.is_some());
}
}