gatekeep_sqlx/audit/
mod.rs1use std::marker::PhantomData;
2
3use async_trait::async_trait;
4use gatekeep::{AuditEntry, AuditSink};
5use serde::{Deserialize, Serialize};
6use sqlx::Pool;
7
8use crate::GatekeepSqlxBackend;
9
10#[cfg(feature = "mysql")]
11mod mysql;
12#[cfg(feature = "postgres")]
13mod postgres;
14#[cfg(feature = "sqlite")]
15mod sqlite;
16mod support;
17
18#[cfg(feature = "mysql")]
19pub use self::mysql::MySqlDecisionAuditRepository;
20#[cfg(feature = "postgres")]
21pub use self::postgres::PgDecisionAuditRepository;
22#[cfg(feature = "sqlite")]
23pub use self::sqlite::SqliteDecisionAuditRepository;
24
25#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
27pub struct DecisionAuditRecord {
28 pub id: i64,
30 pub entry: AuditEntry,
32}
33
34#[derive(Debug, thiserror::Error)]
36#[non_exhaustive]
37pub enum SqlxAuditError {
38 #[error(transparent)]
40 Sqlx(#[from] sqlx::Error),
41 #[error(transparent)]
43 Json(#[from] serde_json::Error),
44 #[error("audit row id {id} does not fit in i64")]
46 IdOverflow {
47 id: u64,
49 },
50}
51
52#[async_trait]
54pub trait SqlxAuditStore<B>: Send + Sync
55where
56 B: GatekeepSqlxBackend,
57{
58 async fn record_decision_audit(&self, entry: &AuditEntry) -> Result<i64, SqlxAuditError>;
60
61 async fn decision_audit_records(
63 &self,
64 after_id: Option<i64>,
65 limit: i64,
66 ) -> Result<Vec<DecisionAuditRecord>, SqlxAuditError>;
67}
68
69#[derive(Debug)]
71pub struct SqlxDecisionAuditRepository<B>
72where
73 B: GatekeepSqlxBackend,
74{
75 pub(crate) pool: Pool<B::Database>,
76 backend: PhantomData<fn() -> B>,
77}
78
79impl<B> Clone for SqlxDecisionAuditRepository<B>
80where
81 B: GatekeepSqlxBackend,
82{
83 fn clone(&self) -> Self {
84 Self {
85 pool: self.pool.clone(),
86 backend: PhantomData,
87 }
88 }
89}
90
91impl<B> SqlxDecisionAuditRepository<B>
92where
93 B: GatekeepSqlxBackend,
94{
95 pub(crate) const fn from_pool(pool: Pool<B::Database>) -> Self {
96 Self {
97 pool,
98 backend: PhantomData,
99 }
100 }
101}
102
103#[async_trait]
104impl<B> AuditSink for SqlxDecisionAuditRepository<B>
105where
106 B: GatekeepSqlxBackend,
107 Self: SqlxAuditStore<B>,
108{
109 type Error = SqlxAuditError;
110
111 async fn record(&self, entry: &AuditEntry) -> Result<(), Self::Error> {
112 self.record_decision_audit(entry).await?;
113 Ok(())
114 }
115}