1use chrono::{DateTime, Utc};
2use scouter_types::{
3 alert::Alert,
4 custom::{BinnedCustomMetric, BinnedCustomMetricStats},
5 get_utc_datetime,
6 psi::{FeatureBinProportion, FeatureBinProportionResult},
7 RecordType,
8};
9use serde::{Deserialize, Serialize};
10use sqlx::{postgres::PgRow, Error, FromRow, Row};
11use std::collections::BTreeMap;
12use std::collections::HashMap;
13
14#[derive(Serialize, Deserialize, Debug, Clone)]
15pub struct DriftRecord {
16 pub created_at: DateTime<Utc>,
17 pub name: String,
18 pub space: String,
19 pub version: String,
20 pub feature: String,
21 pub value: f64,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct SpcFeatureResult {
26 pub feature: String,
27 pub created_at: Vec<DateTime<Utc>>,
28 pub values: Vec<f64>,
29}
30
31impl<'r> FromRow<'r, PgRow> for SpcFeatureResult {
32 fn from_row(row: &'r PgRow) -> Result<Self, Error> {
33 Ok(SpcFeatureResult {
34 feature: row.try_get("feature")?,
35 created_at: row.try_get("created_at")?,
36 values: row.try_get("values")?,
37 })
38 }
39}
40
41pub struct FeatureBinProportionWrapper(pub FeatureBinProportion);
42
43impl<'r> FromRow<'r, PgRow> for FeatureBinProportionWrapper {
44 fn from_row(row: &'r PgRow) -> Result<Self, Error> {
45 let value: serde_json::Value = row.try_get("bins")?;
46 let bins: BTreeMap<usize, f64> = serde_json::from_value(value).unwrap_or_default();
47
48 Ok(FeatureBinProportionWrapper(FeatureBinProportion {
49 feature: row.try_get("feature")?,
50 bins,
51 }))
52 }
53}
54
55pub struct BinnedCustomMetricWrapper(pub BinnedCustomMetric);
56
57impl<'r> FromRow<'r, PgRow> for BinnedCustomMetricWrapper {
58 fn from_row(row: &'r PgRow) -> Result<Self, Error> {
59 let stats_json: Vec<serde_json::Value> = row.try_get("stats")?;
60
61 let stats: Vec<BinnedCustomMetricStats> = stats_json
62 .into_iter()
63 .map(|value| serde_json::from_value(value).unwrap_or_default())
64 .collect();
65
66 Ok(BinnedCustomMetricWrapper(BinnedCustomMetric {
67 metric: row.try_get("metric")?,
68 created_at: row.try_get("created_at")?,
69 stats,
70 }))
71 }
72}
73
74pub struct AlertWrapper(pub Alert);
75
76impl<'r> FromRow<'r, PgRow> for AlertWrapper {
77 fn from_row(row: &'r PgRow) -> Result<Self, Error> {
78 let alert_value: serde_json::Value = row.try_get("alert")?;
79 let alert: BTreeMap<String, String> =
80 serde_json::from_value(alert_value).unwrap_or_default();
81
82 Ok(AlertWrapper(Alert {
83 created_at: row.try_get("created_at")?,
84 name: row.try_get("name")?,
85 space: row.try_get("space")?,
86 version: row.try_get("version")?,
87 alert,
88 entity_name: row.try_get("entity_name")?,
89 id: row.try_get("id")?,
90 drift_type: row.try_get("drift_type")?,
91 active: row.try_get("active")?,
92 }))
93 }
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct TaskRequest {
98 pub name: String,
99 pub space: String,
100 pub version: String,
101 pub profile: String,
102 pub drift_type: String,
103 pub previous_run: DateTime<Utc>,
104 pub schedule: String,
105 pub uid: String,
106}
107
108impl<'r> FromRow<'r, PgRow> for TaskRequest {
109 fn from_row(row: &'r PgRow) -> Result<Self, Error> {
110 let profile: serde_json::Value = row.try_get("profile")?;
111
112 Ok(TaskRequest {
113 name: row.try_get("name")?,
114 space: row.try_get("space")?,
115 version: row.try_get("version")?,
116 profile: profile.to_string(),
117 drift_type: row.try_get("drift_type")?,
118 previous_run: row.try_get("previous_run")?,
119 schedule: row.try_get("schedule")?,
120 uid: row.try_get("uid")?,
121 })
122 }
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct ObservabilityResult {
127 pub route_name: String,
128 pub created_at: Vec<DateTime<Utc>>,
129 pub p5: Vec<f64>,
130 pub p25: Vec<f64>,
131 pub p50: Vec<f64>,
132 pub p95: Vec<f64>,
133 pub p99: Vec<f64>,
134 pub total_request_count: Vec<i64>,
135 pub total_error_count: Vec<i64>,
136 pub error_latency: Vec<f64>,
137 pub status_counts: Vec<HashMap<String, i64>>,
138}
139
140impl<'r> FromRow<'r, PgRow> for ObservabilityResult {
141 fn from_row(row: &'r PgRow) -> Result<Self, Error> {
142 let status_counts: Vec<serde_json::Value> = row.try_get("status_counts")?;
144
145 let status_counts: Vec<HashMap<String, i64>> = status_counts
147 .into_iter()
148 .map(|value| serde_json::from_value(value).unwrap_or_default())
149 .collect();
150
151 Ok(ObservabilityResult {
152 route_name: row.try_get("route_name")?,
153 created_at: row.try_get("created_at")?,
154 p5: row.try_get("p5")?,
155 p25: row.try_get("p25")?,
156 p50: row.try_get("p50")?,
157 p95: row.try_get("p95")?,
158 p99: row.try_get("p99")?,
159 total_request_count: row.try_get("total_request_count")?,
160 total_error_count: row.try_get("total_error_count")?,
161 error_latency: row.try_get("error_latency")?,
162 status_counts,
163 })
164 }
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct BinProportion {
169 pub bin_id: usize,
170 pub proportion: f64,
171}
172
173#[derive(Debug)]
174pub struct FeatureBinProportionResultWrapper(pub FeatureBinProportionResult);
175
176impl<'r> FromRow<'r, PgRow> for FeatureBinProportionResultWrapper {
177 fn from_row(row: &'r PgRow) -> Result<Self, Error> {
178 let bin_proportions_json: Vec<serde_json::Value> = row.try_get("bin_proportions")?;
180
181 let bin_proportions: Vec<BTreeMap<usize, f64>> = bin_proportions_json
183 .into_iter()
184 .map(|json| serde_json::from_value(json).unwrap_or_default())
185 .collect();
186
187 let overall_proportions_json: serde_json::Value = row.try_get("overall_proportions")?;
188 let overall_proportions: BTreeMap<usize, f64> =
189 serde_json::from_value(overall_proportions_json).unwrap_or_default();
190
191 Ok(FeatureBinProportionResultWrapper(
192 FeatureBinProportionResult {
193 feature: row.try_get("feature")?,
194 created_at: row.try_get("created_at")?,
195 bin_proportions,
196 overall_proportions,
197 },
198 ))
199 }
200}
201#[derive(Debug, Clone, FromRow)]
202pub struct Entity {
203 pub space: String,
204 pub name: String,
205 pub version: String,
206 pub begin_timestamp: DateTime<Utc>,
207 pub end_timestamp: DateTime<Utc>,
208}
209
210impl Entity {
211 pub fn get_write_path(&self, record_type: &RecordType) -> String {
212 format!(
213 "{}/{}/{}/{}",
214 self.space, self.name, self.version, record_type
215 )
216 }
217}
218
219#[derive(Debug, Serialize, Deserialize, Clone)]
220pub struct User {
221 pub id: Option<i32>,
222 pub created_at: DateTime<Utc>,
223 pub active: bool,
224 pub username: String,
225 pub password_hash: String,
226 pub hashed_recovery_codes: Vec<String>,
227 pub permissions: Vec<String>,
228 pub group_permissions: Vec<String>,
229 pub role: String,
230 pub favorite_spaces: Vec<String>,
231 pub refresh_token: Option<String>,
232 pub email: String,
233 pub updated_at: DateTime<Utc>,
234}
235
236impl User {
237 #[allow(clippy::too_many_arguments)]
238 pub fn new(
239 username: String,
240 password_hash: String,
241 email: String,
242 hashed_recovery_codes: Vec<String>,
243 permissions: Option<Vec<String>>,
244 group_permissions: Option<Vec<String>>,
245 role: Option<String>,
246 favorite_spaces: Option<Vec<String>>,
247 ) -> Self {
248 let created_at = get_utc_datetime();
249
250 User {
251 id: None,
252 created_at,
253 active: true,
254 username,
255 password_hash,
256 hashed_recovery_codes,
257 permissions: permissions.unwrap_or(vec!["read:all".to_string()]),
258 group_permissions: group_permissions.unwrap_or(vec!["user".to_string()]),
259 favorite_spaces: favorite_spaces.unwrap_or_default(),
260 role: role.unwrap_or("user".to_string()),
261 refresh_token: None,
262 email,
263 updated_at: created_at,
264 }
265 }
266}
267
268impl FromRow<'_, PgRow> for User {
269 fn from_row(row: &PgRow) -> Result<Self, sqlx::Error> {
270 let id = row.try_get("id")?;
271 let created_at = row.try_get("created_at")?;
272 let updated_at = row.try_get("updated_at")?;
273 let active = row.try_get("active")?;
274 let username = row.try_get("username")?;
275 let password_hash = row.try_get("password_hash")?;
276 let email = row.try_get("email")?;
277 let role = row.try_get("role")?;
278 let refresh_token = row.try_get("refresh_token")?;
279
280 let group_permissions: Vec<String> =
281 serde_json::from_value(row.try_get("group_permissions")?).unwrap_or_default();
282
283 let permissions: Vec<String> =
284 serde_json::from_value(row.try_get("permissions")?).unwrap_or_default();
285
286 let hashed_recovery_codes: Vec<String> =
287 serde_json::from_value(row.try_get("hashed_recovery_codes")?).unwrap_or_default();
288
289 let favorite_spaces: Vec<String> =
290 serde_json::from_value(row.try_get("favorite_spaces")?).unwrap_or_default();
291
292 Ok(User {
293 id,
294 created_at,
295 updated_at,
296 active,
297 username,
298 password_hash,
299 email,
300 role,
301 refresh_token,
302 hashed_recovery_codes,
303 permissions,
304 group_permissions,
305 favorite_spaces,
306 })
307 }
308}
309
310#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
311pub struct UpdateAlertResult {
312 pub id: i32,
313 pub active: bool,
314 pub updated_at: DateTime<Utc>,
315}