mcpr_integrations/store/query/
clients.rs1use rusqlite::params;
4use serde::Serialize;
5
6use super::QueryEngine;
7
8pub struct ClientsParams {
10 pub proxy: Option<String>,
12 pub since_ts: i64,
14}
15
16#[derive(Debug, Clone, Serialize)]
18pub struct ClientRow {
19 pub client_name: Option<String>,
20 pub client_version: Option<String>,
21 pub client_platform: Option<String>,
22 pub sessions: i64,
23 pub total_calls: i64,
24 pub total_errors: i64,
25 pub error_pct: f64,
26 pub first_seen: i64,
27 pub last_seen: i64,
28}
29
30impl QueryEngine {
31 pub fn clients(&self, params: &ClientsParams) -> Result<Vec<ClientRow>, rusqlite::Error> {
33 let sql = "
34 SELECT
35 client_name, client_version, client_platform,
36 COUNT(DISTINCT session_id) AS sessions,
37 SUM(total_calls) AS total_calls,
38 SUM(total_errors) AS total_errors,
39 CASE WHEN SUM(total_calls) > 0
40 THEN SUM(total_errors) * 100.0 / SUM(total_calls)
41 ELSE 0.0
42 END AS error_pct,
43 MIN(started_at) AS first_seen,
44 MAX(last_seen_at) AS last_seen
45 FROM sessions
46 WHERE (?1 IS NULL OR proxy = ?1) AND started_at >= ?2
47 GROUP BY client_name, client_version, client_platform
48 ORDER BY total_calls DESC
49 ";
50
51 let mut stmt = self.conn().prepare(sql)?;
52 let rows = stmt.query_map(params![params.proxy, params.since_ts], |row| {
53 Ok(ClientRow {
54 client_name: row.get(0)?,
55 client_version: row.get(1)?,
56 client_platform: row.get(2)?,
57 sessions: row.get(3)?,
58 total_calls: row.get(4)?,
59 total_errors: row.get(5)?,
60 error_pct: row.get(6)?,
61 first_seen: row.get(7)?,
62 last_seen: row.get(8)?,
63 })
64 })?;
65
66 rows.collect()
67 }
68}