Skip to main content

composio_sdk/models/
base.rs

1//! Base resource structures for Composio client
2//!
3//! This module provides base structures and traits for resource management,
4//! including telemetry tracking and method tracing.
5
6use std::time::{SystemTime, UNIX_EPOCH};
7use std::sync::Arc;
8
9use crate::client::ComposioClient;
10use crate::models::telemetry::{
11    push_event, Event, EventType, SourceData, Metadata, TelemetryData, ErrorData,
12    ServiceType, LanguageType, EnvironmentType,
13};
14
15/// Environment type for telemetry
16#[derive(Debug, Clone)]
17pub enum Environment {
18    Development,
19    Production,
20    Staging,
21}
22
23impl Environment {
24    /// Get environment from environment variable or default to Development
25    pub fn from_env() -> Self {
26        match std::env::var("ENVIRONMENT").as_deref() {
27            Ok("production") => Environment::Production,
28            Ok("staging") => Environment::Staging,
29            _ => Environment::Development,
30        }
31    }
32
33    /// Convert to string representation
34    pub fn as_str(&self) -> &str {
35        match self {
36            Environment::Development => "development",
37            Environment::Production => "production",
38            Environment::Staging => "staging",
39        }
40    }
41}
42
43/// Context for tracking telemetry
44#[derive(Debug, Clone)]
45pub struct TelemetryContext {
46    /// Whether tracking is allowed
47    pub allow_tracking: bool,
48    /// Current environment
49    pub environment: Environment,
50}
51
52impl Default for TelemetryContext {
53    fn default() -> Self {
54        Self {
55            allow_tracking: true,
56            environment: Environment::from_env(),
57        }
58    }
59}
60
61impl TelemetryContext {
62    /// Create a new telemetry context
63    pub fn new(allow_tracking: bool) -> Self {
64        Self {
65            allow_tracking,
66            environment: Environment::from_env(),
67        }
68    }
69
70    /// Disable tracking
71    pub fn disable_tracking(&mut self) {
72        self.allow_tracking = false;
73    }
74
75    /// Enable tracking
76    pub fn enable_tracking(&mut self) {
77        self.allow_tracking = true;
78    }
79}
80
81/// Base resource trait for Composio resources
82/// 
83/// This trait provides the foundation for all Composio resource types,
84/// including telemetry tracking, method tracing, and payload sanitization.
85pub trait Resource {
86    /// Get the client reference
87    fn client(&self) -> &ComposioClient;
88
89    /// Get telemetry context
90    fn telemetry_context(&self) -> &TelemetryContext;
91
92    /// Sanitize payload by removing sensitive information
93    /// 
94    /// This method filters out sensitive fields from payloads before logging.
95    /// Override this method in specific resource implementations to customize
96    /// which fields should be sanitized.
97    fn sanitize_payload<T>(&self, payload: T) -> T
98    where
99        T: serde::Serialize + serde::de::DeserializeOwned,
100    {
101        // Default implementation returns payload as-is
102        // Specific resources can override to filter sensitive fields
103        payload
104    }
105
106    /// Get the provider name for telemetry
107    /// 
108    /// Returns the provider name from the client configuration if available.
109    fn provider(&self) -> Option<String> {
110        // This can be overridden by specific resources that have provider info
111        None
112    }
113
114    /// Create a telemetry event for a method call
115    /// 
116    /// This method creates a telemetry event with metadata about the current
117    /// execution context, including timestamp, environment, and provider information.
118    fn create_method_event(
119        &self,
120        function_name: &str,
121        provider: Option<&str>,
122    ) -> Option<TelemetryData> {
123        if !self.telemetry_context().allow_tracking {
124            return None;
125        }
126
127        let timestamp = SystemTime::now()
128            .duration_since(UNIX_EPOCH)
129            .unwrap()
130            .as_secs_f64();
131
132        // Use provider from method parameter or fall back to resource's provider
133        let provider_name = provider
134            .map(|s| s.to_string())
135            .or_else(|| self.provider());
136
137        Some(TelemetryData {
138            function_name: function_name.to_string(),
139            duration_ms: None,
140            timestamp: Some(timestamp),
141            props: None,
142            source: Some(SourceData {
143                host: None,
144                service: Some(ServiceType::Sdk),
145                language: Some(LanguageType::Rust),
146                version: Some(env!("CARGO_PKG_VERSION").to_string()),
147                platform: None,
148                environment: Some(match self.telemetry_context().environment {
149                    Environment::Development => EnvironmentType::Development,
150                    Environment::Production => EnvironmentType::Production,
151                    Environment::Staging => EnvironmentType::Staging,
152                }),
153            }),
154            metadata: Some(Metadata {
155                project_id: None,
156                provider: provider_name,
157            }),
158            error: None,
159        })
160    }
161
162    /// Push a telemetry event
163    fn push_telemetry_event(&self, event: Event) {
164        if self.telemetry_context().allow_tracking {
165            push_event(event);
166        }
167    }
168
169    /// Trace a method execution with telemetry
170    fn trace_method<F, R>(&self, function_name: &str, provider: Option<&str>, f: F) -> R
171    where
172        F: FnOnce() -> R,
173    {
174        let mut telemetry_data = self.create_method_event(function_name, provider);
175        let start_time = SystemTime::now();
176
177        let result = f();
178
179        if let Some(ref mut data) = telemetry_data {
180            let duration_ms = SystemTime::now()
181                .duration_since(start_time)
182                .unwrap()
183                .as_millis() as f64;
184
185            // Update telemetry data with duration
186            data.duration_ms = Some(duration_ms);
187
188            self.push_telemetry_event((EventType::Metric, data.clone()));
189        }
190
191        result
192    }
193
194    /// Trace a method execution with error handling
195    fn trace_method_with_error<F, R, E>(
196        &self,
197        function_name: &str,
198        provider: Option<&str>,
199        f: F,
200    ) -> Result<R, E>
201    where
202        F: FnOnce() -> Result<R, E>,
203        E: std::fmt::Display,
204    {
205        let mut telemetry_data = self.create_method_event(function_name, provider);
206        let start_time = SystemTime::now();
207
208        let result = f();
209
210        if let Some(ref mut data) = telemetry_data {
211            let duration_ms = SystemTime::now()
212                .duration_since(start_time)
213                .unwrap()
214                .as_millis() as f64;
215
216            // Update telemetry data with duration
217            data.duration_ms = Some(duration_ms);
218
219            // Add error information if result is an error
220            let event_type = if let Err(ref e) = result {
221                data.error = Some(ErrorData {
222                    name: std::any::type_name::<E>().to_string(),
223                    code: None,
224                    error_id: None,
225                    message: Some(e.to_string()),
226                    stack: None,
227                });
228                EventType::Error
229            } else {
230                EventType::Metric
231            };
232
233            self.push_telemetry_event((event_type, data.clone()));
234        }
235
236        result
237    }
238}
239
240/// Base resource structure that implements the Resource trait
241#[derive(Clone)]
242pub struct BaseResource {
243    /// Reference to the Composio client
244    pub client: Arc<ComposioClient>,
245    /// Telemetry context
246    pub telemetry_context: TelemetryContext,
247}
248
249impl BaseResource {
250    /// Create a new base resource
251    pub fn new(client: Arc<ComposioClient>) -> Self {
252        Self {
253            client,
254            telemetry_context: TelemetryContext::default(),
255        }
256    }
257
258    /// Create a new base resource with custom telemetry context
259    pub fn with_telemetry_context(
260        client: Arc<ComposioClient>,
261        telemetry_context: TelemetryContext,
262    ) -> Self {
263        Self {
264            client,
265            telemetry_context,
266        }
267    }
268}
269
270impl Resource for BaseResource {
271    fn client(&self) -> &ComposioClient {
272        &self.client
273    }
274
275    fn telemetry_context(&self) -> &TelemetryContext {
276        &self.telemetry_context
277    }
278}
279
280#[cfg(test)]
281mod tests {
282    use super::*;
283
284    #[test]
285    fn test_environment_from_env() {
286        // Test default (development)
287        let env = Environment::from_env();
288        assert_eq!(env.as_str(), "development");
289    }
290
291    #[test]
292    fn test_environment_as_str() {
293        assert_eq!(Environment::Development.as_str(), "development");
294        assert_eq!(Environment::Production.as_str(), "production");
295        assert_eq!(Environment::Staging.as_str(), "staging");
296    }
297
298    #[test]
299    fn test_telemetry_context_default() {
300        let ctx = TelemetryContext::default();
301        assert!(ctx.allow_tracking);
302    }
303
304    #[test]
305    fn test_telemetry_context_disable_enable() {
306        let mut ctx = TelemetryContext::default();
307        assert!(ctx.allow_tracking);
308
309        ctx.disable_tracking();
310        assert!(!ctx.allow_tracking);
311
312        ctx.enable_tracking();
313        assert!(ctx.allow_tracking);
314    }
315
316    #[test]
317    fn test_base_resource_creation() {
318        let client = Arc::new(
319            ComposioClient::builder()
320                .api_key("test_key")
321                .build()
322                .unwrap(),
323        );
324        let resource = BaseResource::new(client);
325
326        assert!(resource.telemetry_context.allow_tracking);
327    }
328}
329
330// Example usage of the Resource trait:
331// 
332// ```rust,no_run
333// use composio_sdk::models::base::{Resource, BaseResource, TelemetryContext};
334// use composio_sdk::client::ComposioClient;
335// use composio_sdk::config::ComposioConfig;
336// use std::sync::Arc;
337// 
338// // Create a custom resource that implements the Resource trait
339// struct MyCustomResource {
340//     base: BaseResource,
341// }
342// 
343// impl MyCustomResource {
344//     pub fn new(client: Arc<ComposioClient>) -> Self {
345//         Self {
346//             base: BaseResource::new(client),
347//         }
348//     }
349//     
350//     // Example method that uses telemetry tracing
351//     pub fn do_something(&self) -> Result<String, String> {
352//         self.trace_method_with_error("MyCustomResource.do_something", None, || {
353//             // Your business logic here
354//             Ok("Success!".to_string())
355//         })
356//     }
357// }
358// 
359// impl Resource for MyCustomResource {
360//     fn client(&self) -> &ComposioClient {
361//         self.base.client()
362//     }
363//     
364//     fn telemetry_context(&self) -> &TelemetryContext {
365//         self.base.telemetry_context()
366//     }
367// }
368// ```