sos_database/entity/
audit.rs1use 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#[doc(hidden)]
10#[derive(Default, Debug)]
11pub struct AuditRow {
12 pub row_id: i64,
14 created_at: String,
16 account_id: String,
18 event_kind: String,
20 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
56pub struct AuditRecord {
58 pub row_id: i64,
60 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
86pub 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 pub fn new(conn: &'conn C) -> Self {
100 Self { conn }
101 }
102
103 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 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}