use std::marker::PhantomData;
use async_trait::async_trait;
use gatekeep::{AuditEntry, AuditSink};
use serde::{Deserialize, Serialize};
use sqlx::Pool;
use crate::GatekeepSqlxBackend;
#[cfg(feature = "mysql")]
mod mysql;
#[cfg(feature = "postgres")]
mod postgres;
#[cfg(feature = "sqlite")]
mod sqlite;
mod support;
#[cfg(feature = "mysql")]
pub use self::mysql::MySqlDecisionAuditRepository;
#[cfg(feature = "postgres")]
pub use self::postgres::PgDecisionAuditRepository;
#[cfg(feature = "sqlite")]
pub use self::sqlite::SqliteDecisionAuditRepository;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct DecisionAuditRecord {
pub id: i64,
pub entry: AuditEntry,
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum SqlxAuditError {
#[error(transparent)]
Sqlx(#[from] sqlx::Error),
#[error(transparent)]
Json(#[from] serde_json::Error),
#[error("audit row id {id} does not fit in i64")]
IdOverflow {
id: u64,
},
}
#[async_trait]
pub trait SqlxAuditStore<B>: Send + Sync
where
B: GatekeepSqlxBackend,
{
async fn record_decision_audit(&self, entry: &AuditEntry) -> Result<i64, SqlxAuditError>;
async fn decision_audit_records(
&self,
after_id: Option<i64>,
limit: i64,
) -> Result<Vec<DecisionAuditRecord>, SqlxAuditError>;
}
#[derive(Debug)]
pub struct SqlxDecisionAuditRepository<B>
where
B: GatekeepSqlxBackend,
{
pub(crate) pool: Pool<B::Database>,
backend: PhantomData<fn() -> B>,
}
impl<B> Clone for SqlxDecisionAuditRepository<B>
where
B: GatekeepSqlxBackend,
{
fn clone(&self) -> Self {
Self {
pool: self.pool.clone(),
backend: PhantomData,
}
}
}
impl<B> SqlxDecisionAuditRepository<B>
where
B: GatekeepSqlxBackend,
{
pub(crate) const fn from_pool(pool: Pool<B::Database>) -> Self {
Self {
pool,
backend: PhantomData,
}
}
}
#[async_trait]
impl<B> AuditSink for SqlxDecisionAuditRepository<B>
where
B: GatekeepSqlxBackend,
Self: SqlxAuditStore<B>,
{
type Error = SqlxAuditError;
async fn record(&self, entry: &AuditEntry) -> Result<(), Self::Error> {
self.record_decision_audit(entry).await?;
Ok(())
}
}