koprogo_api/infrastructure/web/handlers/
gdpr_art30_handlers.rs1use crate::infrastructure::web::{AppState, AuthenticatedUser};
2use actix_web::{get, web, HttpResponse, Responder};
3use serde::Serialize;
4
5#[derive(Debug, Serialize)]
7pub struct DataProcessingActivityDto {
8 pub id: String,
9 pub activity_name: String,
10 pub controller_name: String,
11 pub purpose: String,
12 pub legal_basis: String,
13 pub data_categories: Vec<String>,
14 pub data_subjects: Vec<String>,
15 pub recipients: Vec<String>,
16 pub retention_period: String,
17 pub security_measures: String,
18 pub created_at: String,
19 pub updated_at: String,
20}
21
22#[derive(Debug, Serialize)]
24pub struct DataProcessorAgreementDto {
25 pub id: String,
26 pub processor_name: String,
27 pub service_description: String,
28 pub dpa_signed_at: Option<String>,
29 pub dpa_url: Option<String>,
30 pub transfer_mechanism: Option<String>,
31 pub data_categories: Vec<String>,
32 pub certifications: Option<Vec<String>>,
33 pub created_at: String,
34 pub updated_at: String,
35}
36
37#[derive(Debug, Serialize)]
39pub struct ProcessingActivitiesResponse {
40 pub activities: Vec<DataProcessingActivityDto>,
41 pub total: i64,
42}
43
44#[derive(Debug, Serialize)]
46pub struct ProcessorsResponse {
47 pub processors: Vec<DataProcessorAgreementDto>,
48 pub total: i64,
49}
50
51#[get("/admin/gdpr/processing-register")]
60pub async fn list_processing_activities(
61 data: web::Data<AppState>,
62 auth: AuthenticatedUser,
63) -> impl Responder {
64 if auth.role != "superadmin" {
66 return HttpResponse::Forbidden().json(serde_json::json!({
67 "error": "Access denied. SuperAdmin role required."
68 }));
69 }
70
71 match data.pool.acquire().await {
73 Ok(mut conn) => {
74 match sqlx::query_as!(
75 ProcessingActivityRow,
76 r#"
77 SELECT
78 id,
79 activity_name,
80 controller_name,
81 purpose,
82 legal_basis,
83 data_categories,
84 data_subjects,
85 recipients,
86 retention_period,
87 security_measures,
88 created_at,
89 updated_at
90 FROM data_processing_activities
91 ORDER BY created_at DESC
92 "#
93 )
94 .fetch_all(&mut *conn)
95 .await
96 {
97 Ok(rows) => {
98 let total = rows.len() as i64;
99 let activities: Vec<DataProcessingActivityDto> = rows
100 .into_iter()
101 .map(|row| DataProcessingActivityDto {
102 id: row.id.to_string(),
103 activity_name: row.activity_name,
104 controller_name: row.controller_name,
105 purpose: row.purpose,
106 legal_basis: row.legal_basis,
107 data_categories: row.data_categories.unwrap_or_default(),
108 data_subjects: row.data_subjects.unwrap_or_default(),
109 recipients: row.recipients.unwrap_or_default(),
110 retention_period: row.retention_period,
111 security_measures: row.security_measures,
112 created_at: row.created_at.to_rfc3339(),
113 updated_at: row.updated_at.to_rfc3339(),
114 })
115 .collect();
116
117 HttpResponse::Ok().json(ProcessingActivitiesResponse { activities, total })
118 }
119 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({
120 "error": format!("Failed to fetch processing activities: {}", e)
121 })),
122 }
123 }
124 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({
125 "error": format!("Database connection error: {}", e)
126 })),
127 }
128}
129
130#[get("/admin/gdpr/processors")]
139pub async fn list_sub_processors(
140 data: web::Data<AppState>,
141 auth: AuthenticatedUser,
142) -> impl Responder {
143 if auth.role != "superadmin" {
145 return HttpResponse::Forbidden().json(serde_json::json!({
146 "error": "Access denied. SuperAdmin role required."
147 }));
148 }
149
150 match data.pool.acquire().await {
152 Ok(mut conn) => {
153 match sqlx::query_as!(
154 ProcessorAgreementRow,
155 r#"
156 SELECT
157 id,
158 processor_name,
159 service_description,
160 dpa_signed_at,
161 dpa_url,
162 transfer_mechanism,
163 data_categories,
164 certifications,
165 created_at,
166 updated_at
167 FROM data_processor_agreements
168 ORDER BY processor_name ASC
169 "#
170 )
171 .fetch_all(&mut *conn)
172 .await
173 {
174 Ok(rows) => {
175 let total = rows.len() as i64;
176 let processors: Vec<DataProcessorAgreementDto> = rows
177 .into_iter()
178 .map(|row| DataProcessorAgreementDto {
179 id: row.id.to_string(),
180 processor_name: row.processor_name,
181 service_description: row.service_description,
182 dpa_signed_at: row.dpa_signed_at.map(|dt| dt.to_rfc3339()),
183 dpa_url: row.dpa_url,
184 transfer_mechanism: row.transfer_mechanism,
185 data_categories: row.data_categories.unwrap_or_default(),
186 certifications: row.certifications,
187 created_at: row.created_at.to_rfc3339(),
188 updated_at: row.updated_at.to_rfc3339(),
189 })
190 .collect();
191
192 HttpResponse::Ok().json(ProcessorsResponse { processors, total })
193 }
194 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({
195 "error": format!("Failed to fetch sub-processors: {}", e)
196 })),
197 }
198 }
199 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({
200 "error": format!("Database connection error: {}", e)
201 })),
202 }
203}
204
205struct ProcessingActivityRow {
207 id: uuid::Uuid,
208 activity_name: String,
209 controller_name: String,
210 purpose: String,
211 legal_basis: String,
212 data_categories: Option<Vec<String>>,
213 data_subjects: Option<Vec<String>>,
214 recipients: Option<Vec<String>>,
215 retention_period: String,
216 security_measures: String,
217 created_at: chrono::DateTime<chrono::Utc>,
218 updated_at: chrono::DateTime<chrono::Utc>,
219}
220
221struct ProcessorAgreementRow {
222 id: uuid::Uuid,
223 processor_name: String,
224 service_description: String,
225 dpa_signed_at: Option<chrono::DateTime<chrono::Utc>>,
226 dpa_url: Option<String>,
227 transfer_mechanism: Option<String>,
228 data_categories: Option<Vec<String>>,
229 certifications: Option<Vec<String>>,
230 created_at: chrono::DateTime<chrono::Utc>,
231 updated_at: chrono::DateTime<chrono::Utc>,
232}
233
234#[cfg(test)]
235mod tests {
236 #[test]
237 fn test_handler_structure_list_processing_activities() {
238 }
240
241 #[test]
242 fn test_handler_structure_list_sub_processors() {
243 }
245}