| 2297 | } |
| 2298 | |
| 2299 | pub async fn query_analytics_events( |
| 2300 | &self, |
| 2301 | query: &AnalyticsEventQuery, |
| 2302 | ) -> Result<Vec<AnalyticsEventRecord>, String> { |
| 2303 | if query.limit == 0 { |
| 2304 | return Ok(Vec::new()); |
| 2305 | } |
| 2306 | |
| 2307 | let mut sql = String::from( |
| 2308 | "SELECT id, provider, project_id, session_id, timestamp, event_kind, |
| 2309 | hook_name, tool_name, tool_category, skill_name, hint_category, |
| 2310 | hint_id, outcome, metadata_json |
| 2311 | FROM analytics_events", |
| 2312 | ); |
| 2313 | let mut clauses = Vec::new(); |
| 2314 | let mut values = Vec::new(); |
| 2315 | for (column, value) in [ |
| 2316 | ("provider", query.provider.as_deref()), |
| 2317 | ("project_id", query.project_id.as_deref()), |
| 2318 | ("session_id", query.session_id.as_deref()), |
| 2319 | ("event_kind", query.event_kind.as_deref()), |
| 2320 | ] { |
| 2321 | push_optional_analytics_filter(&mut clauses, &mut values, column, value); |
| 2322 | } |
| 2323 | if let Some(since) = query.since { |
| 2324 | values.push(Value::Integer(since)); |
| 2325 | clauses.push(format!("timestamp >= ?{}", values.len())); |
| 2326 | } |
| 2327 | if !clauses.is_empty() { |
| 2328 | sql.push_str(" WHERE "); |
| 2329 | sql.push_str(&clauses.join(" AND ")); |
| 2330 | } |
| 2331 | values.push(Value::Integer( |
| 2332 | i64::try_from(query.limit).unwrap_or(i64::MAX), |
| 2333 | )); |
| 2334 | let limit_param = values.len(); |
| 2335 | let _ = write!( |
| 2336 | sql, |
| 2337 | " ORDER BY timestamp DESC, id DESC LIMIT ?{limit_param}" |
| 2338 | ); |
| 2339 | |
| 2340 | let mut rows = self |
| 2341 | .conn |
| 2342 | .query(&sql, libsql::params_from_iter(values)) |
| 2343 | .await |
| 2344 | .map_err(|e| format!("failed to query analytics events: {e}"))?; |
| 2345 | let mut events = Vec::new(); |
| 2346 | while let Some(row) = rows |
| 2347 | .next() |
| 2348 | .await |
| 2349 | .map_err(|e| format!("failed to read analytics events: {e}"))? |
| 2350 | { |
| 2351 | let event = row_to_analytics_event(&row) |
| 2352 | .ok_or_else(|| "failed to decode analytics event row".to_string())?; |
| 2353 | events.push(event); |
| 2354 | } |
| 2355 | events.reverse(); |
| 2356 | Ok(events) |