sos_database/entity/
audit.rs

1use crate::Error;
2use async_sqlite::rusqlite::{Connection, Error as SqlError, Row};
3use sos_audit::AuditEvent;
4use sos_core::{events::EventKind, AccountId, UtcDateTime};
5use sql_query_builder as sql;
6use std::ops::Deref;
7
8/// Audit row.
9#[doc(hidden)]
10#[derive(Default, Debug)]
11pub struct AuditRow {
12    /// Row identifier.
13    pub row_id: i64,
14    /// RFC3339 date and time.
15    created_at: String,
16    /// Account identifier.
17    account_id: String,
18    /// Event kind string.
19    event_kind: String,
20    /// Associated data encoded as JSON.
21    data: Option<String>,
22}
23
24impl TryFrom<&AuditEvent> for AuditRow {
25    type Error = Error;
26
27    fn try_from(value: &AuditEvent) -> Result<Self, Self::Error> {
28        let data = if let Some(data) = value.data() {
29            Some(serde_json::to_string(data)?)
30        } else {
31            None
32        };
33        Ok(Self {
34            created_at: value.time().to_rfc3339()?,
35            account_id: value.account_id().to_string(),
36            event_kind: value.event_kind().to_string(),
37            data,
38            ..Default::default()
39        })
40    }
41}
42
43impl<'a> TryFrom<&Row<'a>> for AuditRow {
44    type Error = SqlError;
45    fn try_from(row: &Row<'a>) -> Result<Self, Self::Error> {
46        Ok(AuditRow {
47            row_id: row.get(0)?,
48            created_at: row.get(1)?,
49            account_id: row.get(2)?,
50            event_kind: row.get(3)?,
51            data: row.get(4)?,
52        })
53    }
54}
55
56/// Audit record.
57pub struct AuditRecord {
58    /// Row identifier.
59    pub row_id: i64,
60    /// Audit event.
61    pub event: AuditEvent,
62}
63
64impl TryFrom<AuditRow> for AuditRecord {
65    type Error = Error;
66
67    fn try_from(value: AuditRow) -> Result<Self, Self::Error> {
68        let data = if let Some(data) = value.data {
69            Some(serde_json::from_str(&data)?)
70        } else {
71            None
72        };
73
74        let date_time = UtcDateTime::parse_rfc3339(&value.created_at)?;
75        let account_id: AccountId = value.account_id.parse()?;
76        let event_kind: EventKind = value.event_kind.parse()?;
77        let event = AuditEvent::new(date_time, event_kind, account_id, data);
78
79        Ok(AuditRecord {
80            row_id: value.row_id,
81            event,
82        })
83    }
84}
85
86/// Audit entity.
87pub struct AuditEntity<'conn, C>
88where
89    C: Deref<Target = Connection>,
90{
91    conn: &'conn C,
92}
93
94impl<'conn, C> AuditEntity<'conn, C>
95where
96    C: Deref<Target = Connection>,
97{
98    /// Create a new audit entity.
99    pub fn new(conn: &'conn C) -> Self {
100        Self { conn }
101    }
102
103    /// Create audit logs in the database.
104    pub fn insert_audit_log(
105        &self,
106        event: &AuditRow,
107    ) -> std::result::Result<(), SqlError> {
108        let query = sql::Insert::new()
109            .insert_into("audit_logs (created_at, account_identifier, event_kind, event_data)")
110            .values("(?1, ?2, ?3, ?4)");
111        let mut stmt = self.conn.prepare_cached(&query.as_string())?;
112        stmt.execute((
113            &event.created_at,
114            &event.account_id,
115            &event.event_kind,
116            &event.data,
117        ))?;
118
119        Ok(())
120    }
121
122    /// Create audit logs in the database.
123    pub fn insert_audit_logs(
124        &self,
125        events: &[AuditRow],
126    ) -> std::result::Result<(), SqlError> {
127        for event in events {
128            self.insert_audit_log(event)?;
129        }
130        Ok(())
131    }
132}