Skip to main content

backbone_integrations/domain/entity/
integration_account.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use sqlx::FromRow;
4use uuid::Uuid;
5
6use super::OAuthProvider;
7use super::IntegrationAccountStatus;
8use super::AuditMetadata;
9
10/// Strongly-typed ID for IntegrationAccount
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
12#[serde(transparent)]
13pub struct IntegrationAccountId(pub Uuid);
14
15impl IntegrationAccountId {
16    pub fn new(id: Uuid) -> Self { Self(id) }
17    pub fn generate() -> Self { Self(Uuid::new_v4()) }
18    pub fn into_inner(self) -> Uuid { self.0 }
19}
20
21impl std::fmt::Display for IntegrationAccountId {
22    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23        write!(f, "{}", self.0)
24    }
25}
26
27impl std::str::FromStr for IntegrationAccountId {
28    type Err = uuid::Error;
29    fn from_str(s: &str) -> Result<Self, Self::Err> {
30        Ok(Self(Uuid::parse_str(s)?))
31    }
32}
33
34impl From<Uuid> for IntegrationAccountId {
35    fn from(id: Uuid) -> Self { Self(id) }
36}
37
38impl From<IntegrationAccountId> for Uuid {
39    fn from(id: IntegrationAccountId) -> Self { id.0 }
40}
41
42impl AsRef<Uuid> for IntegrationAccountId {
43    fn as_ref(&self) -> &Uuid { &self.0 }
44}
45
46impl std::ops::Deref for IntegrationAccountId {
47    type Target = Uuid;
48    fn deref(&self) -> &Self::Target { &self.0 }
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
52pub struct IntegrationAccount {
53    pub id: Uuid,
54    pub provider: OAuthProvider,
55    pub account_ref: String,
56    pub status: IntegrationAccountStatus,
57    pub scopes: String,
58    pub pkce_verifier: Option<String>,
59    pub expires_at: Option<DateTime<Utc>>,
60    pub last_refreshed_at: Option<DateTime<Utc>>,
61    #[serde(default)]
62    #[sqlx(json)]
63    pub metadata: AuditMetadata,
64}
65
66impl IntegrationAccount {
67    /// Create a builder for IntegrationAccount
68    pub fn builder() -> IntegrationAccountBuilder {
69        <IntegrationAccountBuilder as Default>::default()
70    }
71
72    /// Create a new IntegrationAccount with required fields
73    pub fn new(provider: OAuthProvider, account_ref: String, status: IntegrationAccountStatus, scopes: String) -> Self {
74        Self {
75            id: Uuid::new_v4(),
76            provider,
77            account_ref,
78            status,
79            scopes,
80            pkce_verifier: None,
81            expires_at: None,
82            last_refreshed_at: None,
83            metadata: AuditMetadata::default(),
84        }
85    }
86
87    /// Get the entity's unique identifier
88    pub fn id(&self) -> &Uuid {
89        &self.id
90    }
91
92    /// Get a strongly-typed ID for this entity
93    pub fn typed_id(&self) -> IntegrationAccountId {
94        IntegrationAccountId(self.id)
95    }
96
97    /// Get when this entity was created
98    pub fn created_at(&self) -> Option<&DateTime<Utc>> {
99        self.metadata.created_at.as_ref()
100    }
101
102    /// Get when this entity was last updated
103    pub fn updated_at(&self) -> Option<&DateTime<Utc>> {
104        self.metadata.updated_at.as_ref()
105    }
106
107    /// Check if this entity is soft deleted
108    pub fn is_deleted(&self) -> bool {
109        self.metadata.deleted_at.is_some()
110    }
111
112    /// Check if this entity is active (not deleted)
113    pub fn is_active(&self) -> bool {
114        self.metadata.deleted_at.is_none()
115    }
116
117    /// Get when this entity was deleted
118    pub fn deleted_at(&self) -> Option<&DateTime<Utc>> {
119        self.metadata.deleted_at.as_ref()
120    }
121
122    /// Get who created this entity
123    pub fn created_by(&self) -> Option<&Uuid> {
124        self.metadata.created_by.as_ref()
125    }
126
127    /// Get who last updated this entity
128    pub fn updated_by(&self) -> Option<&Uuid> {
129        self.metadata.updated_by.as_ref()
130    }
131
132    /// Get who deleted this entity
133    pub fn deleted_by(&self) -> Option<&Uuid> {
134        self.metadata.deleted_by.as_ref()
135    }
136
137    /// Get the current status
138    pub fn status(&self) -> &IntegrationAccountStatus {
139        &self.status
140    }
141
142
143    // ==========================================================
144    // Fluent Setters (with_* for optional fields)
145    // ==========================================================
146
147    /// Set the pkce_verifier field (chainable)
148    pub fn with_pkce_verifier(mut self, value: String) -> Self {
149        self.pkce_verifier = Some(value);
150        self
151    }
152
153    /// Set the expires_at field (chainable)
154    pub fn with_expires_at(mut self, value: DateTime<Utc>) -> Self {
155        self.expires_at = Some(value);
156        self
157    }
158
159    /// Set the last_refreshed_at field (chainable)
160    pub fn with_last_refreshed_at(mut self, value: DateTime<Utc>) -> Self {
161        self.last_refreshed_at = Some(value);
162        self
163    }
164
165    // ==========================================================
166    // Partial Update
167    // ==========================================================
168
169    /// Apply partial updates from a map of field name to JSON value
170    pub fn apply_patch(&mut self, fields: std::collections::HashMap<String, serde_json::Value>) {
171        for (key, value) in fields {
172            match key.as_str() {
173                "provider" => {
174                    if let Ok(v) = serde_json::from_value(value) { self.provider = v; }
175                }
176                "account_ref" => {
177                    if let Ok(v) = serde_json::from_value(value) { self.account_ref = v; }
178                }
179                "status" => {
180                    if let Ok(v) = serde_json::from_value(value) { self.status = v; }
181                }
182                "scopes" => {
183                    if let Ok(v) = serde_json::from_value(value) { self.scopes = v; }
184                }
185                "pkce_verifier" => {
186                    if let Ok(v) = serde_json::from_value(value) { self.pkce_verifier = v; }
187                }
188                "expires_at" => {
189                    if let Ok(v) = serde_json::from_value(value) { self.expires_at = v; }
190                }
191                "last_refreshed_at" => {
192                    if let Ok(v) = serde_json::from_value(value) { self.last_refreshed_at = v; }
193                }
194                _ => {} // ignore unknown fields
195            }
196        }
197    }
198
199}
200
201impl super::Entity for IntegrationAccount {
202    type Id = Uuid;
203
204    fn entity_id(&self) -> &Self::Id {
205        &self.id
206    }
207
208    fn entity_type() -> &'static str {
209        "IntegrationAccount"
210    }
211}
212
213impl backbone_core::PersistentEntity for IntegrationAccount {
214    fn entity_id(&self) -> String {
215        self.id.to_string()
216    }
217    fn set_entity_id(&mut self, id: String) {
218        if let Ok(uuid) = uuid::Uuid::parse_str(&id) {
219            self.id = uuid;
220        }
221    }
222    fn created_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
223        self.metadata.created_at
224    }
225    fn set_created_at(&mut self, ts: chrono::DateTime<chrono::Utc>) {
226        self.metadata.created_at = Some(ts);
227    }
228    fn updated_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
229        self.metadata.updated_at
230    }
231    fn set_updated_at(&mut self, ts: chrono::DateTime<chrono::Utc>) {
232        self.metadata.updated_at = Some(ts);
233    }
234    fn deleted_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
235        self.metadata.deleted_at
236    }
237    fn set_deleted_at(&mut self, ts: Option<chrono::DateTime<chrono::Utc>>) {
238        self.metadata.deleted_at = ts;
239    }
240}
241
242impl backbone_orm::EntityRepoMeta for IntegrationAccount {
243    fn column_types() -> std::collections::HashMap<String, String> {
244        let mut m = std::collections::HashMap::new();
245        m.insert("id".to_string(), "uuid".to_string());
246        m.insert("provider".to_string(), "o_auth_provider".to_string());
247        m.insert("status".to_string(), "integration_account_status".to_string());
248        m.insert("expires_at".to_string(), "timestamptz".to_string());
249        m.insert("last_refreshed_at".to_string(), "timestamptz".to_string());
250        m
251    }
252    fn search_fields() -> &'static [&'static str] {
253        &["account_ref", "scopes"]
254    }
255}
256
257/// Builder for IntegrationAccount entity
258///
259/// Provides a fluent API for constructing IntegrationAccount instances.
260/// System fields (id, metadata, timestamps) are auto-initialized.
261#[derive(Debug, Clone, Default)]
262pub struct IntegrationAccountBuilder {
263    provider: Option<OAuthProvider>,
264    account_ref: Option<String>,
265    status: Option<IntegrationAccountStatus>,
266    scopes: Option<String>,
267    pkce_verifier: Option<String>,
268    expires_at: Option<DateTime<Utc>>,
269    last_refreshed_at: Option<DateTime<Utc>>,
270}
271
272impl IntegrationAccountBuilder {
273    /// Set the provider field (required)
274    pub fn provider(mut self, value: OAuthProvider) -> Self {
275        self.provider = Some(value);
276        self
277    }
278
279    /// Set the account_ref field (required)
280    pub fn account_ref(mut self, value: String) -> Self {
281        self.account_ref = Some(value);
282        self
283    }
284
285    /// Set the status field (default: `IntegrationAccountStatus::default()`)
286    pub fn status(mut self, value: IntegrationAccountStatus) -> Self {
287        self.status = Some(value);
288        self
289    }
290
291    /// Set the scopes field (default: `"".to_string()`)
292    pub fn scopes(mut self, value: String) -> Self {
293        self.scopes = Some(value);
294        self
295    }
296
297    /// Set the pkce_verifier field (optional)
298    pub fn pkce_verifier(mut self, value: String) -> Self {
299        self.pkce_verifier = Some(value);
300        self
301    }
302
303    /// Set the expires_at field (optional)
304    pub fn expires_at(mut self, value: DateTime<Utc>) -> Self {
305        self.expires_at = Some(value);
306        self
307    }
308
309    /// Set the last_refreshed_at field (optional)
310    pub fn last_refreshed_at(mut self, value: DateTime<Utc>) -> Self {
311        self.last_refreshed_at = Some(value);
312        self
313    }
314
315    /// Build the IntegrationAccount entity
316    ///
317    /// Returns Err if any required field without a default is missing.
318    pub fn build(self) -> Result<IntegrationAccount, String> {
319        let provider = self.provider.ok_or_else(|| "provider is required".to_string())?;
320        let account_ref = self.account_ref.ok_or_else(|| "account_ref is required".to_string())?;
321
322        Ok(IntegrationAccount {
323            id: Uuid::new_v4(),
324            provider,
325            account_ref,
326            status: self.status.unwrap_or_default(),
327            scopes: self.scopes.unwrap_or("".to_string()),
328            pkce_verifier: self.pkce_verifier,
329            expires_at: self.expires_at,
330            last_refreshed_at: self.last_refreshed_at,
331            metadata: AuditMetadata::default(),
332        })
333    }
334}