gatekeep_sqlx/audit/
postgres.rs1use async_trait::async_trait;
2use gatekeep::{AuditEntry, AuditSink};
3use sqlx::{PgPool, Postgres, Transaction};
4
5use dovecote::EnqueueOutcome;
6use dovecote_sqlx_postgres::PostgresDovecote;
7
8use super::{
9 DecisionAuditConfig, DecisionAuditConfigError, DecisionAuditEventError, event_from_entry,
10};
11
12#[derive(Clone)]
14pub struct PgDovecoteAudit {
15 dovecote: PostgresDovecote,
16 config: DecisionAuditConfig,
17}
18
19impl PgDovecoteAudit {
20 pub fn new(pool: PgPool, source: impl Into<String>) -> Result<Self, DecisionAuditConfigError> {
26 let config = DecisionAuditConfig::new(source)?;
27 Ok(Self::from_config(pool, config))
28 }
29
30 #[must_use]
32 pub fn from_config(pool: PgPool, config: DecisionAuditConfig) -> Self {
33 Self {
34 dovecote: PostgresDovecote::new(pool),
35 config,
36 }
37 }
38
39 #[must_use]
41 pub const fn config(&self) -> &DecisionAuditConfig {
42 &self.config
43 }
44
45 pub async fn check_schema(&self) -> Result<(), dovecote_sqlx_postgres::SchemaError> {
52 self.dovecote.check_schema().await
53 }
54
55 pub async fn record_decision_audit(
65 &self,
66 entry: &AuditEntry,
67 ) -> Result<EnqueueOutcome, PgDovecoteAuditError> {
68 let mut transaction = self.dovecote.pool().begin().await?;
69 let outcome = self
70 .record_decision_audit_in_transaction(&mut transaction, entry)
71 .await?;
72 transaction.commit().await?;
73 Ok(outcome)
74 }
75
76 pub async fn record_decision_audit_in_transaction(
87 &self,
88 transaction: &mut Transaction<'_, Postgres>,
89 entry: &AuditEntry,
90 ) -> Result<EnqueueOutcome, PgDovecoteAuditError> {
91 let (tenant, event) = event_from_entry(&self.config, entry)?;
92 Ok(self
93 .dovecote
94 .for_tenant(tenant)
95 .enqueue(transaction, event)
96 .await?)
97 }
98}
99
100#[derive(Debug, thiserror::Error)]
102#[non_exhaustive]
103pub enum PgDovecoteAuditError {
104 #[error(transparent)]
106 Event(#[from] DecisionAuditEventError),
107 #[error(transparent)]
109 Dovecote(#[from] dovecote_sqlx_postgres::EnqueueError),
110 #[error(transparent)]
112 Sqlx(#[from] sqlx::Error),
113}
114
115#[async_trait]
116impl AuditSink for PgDovecoteAudit {
117 type Error = PgDovecoteAuditError;
118
119 async fn record(&self, entry: &AuditEntry) -> Result<(), Self::Error> {
120 self.record_decision_audit(entry).await.map(|_| ())
121 }
122}