koprogo_api/domain/entities/
gdpr_rectification.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use uuid::Uuid;
4
5/// GDPR Article 16 - Right to Rectification
6///
7/// Represents a user's request to correct inaccurate personal data.
8/// This entity tracks which fields need correction and their new values.
9#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
10pub struct GdprRectificationRequest {
11    pub id: Uuid,
12    pub user_id: Uuid,
13    pub organization_id: Option<Uuid>,
14    pub requested_at: DateTime<Utc>,
15    pub status: RectificationStatus,
16    pub changes: Vec<FieldChange>,
17    pub reason: Option<String>,
18    pub processed_at: Option<DateTime<Utc>>,
19    pub processed_by: Option<Uuid>,
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
23pub enum RectificationStatus {
24    Pending,
25    Approved,
26    Rejected,
27    Applied,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
31pub struct FieldChange {
32    pub entity_type: String, // "User", "Owner", etc.
33    pub entity_id: Uuid,
34    pub field_name: String,
35    pub old_value: Option<String>,
36    pub new_value: String,
37}
38
39impl GdprRectificationRequest {
40    /// Create a new rectification request
41    pub fn new(
42        user_id: Uuid,
43        organization_id: Option<Uuid>,
44        changes: Vec<FieldChange>,
45        reason: Option<String>,
46    ) -> Self {
47        Self {
48            id: Uuid::new_v4(),
49            user_id,
50            organization_id,
51            requested_at: Utc::now(),
52            status: RectificationStatus::Pending,
53            changes,
54            reason,
55            processed_at: None,
56            processed_by: None,
57        }
58    }
59
60    /// Approve the rectification request
61    pub fn approve(&mut self, admin_id: Uuid) {
62        self.status = RectificationStatus::Approved;
63        self.processed_at = Some(Utc::now());
64        self.processed_by = Some(admin_id);
65    }
66
67    /// Reject the rectification request
68    pub fn reject(&mut self, admin_id: Uuid) {
69        self.status = RectificationStatus::Rejected;
70        self.processed_at = Some(Utc::now());
71        self.processed_by = Some(admin_id);
72    }
73
74    /// Mark as applied after changes are made
75    pub fn mark_applied(&mut self) {
76        self.status = RectificationStatus::Applied;
77    }
78
79    /// Check if request is still pending
80    pub fn is_pending(&self) -> bool {
81        matches!(self.status, RectificationStatus::Pending)
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn test_create_rectification_request() {
91        let user_id = Uuid::new_v4();
92        let org_id = Uuid::new_v4();
93        let changes = vec![FieldChange {
94            entity_type: "User".to_string(),
95            entity_id: user_id,
96            field_name: "email".to_string(),
97            old_value: Some("old@example.com".to_string()),
98            new_value: "new@example.com".to_string(),
99        }];
100
101        let request = GdprRectificationRequest::new(
102            user_id,
103            Some(org_id),
104            changes.clone(),
105            Some("Email address was incorrect".to_string()),
106        );
107
108        assert_eq!(request.user_id, user_id);
109        assert_eq!(request.organization_id, Some(org_id));
110        assert!(request.is_pending());
111        assert_eq!(request.changes.len(), 1);
112        assert_eq!(request.changes[0].new_value, "new@example.com");
113    }
114
115    #[test]
116    fn test_approve_request() {
117        let user_id = Uuid::new_v4();
118        let admin_id = Uuid::new_v4();
119        let changes = vec![FieldChange {
120            entity_type: "User".to_string(),
121            entity_id: user_id,
122            field_name: "first_name".to_string(),
123            old_value: Some("Jon".to_string()),
124            new_value: "John".to_string(),
125        }];
126
127        let mut request = GdprRectificationRequest::new(user_id, None, changes, None);
128        request.approve(admin_id);
129
130        assert_eq!(request.status, RectificationStatus::Approved);
131        assert!(request.processed_at.is_some());
132        assert_eq!(request.processed_by, Some(admin_id));
133        assert!(!request.is_pending());
134    }
135
136    #[test]
137    fn test_reject_request() {
138        let user_id = Uuid::new_v4();
139        let admin_id = Uuid::new_v4();
140        let changes = vec![FieldChange {
141            entity_type: "User".to_string(),
142            entity_id: user_id,
143            field_name: "email".to_string(),
144            old_value: Some("old@example.com".to_string()),
145            new_value: "invalid-email".to_string(),
146        }];
147
148        let mut request = GdprRectificationRequest::new(user_id, None, changes, None);
149        request.reject(admin_id);
150
151        assert_eq!(request.status, RectificationStatus::Rejected);
152        assert!(request.processed_at.is_some());
153        assert_eq!(request.processed_by, Some(admin_id));
154    }
155
156    #[test]
157    fn test_mark_applied() {
158        let user_id = Uuid::new_v4();
159        let admin_id = Uuid::new_v4();
160        let changes = vec![FieldChange {
161            entity_type: "User".to_string(),
162            entity_id: user_id,
163            field_name: "last_name".to_string(),
164            old_value: Some("Smith".to_string()),
165            new_value: "Smyth".to_string(),
166        }];
167
168        let mut request = GdprRectificationRequest::new(user_id, None, changes, None);
169        request.approve(admin_id);
170        request.mark_applied();
171
172        assert_eq!(request.status, RectificationStatus::Applied);
173    }
174}