1use crate::error::ComposioError;
4use crate::models::versioning::ToolkitVersionParam;
5use crate::retry::RetryPolicy;
6use std::path::PathBuf;
7use std::time::Duration;
8
9#[derive(Debug, Clone)]
11pub struct ComposioConfig {
12 pub api_key: String,
14 pub base_url: String,
16 pub timeout: Duration,
18 pub retry_policy: RetryPolicy,
20 pub toolkit_versions: Option<ToolkitVersionParam>,
22 pub file_download_dir: Option<PathBuf>,
24 pub auto_upload_download_files: bool,
26 pub telemetry_enabled: bool,
28}
29
30impl ComposioConfig {
31 pub fn new(api_key: impl Into<String>) -> Self {
53 Self {
54 api_key: api_key.into(),
55 base_url: "https://backend.composio.dev/api/v3".to_string(),
56 timeout: Duration::from_secs(30),
57 retry_policy: RetryPolicy::default(),
58 toolkit_versions: None,
59 file_download_dir: None,
60 auto_upload_download_files: true,
61 telemetry_enabled: false, }
63 }
64
65 pub fn validate(&self) -> Result<(), ComposioError> {
85 if self.api_key.is_empty() {
86 return Err(ComposioError::InvalidInput(
87 "API key cannot be empty".to_string(),
88 ));
89 }
90
91 if !self.base_url.starts_with("http") {
92 return Err(ComposioError::ConfigError(
93 "Base URL must start with http:// or https://".to_string(),
94 ));
95 }
96
97 Ok(())
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
106 fn test_new_config_with_defaults() {
107 let config = ComposioConfig::new("test_api_key");
108
109 assert_eq!(config.api_key, "test_api_key");
110 assert_eq!(config.base_url, "https://backend.composio.dev/api/v3");
111 assert_eq!(config.timeout, Duration::from_secs(30));
112 assert_eq!(config.retry_policy.max_retries, 3);
113 assert_eq!(config.retry_policy.initial_delay, Duration::from_secs(1));
114 assert_eq!(config.retry_policy.max_delay, Duration::from_secs(10));
115 }
116
117 #[test]
118 fn test_new_config_accepts_string() {
119 let config = ComposioConfig::new("test_key".to_string());
120 assert_eq!(config.api_key, "test_key");
121 }
122
123 #[test]
124 fn test_new_config_accepts_str() {
125 let config = ComposioConfig::new("test_key");
126 assert_eq!(config.api_key, "test_key");
127 }
128
129 #[test]
130 fn test_validate_valid_config() {
131 let config = ComposioConfig::new("valid_api_key");
132 assert!(config.validate().is_ok());
133 }
134
135 #[test]
136 fn test_validate_empty_api_key() {
137 let config = ComposioConfig::new("");
138 let result = config.validate();
139
140 assert!(result.is_err());
141 match result {
142 Err(ComposioError::InvalidInput(msg)) => {
143 assert_eq!(msg, "API key cannot be empty");
144 }
145 _ => panic!("Expected InvalidInput error"),
146 }
147 }
148
149 #[test]
150 fn test_validate_invalid_base_url() {
151 let mut config = ComposioConfig::new("test_key");
152 config.base_url = "invalid-url".to_string();
153
154 let result = config.validate();
155 assert!(result.is_err());
156 match result {
157 Err(ComposioError::ConfigError(msg)) => {
158 assert_eq!(msg, "Base URL must start with http:// or https://");
159 }
160 _ => panic!("Expected ConfigError"),
161 }
162 }
163
164 #[test]
165 fn test_validate_http_base_url() {
166 let mut config = ComposioConfig::new("test_key");
167 config.base_url = "http://localhost:8080".to_string();
168
169 assert!(config.validate().is_ok());
170 }
171
172 #[test]
173 fn test_validate_https_base_url() {
174 let mut config = ComposioConfig::new("test_key");
175 config.base_url = "https://api.example.com".to_string();
176
177 assert!(config.validate().is_ok());
178 }
179
180 #[test]
181 fn test_config_is_cloneable() {
182 let config = ComposioConfig::new("test_key");
183 let cloned = config.clone();
184
185 assert_eq!(config.api_key, cloned.api_key);
186 assert_eq!(config.base_url, cloned.base_url);
187 assert_eq!(config.timeout, cloned.timeout);
188 }
189
190 #[test]
191 fn test_config_is_debuggable() {
192 let config = ComposioConfig::new("test_key");
193 let debug_str = format!("{:?}", config);
194
195 assert!(debug_str.contains("ComposioConfig"));
196 assert!(debug_str.contains("test_key"));
197 }
198
199 #[test]
200 fn test_default_base_url() {
201 let config = ComposioConfig::new("key");
202 assert_eq!(config.base_url, "https://backend.composio.dev/api/v3");
203 }
204
205 #[test]
206 fn test_default_timeout() {
207 let config = ComposioConfig::new("key");
208 assert_eq!(config.timeout, Duration::from_secs(30));
209 }
210
211 #[test]
212 fn test_default_retry_policy() {
213 let config = ComposioConfig::new("key");
214 assert_eq!(config.retry_policy.max_retries, 3);
215 assert_eq!(config.retry_policy.initial_delay, Duration::from_secs(1));
216 assert_eq!(config.retry_policy.max_delay, Duration::from_secs(10));
217 }
218
219 #[test]
220 fn test_custom_base_url() {
221 let mut config = ComposioConfig::new("key");
222 config.base_url = "https://custom.api.com".to_string();
223
224 assert!(config.validate().is_ok());
225 assert_eq!(config.base_url, "https://custom.api.com");
226 }
227
228 #[test]
229 fn test_custom_timeout() {
230 let mut config = ComposioConfig::new("key");
231 config.timeout = Duration::from_secs(60);
232
233 assert_eq!(config.timeout, Duration::from_secs(60));
234 }
235
236 #[test]
237 fn test_custom_retry_policy() {
238 let mut config = ComposioConfig::new("key");
239 config.retry_policy = RetryPolicy {
240 max_retries: 5,
241 initial_delay: Duration::from_secs(2),
242 max_delay: Duration::from_secs(20),
243 };
244
245 assert_eq!(config.retry_policy.max_retries, 5);
246 assert_eq!(config.retry_policy.initial_delay, Duration::from_secs(2));
247 assert_eq!(config.retry_policy.max_delay, Duration::from_secs(20));
248 }
249}