1use crate::sql::error::SqlError;
2use chrono::{DateTime, Utc};
3use scouter_types::{
4 CustomMetricServerRecord, PsiServerRecord, RecordType, ServerRecord, ServerRecords,
5 SpcServerRecord,
6};
7use sqlx::{postgres::PgRow, Row};
8
9fn spc_record_from_row(row: &PgRow) -> Result<SpcServerRecord, SqlError> {
11 Ok(SpcServerRecord {
12 created_at: row.try_get("created_at")?,
13 name: row.try_get("name")?,
14 space: row.try_get("space")?,
15 version: row.try_get("version")?,
16 feature: row.try_get("feature")?,
17 value: row.try_get("value")?,
18 })
19}
20
21fn psi_record_from_row(row: &PgRow) -> Result<PsiServerRecord, SqlError> {
23 let bin_id: i32 = row.try_get("bin_id")?;
24 let bin_count: i32 = row.try_get("bin_count")?;
25
26 Ok(PsiServerRecord {
27 created_at: row.try_get("created_at")?,
28 name: row.try_get("name")?,
29 space: row.try_get("space")?,
30 version: row.try_get("version")?,
31 feature: row.try_get("feature")?,
32 bin_id: bin_id as usize,
33 bin_count: bin_count as usize,
34 })
35}
36
37fn custom_record_from_row(row: &PgRow) -> Result<CustomMetricServerRecord, SqlError> {
39 Ok(CustomMetricServerRecord {
40 created_at: row.try_get("created_at")?,
41 name: row.try_get("name")?,
42 space: row.try_get("space")?,
43 version: row.try_get("version")?,
44 metric: row.try_get("metric")?,
45 value: row.try_get("value")?,
46 })
47}
48
49pub fn pg_rows_to_server_records(
61 rows: &[PgRow],
62 record_type: &RecordType,
63) -> Result<ServerRecords, SqlError> {
64 let convert_fn = match record_type {
67 RecordType::Spc => |row| Ok(ServerRecord::Spc(spc_record_from_row(row)?)),
68 RecordType::Psi => |row| Ok(ServerRecord::Psi(psi_record_from_row(row)?)),
69 RecordType::Custom => |row| Ok(ServerRecord::Custom(custom_record_from_row(row)?)),
70 _ => return Err(SqlError::InvalidRecordTypeError),
71 };
72
73 let records: Result<Vec<ServerRecord>, SqlError> = rows.iter().map(convert_fn).collect();
75
76 records.map(ServerRecords::new)
78}
79
80#[derive(Debug)]
81pub struct QueryTimestamps {
82 pub archived_range: Option<(DateTime<Utc>, DateTime<Utc>)>,
84
85 pub archived_minutes: Option<i32>,
86
87 pub current_minutes: Option<i32>,
89}
90
91pub fn split_custom_interval(
115 begin_datetime: DateTime<Utc>,
116 end_datetime: DateTime<Utc>,
117 retention_period: &i32,
118) -> Result<QueryTimestamps, SqlError> {
119 if begin_datetime >= end_datetime {
120 return Err(SqlError::InvalidDateRangeError);
121 }
122
123 let retention_date = Utc::now() - chrono::Duration::days(*retention_period as i64);
124 let mut timestamps = QueryTimestamps {
125 archived_range: None,
126 current_minutes: None,
127 archived_minutes: None,
128 };
129
130 if begin_datetime < retention_date {
132 let archive_end = if end_datetime <= retention_date {
133 end_datetime
134 } else {
135 retention_date
136 };
137 timestamps.archived_range = Some((begin_datetime, archive_end));
138 }
139
140 if end_datetime > retention_date {
142 let current_begin = if begin_datetime < retention_date {
143 retention_date
144 } else {
145 begin_datetime
146 };
147 let minutes = end_datetime
148 .signed_duration_since(current_begin)
149 .num_minutes() as i32;
150 timestamps.current_minutes = Some(minutes);
151 }
152
153 if let Some((begin, end)) = timestamps.archived_range {
155 timestamps.archived_minutes = Some(end.signed_duration_since(begin).num_minutes() as i32);
156 }
157
158 Ok(timestamps)
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164 use chrono::Duration;
165
166 #[test]
167 fn test_split_custom_interval() {
168 let now = Utc::now();
169 let retention_period = &30; let result = split_custom_interval(
173 now - Duration::days(60),
174 now - Duration::days(40),
175 retention_period,
176 )
177 .unwrap();
178 assert!(result.archived_range.is_some());
179 assert!(result.current_minutes.is_none());
180
181 let result = split_custom_interval(
183 now - Duration::days(20),
184 now - Duration::days(1),
185 retention_period,
186 )
187 .unwrap();
188 assert!(result.archived_range.is_none());
189 assert!(result.current_minutes.is_some());
190
191 let result = split_custom_interval(
193 now - Duration::days(60),
194 now - Duration::days(1),
195 retention_period,
196 )
197 .unwrap();
198 assert!(result.archived_range.is_some());
199 assert!(result.current_minutes.is_some());
200
201 let result = split_custom_interval(
203 now - Duration::days(1),
204 now - Duration::days(2),
205 retention_period,
206 );
207 assert!(result.is_err());
208 }
209}