reinfer_client/resources/
audit.rs

1use chrono::{DateTime, Utc};
2
3use crate::{Continuation, DatasetId, DatasetName, ProjectName, UserEmail, UserId, Username};
4
5use super::{comment::CommentTimestampFilter, project::Id as ProjectId};
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Deserialize, Serialize)]
9pub struct AuditQueryFilter {
10    pub timestamp: CommentTimestampFilter,
11}
12
13#[derive(PartialEq, Eq, Debug, Clone, Deserialize, Serialize)]
14pub struct AuditEventId(pub String);
15
16#[derive(PartialEq, Eq, Debug, Clone, Deserialize, Serialize)]
17pub struct AuditEventType(pub String);
18
19#[derive(PartialEq, Eq, Debug, Clone, Deserialize, Serialize)]
20pub struct AuditTenantName(pub String);
21
22#[derive(PartialEq, Eq, Debug, Clone, Deserialize, Serialize)]
23pub struct AuditTenantId(pub String);
24
25#[derive(Debug, Clone, Deserialize, Serialize)]
26pub struct AuditQueryRequest {
27    pub filter: AuditQueryFilter,
28    #[serde(skip_serializing_if = "Option::is_none", default)]
29    pub continuation: Option<Continuation>,
30}
31
32#[derive(Debug, Clone, Deserialize, Serialize)]
33pub struct AuditEvent {
34    actor_user_id: UserId,
35    actor_tenant_id: AuditTenantId,
36    #[serde(skip_serializing_if = "Vec::is_empty", default)]
37    dataset_ids: Vec<DatasetId>,
38    event_id: AuditEventId,
39    event_type: AuditEventType,
40    #[serde(skip_serializing_if = "Vec::is_empty", default)]
41    project_ids: Vec<ProjectId>,
42    tenant_ids: Vec<AuditTenantId>,
43    timestamp: DateTime<Utc>,
44}
45
46#[derive(Debug, Clone, Deserialize, Serialize)]
47pub struct PrintableAuditEvent {
48    pub actor_email: UserEmail,
49    pub actor_tenant_name: AuditTenantName,
50    pub event_type: AuditEventType,
51    pub dataset_names: Vec<DatasetName>,
52    pub event_id: AuditEventId,
53    pub project_names: Vec<ProjectName>,
54    pub tenant_names: Vec<AuditTenantName>,
55    pub timestamp: DateTime<Utc>,
56}
57
58#[derive(Debug, Clone, Deserialize, Serialize)]
59struct AuditDataset {
60    id: DatasetId,
61    name: DatasetName,
62    project_id: ProjectId,
63    title: String,
64}
65
66#[derive(Debug, Clone, Deserialize, Serialize)]
67struct AuditProject {
68    id: ProjectId,
69    name: ProjectName,
70    tenant_id: AuditTenantId,
71}
72
73#[derive(Debug, Clone, Deserialize, Serialize)]
74struct AuditTenant {
75    id: AuditTenantId,
76    name: AuditTenantName,
77}
78
79#[derive(Debug, Clone, Deserialize, Serialize)]
80struct AuditUser {
81    display_name: Username,
82    email: UserEmail,
83    id: UserId,
84    tenant_id: AuditTenantId,
85    username: Username,
86}
87
88#[derive(Debug, Clone, Deserialize, Serialize)]
89pub struct AuditQueryResponse {
90    audit_events: Vec<AuditEvent>,
91    projects: Vec<AuditProject>,
92    pub continuation: Option<Continuation>,
93    datasets: Vec<AuditDataset>,
94    tenants: Vec<AuditTenant>,
95    users: Vec<AuditUser>,
96}
97
98impl AuditQueryResponse {
99    pub fn into_iter_printable(self) -> QueryResponseIterator {
100        QueryResponseIterator {
101            response: self,
102            index: 0,
103        }
104    }
105
106    fn get_user(&self, user_id: &UserId) -> Option<&AuditUser> {
107        self.users.iter().find(|user| user.id == *user_id)
108    }
109
110    fn get_dataset(&self, dataset_id: &DatasetId) -> Option<&AuditDataset> {
111        self.datasets
112            .iter()
113            .find(|dataset| dataset.id == *dataset_id)
114    }
115
116    fn get_project(&self, project_id: &ProjectId) -> Option<&AuditProject> {
117        self.projects
118            .iter()
119            .find(|project| project.id == *project_id)
120    }
121
122    fn get_tenant(&self, tenant_id: &AuditTenantId) -> Option<&AuditTenant> {
123        self.tenants.iter().find(|tenant| tenant.id == *tenant_id)
124    }
125}
126
127pub struct QueryResponseIterator {
128    response: AuditQueryResponse,
129    index: usize,
130}
131
132impl Iterator for QueryResponseIterator {
133    type Item = PrintableAuditEvent;
134    fn next(&mut self) -> Option<Self::Item> {
135        let event = self.response.audit_events.get(self.index)?;
136
137        let actor_email = &self
138            .response
139            .get_user(&event.actor_user_id)
140            .unwrap_or_else(|| panic!("Could not find user for id `{}`", event.actor_user_id.0))
141            .email;
142
143        let dataset_names = event
144            .dataset_ids
145            .iter()
146            .map(|dataset_id| {
147                &self
148                    .response
149                    .get_dataset(dataset_id)
150                    .unwrap_or_else(|| panic!("Could not get dataset for id `{}`", dataset_id.0))
151                    .name
152            })
153            .cloned()
154            .collect();
155
156        let project_names = event
157            .project_ids
158            .iter()
159            .map(|project_id| {
160                &self
161                    .response
162                    .get_project(project_id)
163                    .unwrap_or_else(|| panic!("Could not get project for id `{}`", project_id.0))
164                    .name
165            })
166            .cloned()
167            .collect();
168
169        let tenant_names = event
170            .tenant_ids
171            .iter()
172            .map(|tenant_id| {
173                &self
174                    .response
175                    .get_tenant(tenant_id)
176                    .unwrap_or_else(|| panic!("Could not get tenant for id `{}`", tenant_id.0))
177                    .name
178            })
179            .cloned()
180            .collect();
181
182        let actor_tenant_name = &self
183            .response
184            .get_tenant(&event.actor_tenant_id)
185            .unwrap_or_else(|| panic!("Could not get tenant for id `{}`", event.actor_tenant_id.0))
186            .name;
187
188        self.index += 1;
189
190        Some(PrintableAuditEvent {
191            event_type: event.event_type.clone(),
192            actor_tenant_name: actor_tenant_name.clone(),
193            event_id: event.event_id.clone(),
194            timestamp: event.timestamp,
195            actor_email: actor_email.clone(),
196            dataset_names,
197            project_names,
198            tenant_names,
199        })
200    }
201}