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    // <<< CUSTOM METHODS START >>>
200    // <<< CUSTOM METHODS END >>>
201}
202
203impl super::Entity for IntegrationAccount {
204    type Id = Uuid;
205
206    fn entity_id(&self) -> &Self::Id {
207        &self.id
208    }
209
210    fn entity_type() -> &'static str {
211        "IntegrationAccount"
212    }
213}
214
215impl backbone_core::PersistentEntity for IntegrationAccount {
216    fn entity_id(&self) -> String {
217        self.id.to_string()
218    }
219    fn set_entity_id(&mut self, id: String) {
220        if let Ok(uuid) = uuid::Uuid::parse_str(&id) {
221            self.id = uuid;
222        }
223    }
224    fn created_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
225        self.metadata.created_at
226    }
227    fn set_created_at(&mut self, ts: chrono::DateTime<chrono::Utc>) {
228        self.metadata.created_at = Some(ts);
229    }
230    fn updated_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
231        self.metadata.updated_at
232    }
233    fn set_updated_at(&mut self, ts: chrono::DateTime<chrono::Utc>) {
234        self.metadata.updated_at = Some(ts);
235    }
236    fn deleted_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
237        self.metadata.deleted_at
238    }
239    fn set_deleted_at(&mut self, ts: Option<chrono::DateTime<chrono::Utc>>) {
240        self.metadata.deleted_at = ts;
241    }
242}
243
244impl backbone_orm::EntityRepoMeta for IntegrationAccount {
245    fn column_types() -> std::collections::HashMap<String, String> {
246        let mut m = std::collections::HashMap::new();
247        m.insert("id".to_string(), "uuid".to_string());
248        m.insert("provider".to_string(), "o_auth_provider".to_string());
249        m.insert("status".to_string(), "integration_account_status".to_string());
250        m.insert("expires_at".to_string(), "timestamptz".to_string());
251        m.insert("last_refreshed_at".to_string(), "timestamptz".to_string());
252        m
253    }
254    fn search_fields() -> &'static [&'static str] {
255        &["account_ref", "scopes"]
256    }
257}
258
259/// Builder for IntegrationAccount entity
260///
261/// Provides a fluent API for constructing IntegrationAccount instances.
262/// System fields (id, metadata, timestamps) are auto-initialized.
263#[derive(Debug, Clone, Default)]
264pub struct IntegrationAccountBuilder {
265    provider: Option<OAuthProvider>,
266    account_ref: Option<String>,
267    status: Option<IntegrationAccountStatus>,
268    scopes: Option<String>,
269    pkce_verifier: Option<String>,
270    expires_at: Option<DateTime<Utc>>,
271    last_refreshed_at: Option<DateTime<Utc>>,
272}
273
274impl IntegrationAccountBuilder {
275    /// Set the provider field (required)
276    pub fn provider(mut self, value: OAuthProvider) -> Self {
277        self.provider = Some(value);
278        self
279    }
280
281    /// Set the account_ref field (required)
282    pub fn account_ref(mut self, value: String) -> Self {
283        self.account_ref = Some(value);
284        self
285    }
286
287    /// Set the status field (default: `IntegrationAccountStatus::default()`)
288    pub fn status(mut self, value: IntegrationAccountStatus) -> Self {
289        self.status = Some(value);
290        self
291    }
292
293    /// Set the scopes field (default: `"".to_string()`)
294    pub fn scopes(mut self, value: String) -> Self {
295        self.scopes = Some(value);
296        self
297    }
298
299    /// Set the pkce_verifier field (optional)
300    pub fn pkce_verifier(mut self, value: String) -> Self {
301        self.pkce_verifier = Some(value);
302        self
303    }
304
305    /// Set the expires_at field (optional)
306    pub fn expires_at(mut self, value: DateTime<Utc>) -> Self {
307        self.expires_at = Some(value);
308        self
309    }
310
311    /// Set the last_refreshed_at field (optional)
312    pub fn last_refreshed_at(mut self, value: DateTime<Utc>) -> Self {
313        self.last_refreshed_at = Some(value);
314        self
315    }
316
317    /// Build the IntegrationAccount entity
318    ///
319    /// Returns Err if any required field without a default is missing.
320    pub fn build(self) -> Result<IntegrationAccount, String> {
321        let provider = self.provider.ok_or_else(|| "provider is required".to_string())?;
322        let account_ref = self.account_ref.ok_or_else(|| "account_ref is required".to_string())?;
323
324        Ok(IntegrationAccount {
325            id: Uuid::new_v4(),
326            provider,
327            account_ref,
328            status: self.status.unwrap_or_default(),
329            scopes: self.scopes.unwrap_or("".to_string()),
330            pkce_verifier: self.pkce_verifier,
331            expires_at: self.expires_at,
332            last_refreshed_at: self.last_refreshed_at,
333            metadata: AuditMetadata::default(),
334        })
335    }
336}