1use 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#[derive(Debug, Clone)]
17pub enum Environment {
18 Development,
19 Production,
20 Staging,
21}
22
23impl Environment {
24 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 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#[derive(Debug, Clone)]
45pub struct TelemetryContext {
46 pub allow_tracking: bool,
48 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 pub fn new(allow_tracking: bool) -> Self {
64 Self {
65 allow_tracking,
66 environment: Environment::from_env(),
67 }
68 }
69
70 pub fn disable_tracking(&mut self) {
72 self.allow_tracking = false;
73 }
74
75 pub fn enable_tracking(&mut self) {
77 self.allow_tracking = true;
78 }
79}
80
81pub trait Resource {
86 fn client(&self) -> &ComposioClient;
88
89 fn telemetry_context(&self) -> &TelemetryContext;
91
92 fn sanitize_payload<T>(&self, payload: T) -> T
98 where
99 T: serde::Serialize + serde::de::DeserializeOwned,
100 {
101 payload
104 }
105
106 fn provider(&self) -> Option<String> {
110 None
112 }
113
114 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 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 fn push_telemetry_event(&self, event: Event) {
164 if self.telemetry_context().allow_tracking {
165 push_event(event);
166 }
167 }
168
169 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 data.duration_ms = Some(duration_ms);
187
188 self.push_telemetry_event((EventType::Metric, data.clone()));
189 }
190
191 result
192 }
193
194 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 data.duration_ms = Some(duration_ms);
218
219 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#[derive(Clone)]
242pub struct BaseResource {
243 pub client: Arc<ComposioClient>,
245 pub telemetry_context: TelemetryContext,
247}
248
249impl BaseResource {
250 pub fn new(client: Arc<ComposioClient>) -> Self {
252 Self {
253 client,
254 telemetry_context: TelemetryContext::default(),
255 }
256 }
257
258 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 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