use crate::api::kasl_server::AgentState;
use crate::db::db::Db;
use anyhow::Result;
use chrono::{DateTime, Utc};
use rusqlite::{Connection, OptionalExtension};
const RECORD_ACCEPTED: &str = "INSERT INTO server_pulse
(id, attempted_at, next_at, sent_at, state, stale_after_seconds, clock_skew_seconds, error)
VALUES (1, ?1, ?2, ?1, ?3, ?4, ?5, NULL)
ON CONFLICT(id) DO UPDATE SET
attempted_at = excluded.attempted_at,
next_at = excluded.next_at,
sent_at = excluded.sent_at,
state = excluded.state,
stale_after_seconds = excluded.stale_after_seconds,
clock_skew_seconds = excluded.clock_skew_seconds,
error = NULL";
const RECORD_FAILED: &str = "INSERT INTO server_pulse (id, attempted_at, next_at, error)
VALUES (1, ?1, ?2, ?3)
ON CONFLICT(id) DO UPDATE SET
attempted_at = excluded.attempted_at,
next_at = excluded.next_at,
error = excluded.error";
const SELECT_LAST: &str = "SELECT attempted_at, next_at, sent_at, state, stale_after_seconds, clock_skew_seconds, error FROM server_pulse WHERE id = 1";
const DELETE_ALL: &str = "DELETE FROM server_pulse";
#[derive(Debug, Clone, PartialEq)]
pub struct PulseRecord {
pub attempted_at: DateTime<Utc>,
pub next_at: DateTime<Utc>,
pub sent_at: Option<DateTime<Utc>>,
pub state: Option<AgentState>,
pub stale_after_seconds: Option<i64>,
pub clock_skew_seconds: Option<i64>,
pub error: Option<String>,
}
pub struct ServerPulse {
pub conn: Connection,
}
impl ServerPulse {
pub fn new() -> Result<Self> {
Ok(ServerPulse { conn: Db::new()?.conn })
}
pub fn record_accepted(
&mut self,
at: DateTime<Utc>,
next_at: DateTime<Utc>,
state: AgentState,
stale_after_seconds: i64,
clock_skew_seconds: i64,
) -> Result<()> {
self.conn.execute(
RECORD_ACCEPTED,
rusqlite::params![at, next_at, state.as_str(), stale_after_seconds, clock_skew_seconds],
)?;
Ok(())
}
pub fn record_failed(&mut self, at: DateTime<Utc>, next_at: DateTime<Utc>, error: &str) -> Result<()> {
self.conn.execute(RECORD_FAILED, rusqlite::params![at, next_at, error])?;
Ok(())
}
pub fn last(&self) -> Result<Option<PulseRecord>> {
let record = self
.conn
.query_row(SELECT_LAST, [], |row| {
let state: Option<String> = row.get(3)?;
Ok(PulseRecord {
attempted_at: row.get(0)?,
next_at: row.get(1)?,
sent_at: row.get(2)?,
state: state.as_deref().and_then(AgentState::parse),
stale_after_seconds: row.get(4)?,
clock_skew_seconds: row.get(5)?,
error: row.get(6)?,
})
})
.optional()?;
Ok(record)
}
pub fn clear(&mut self) -> Result<()> {
self.conn.execute(DELETE_ALL, [])?;
Ok(())
}
}