use crate::sql::error::SqlError;
use chrono::{DateTime, Utc};
use scouter_types::{
CustomMetricServerRecord, PsiServerRecord, RecordType, ServerRecord, ServerRecords,
SpcServerRecord,
};
use sqlx::{postgres::PgRow, Row};
fn spc_record_from_row(row: &PgRow) -> Result<SpcServerRecord, SqlError> {
Ok(SpcServerRecord {
created_at: row.try_get("created_at")?,
name: row.try_get("name")?,
space: row.try_get("space")?,
version: row.try_get("version")?,
feature: row.try_get("feature")?,
value: row.try_get("value")?,
})
}
fn psi_record_from_row(row: &PgRow) -> Result<PsiServerRecord, SqlError> {
let bin_id: i32 = row.try_get("bin_id")?;
let bin_count: i32 = row.try_get("bin_count")?;
Ok(PsiServerRecord {
created_at: row.try_get("created_at")?,
name: row.try_get("name")?,
space: row.try_get("space")?,
version: row.try_get("version")?,
feature: row.try_get("feature")?,
bin_id: bin_id as usize,
bin_count: bin_count as usize,
})
}
fn custom_record_from_row(row: &PgRow) -> Result<CustomMetricServerRecord, SqlError> {
Ok(CustomMetricServerRecord {
created_at: row.try_get("created_at")?,
name: row.try_get("name")?,
space: row.try_get("space")?,
version: row.try_get("version")?,
metric: row.try_get("metric")?,
value: row.try_get("value")?,
})
}
pub fn pg_rows_to_server_records(
rows: &[PgRow],
record_type: &RecordType,
) -> Result<ServerRecords, SqlError> {
let convert_fn = match record_type {
RecordType::Spc => |row| Ok(ServerRecord::Spc(spc_record_from_row(row)?)),
RecordType::Psi => |row| Ok(ServerRecord::Psi(psi_record_from_row(row)?)),
RecordType::Custom => |row| Ok(ServerRecord::Custom(custom_record_from_row(row)?)),
_ => return Err(SqlError::InvalidRecordTypeError),
};
let records: Result<Vec<ServerRecord>, SqlError> = rows.iter().map(convert_fn).collect();
records.map(ServerRecords::new)
}
#[derive(Debug)]
pub struct QueryTimestamps {
pub archived_range: Option<(DateTime<Utc>, DateTime<Utc>)>,
pub archived_minutes: Option<i32>,
pub current_minutes: Option<i32>,
}
pub fn split_custom_interval(
begin_datetime: DateTime<Utc>,
end_datetime: DateTime<Utc>,
retention_period: &i32,
) -> Result<QueryTimestamps, SqlError> {
if begin_datetime >= end_datetime {
return Err(SqlError::InvalidDateRangeError);
}
let retention_date = Utc::now() - chrono::Duration::days(*retention_period as i64);
let mut timestamps = QueryTimestamps {
archived_range: None,
current_minutes: None,
archived_minutes: None,
};
if begin_datetime < retention_date {
let archive_end = if end_datetime <= retention_date {
end_datetime
} else {
retention_date
};
timestamps.archived_range = Some((begin_datetime, archive_end));
}
if end_datetime > retention_date {
let current_begin = if begin_datetime < retention_date {
retention_date
} else {
begin_datetime
};
let minutes = end_datetime
.signed_duration_since(current_begin)
.num_minutes() as i32;
timestamps.current_minutes = Some(minutes);
}
if let Some((begin, end)) = timestamps.archived_range {
timestamps.archived_minutes = Some(end.signed_duration_since(begin).num_minutes() as i32);
}
Ok(timestamps)
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::Duration;
#[test]
fn test_split_custom_interval() {
let now = Utc::now();
let retention_period = &30;
let result = split_custom_interval(
now - Duration::days(60),
now - Duration::days(40),
retention_period,
)
.unwrap();
assert!(result.archived_range.is_some());
assert!(result.current_minutes.is_none());
let result = split_custom_interval(
now - Duration::days(20),
now - Duration::days(1),
retention_period,
)
.unwrap();
assert!(result.archived_range.is_none());
assert!(result.current_minutes.is_some());
let result = split_custom_interval(
now - Duration::days(60),
now - Duration::days(1),
retention_period,
)
.unwrap();
assert!(result.archived_range.is_some());
assert!(result.current_minutes.is_some());
let result = split_custom_interval(
now - Duration::days(1),
now - Duration::days(2),
retention_period,
);
assert!(result.is_err());
}
}