backbone_integrations/presentation/dto/
integration_account_dto.rs1use serde::{Deserialize, Serialize};
9use uuid::Uuid;
10use chrono::{DateTime, Utc};
11
12#[cfg(feature = "openapi")]
13#[cfg(feature = "openapi")]
14use utoipa::ToSchema;
15
16#[cfg(feature = "validation")]
17use validator::Validate;
18
19use crate::domain::entity::IntegrationAccount;
20use crate::domain::entity::AuditMetadata;
21use crate::domain::entity::IntegrationAccountStatus;
22use crate::domain::entity::OAuthProvider;
23
24#[derive(Debug, Clone, Deserialize)]
33#[cfg_attr(feature = "openapi", derive(ToSchema))]
34#[cfg_attr(feature = "validation", derive(Validate))]
35#[serde(rename_all = "camelCase")]
36pub struct CreateIntegrationAccountDto {
37 pub provider: OAuthProvider,
38 #[cfg_attr(feature = "openapi", schema(example = "example"))]
39 #[serde(alias = "account_ref")]
40 pub account_ref: String,
41 pub status: IntegrationAccountStatus,
42 #[cfg_attr(feature = "openapi", schema(example = "example"))]
43 pub scopes: String,
44 #[serde(default, skip_serializing_if = "Option::is_none", alias = "pkce_verifier")]
45 pub pkce_verifier: Option<String>,
46 #[serde(default, skip_serializing_if = "Option::is_none", alias = "expires_at")]
47 pub expires_at: Option<DateTime<Utc>>,
48 #[serde(default, skip_serializing_if = "Option::is_none", alias = "last_refreshed_at")]
49 pub last_refreshed_at: Option<DateTime<Utc>>,
50}
51
52#[derive(Debug, Clone, Deserialize)]
61#[cfg_attr(feature = "openapi", derive(ToSchema))]
62#[cfg_attr(feature = "validation", derive(Validate))]
63#[serde(rename_all = "camelCase")]
64pub struct UpdateIntegrationAccountDto {
65 pub provider: OAuthProvider,
66 #[cfg_attr(feature = "openapi", schema(example = "example"))]
67 #[serde(alias = "account_ref")]
68 pub account_ref: String,
69 pub status: IntegrationAccountStatus,
70 #[cfg_attr(feature = "openapi", schema(example = "example"))]
71 pub scopes: String,
72 #[serde(default, skip_serializing_if = "Option::is_none", alias = "pkce_verifier")]
73 pub pkce_verifier: Option<String>,
74 #[serde(default, skip_serializing_if = "Option::is_none", alias = "expires_at")]
75 pub expires_at: Option<DateTime<Utc>>,
76 #[serde(default, skip_serializing_if = "Option::is_none", alias = "last_refreshed_at")]
77 pub last_refreshed_at: Option<DateTime<Utc>>,
78}
79
80#[derive(Debug, Clone, Default, Deserialize)]
89#[cfg_attr(feature = "openapi", derive(ToSchema))]
90#[cfg_attr(feature = "validation", derive(Validate))]
91#[serde(rename_all = "camelCase")]
92pub struct PatchIntegrationAccountDto {
93 #[serde(skip_serializing_if = "Option::is_none")]
94 pub provider: Option<OAuthProvider>,
95 #[cfg_attr(feature = "openapi", schema(example = "example"))]
96 #[serde(skip_serializing_if = "Option::is_none", alias = "account_ref")]
97 pub account_ref: Option<String>,
98 #[serde(skip_serializing_if = "Option::is_none")]
99 pub status: Option<IntegrationAccountStatus>,
100 #[cfg_attr(feature = "openapi", schema(example = "example"))]
101 #[serde(skip_serializing_if = "Option::is_none")]
102 pub scopes: Option<String>,
103 #[serde(skip_serializing_if = "Option::is_none", alias = "pkce_verifier")]
104 pub pkce_verifier: Option<String>,
105 #[serde(skip_serializing_if = "Option::is_none", alias = "expires_at")]
106 pub expires_at: Option<DateTime<Utc>>,
107 #[serde(skip_serializing_if = "Option::is_none", alias = "last_refreshed_at")]
108 pub last_refreshed_at: Option<DateTime<Utc>>,
109}
110
111impl PatchIntegrationAccountDto {
112 pub fn has_changes(&self) -> bool {
114 self.provider.is_some() || self.account_ref.is_some() || self.status.is_some() || self.scopes.is_some() || self.pkce_verifier.is_some() || self.expires_at.is_some() || self.last_refreshed_at.is_some()
115 }
116}
117
118#[derive(Debug, Clone, Serialize)]
127#[cfg_attr(feature = "openapi", derive(ToSchema))]
128#[serde(rename_all = "camelCase")]
129pub struct IntegrationAccountResponseDto {
130 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
131 pub id: Uuid,
132 pub provider: OAuthProvider,
133 #[cfg_attr(feature = "openapi", schema(example = "example"))]
134 pub account_ref: String,
135 pub status: IntegrationAccountStatus,
136 #[cfg_attr(feature = "openapi", schema(example = "example"))]
137 pub scopes: String,
138 pub pkce_verifier: Option<String>,
139 pub expires_at: Option<DateTime<Utc>>,
140 pub last_refreshed_at: Option<DateTime<Utc>>,
141 pub metadata: AuditMetadata,
142}
143
144#[derive(Debug, Clone, Serialize)]
153#[cfg_attr(feature = "openapi", derive(ToSchema))]
154#[serde(rename_all = "camelCase")]
155pub struct IntegrationAccountListResponseDto {
156 pub items: Vec<IntegrationAccountResponseDto>,
158 pub total: u64,
160 pub page: u32,
162 pub per_page: u32,
164 pub total_pages: u32,
166 pub has_next: bool,
168 pub has_prev: bool,
170}
171
172impl IntegrationAccountListResponseDto {
173 pub fn new(items: Vec<IntegrationAccountResponseDto>, total: u64, page: u32, per_page: u32) -> Self {
175 let total_pages = if per_page > 0 {
176 ((total as f64) / (per_page as f64)).ceil() as u32
177 } else {
178 0
179 };
180 Self {
181 items,
182 total,
183 page,
184 per_page,
185 total_pages,
186 has_next: page < total_pages,
187 has_prev: page > 1,
188 }
189 }
190}
191
192#[derive(Debug, Clone, Serialize)]
194#[cfg_attr(feature = "openapi", derive(ToSchema))]
195#[serde(rename_all = "camelCase")]
196pub struct IntegrationAccountSummaryDto {
197 pub id: Uuid,
198 pub provider: OAuthProvider,
199 pub account_ref: String,
200 pub status: IntegrationAccountStatus,
201 pub created_at: Option<DateTime<Utc>>,
202}
203
204impl From<IntegrationAccount> for IntegrationAccountResponseDto {
209 fn from(entity: IntegrationAccount) -> Self {
210 Self {
211 id: entity.id,
212 provider: entity.provider,
213 account_ref: entity.account_ref,
214 status: entity.status,
215 scopes: entity.scopes,
216 pkce_verifier: entity.pkce_verifier,
217 expires_at: entity.expires_at,
218 last_refreshed_at: entity.last_refreshed_at,
219 metadata: entity.metadata,
220 }
221 }
222}
223
224impl From<IntegrationAccount> for IntegrationAccountSummaryDto {
225 fn from(entity: IntegrationAccount) -> Self {
226 let created_at = backbone_core::PersistentEntity::created_at(&entity);
227 Self {
228 id: entity.id,
229 provider: entity.provider,
230 account_ref: entity.account_ref,
231 status: entity.status,
232 created_at,
233 }
234 }
235}
236
237impl From<CreateIntegrationAccountDto> for IntegrationAccount {
238 fn from(dto: CreateIntegrationAccountDto) -> Self {
239 Self {
240 id: Uuid::new_v4(),
241 provider: dto.provider,
242 account_ref: dto.account_ref,
243 status: dto.status,
244 scopes: dto.scopes,
245 pkce_verifier: dto.pkce_verifier,
246 expires_at: dto.expires_at,
247 last_refreshed_at: dto.last_refreshed_at,
248 metadata: AuditMetadata::default(),
249 }
250 }
251}
252
253impl From<&IntegrationAccount> for IntegrationAccountResponseDto {
254 fn from(entity: &IntegrationAccount) -> Self {
255 Self {
256 id: entity.id.clone(),
257 provider: entity.provider.clone(),
258 account_ref: entity.account_ref.clone(),
259 status: entity.status.clone(),
260 scopes: entity.scopes.clone(),
261 pkce_verifier: entity.pkce_verifier.clone(),
262 expires_at: entity.expires_at.clone(),
263 last_refreshed_at: entity.last_refreshed_at.clone(),
264 metadata: entity.metadata.clone(),
265 }
266 }
267}
268
269impl backbone_core::FromCreateDto<CreateIntegrationAccountDto> for IntegrationAccount {
270 fn from_create_dto(dto: CreateIntegrationAccountDto) -> backbone_core::ServiceResult<Self> {
271 Ok(IntegrationAccount::from(dto))
272 }
273}
274
275impl backbone_core::ApplyUpdateDto<UpdateIntegrationAccountDto> for IntegrationAccount {
276 fn apply_update(mut self, dto: UpdateIntegrationAccountDto) -> backbone_core::ServiceResult<Self> {
277 self.provider = dto.provider;
278 self.account_ref = dto.account_ref;
279 self.status = dto.status;
280 self.scopes = dto.scopes;
281 self.pkce_verifier = dto.pkce_verifier;
282 self.expires_at = dto.expires_at;
283 self.last_refreshed_at = dto.last_refreshed_at;
284 Ok(self)
285 }
286}
287
288