backbone_bucket/presentation/dto/
file_lock_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::FileLock;
20use crate::domain::entity::AuditMetadata;
21use crate::domain::entity::LockStatus;
22
23#[derive(Debug, Clone, Deserialize)]
32#[cfg_attr(feature = "openapi", derive(ToSchema))]
33#[cfg_attr(feature = "validation", derive(Validate))]
34#[serde(rename_all = "camelCase")]
35pub struct CreateFileLockDto {
36 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
37 #[serde(alias = "file_id")]
38 pub file_id: Uuid,
39 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
40 #[serde(alias = "user_id")]
41 pub user_id: Uuid,
42 #[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
43 #[serde(alias = "locked_at")]
44 pub locked_at: DateTime<Utc>,
45 #[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
46 #[serde(alias = "expires_at")]
47 pub expires_at: DateTime<Utc>,
48 #[serde(default, skip_serializing_if = "Option::is_none", alias = "refreshed_at")]
49 pub refreshed_at: Option<DateTime<Utc>>,
50 pub status: LockStatus,
51}
52
53#[derive(Debug, Clone, Deserialize)]
62#[cfg_attr(feature = "openapi", derive(ToSchema))]
63#[cfg_attr(feature = "validation", derive(Validate))]
64#[serde(rename_all = "camelCase")]
65pub struct UpdateFileLockDto {
66 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
67 #[serde(alias = "file_id")]
68 pub file_id: Uuid,
69 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
70 #[serde(alias = "user_id")]
71 pub user_id: Uuid,
72 #[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
73 #[serde(alias = "locked_at")]
74 pub locked_at: DateTime<Utc>,
75 #[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
76 #[serde(alias = "expires_at")]
77 pub expires_at: DateTime<Utc>,
78 #[serde(default, skip_serializing_if = "Option::is_none", alias = "refreshed_at")]
79 pub refreshed_at: Option<DateTime<Utc>>,
80 pub status: LockStatus,
81}
82
83#[derive(Debug, Clone, Default, Deserialize)]
92#[cfg_attr(feature = "openapi", derive(ToSchema))]
93#[cfg_attr(feature = "validation", derive(Validate))]
94#[serde(rename_all = "camelCase")]
95pub struct PatchFileLockDto {
96 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
97 #[serde(skip_serializing_if = "Option::is_none", alias = "file_id")]
98 pub file_id: Option<Uuid>,
99 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
100 #[serde(skip_serializing_if = "Option::is_none", alias = "user_id")]
101 pub user_id: Option<Uuid>,
102 #[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
103 #[serde(skip_serializing_if = "Option::is_none", alias = "locked_at")]
104 pub locked_at: Option<DateTime<Utc>>,
105 #[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
106 #[serde(skip_serializing_if = "Option::is_none", alias = "expires_at")]
107 pub expires_at: Option<DateTime<Utc>>,
108 #[serde(skip_serializing_if = "Option::is_none", alias = "refreshed_at")]
109 pub refreshed_at: Option<DateTime<Utc>>,
110 #[serde(skip_serializing_if = "Option::is_none")]
111 pub status: Option<LockStatus>,
112}
113
114impl PatchFileLockDto {
115 pub fn has_changes(&self) -> bool {
117 self.file_id.is_some() || self.user_id.is_some() || self.locked_at.is_some() || self.expires_at.is_some() || self.refreshed_at.is_some() || self.status.is_some()
118 }
119}
120
121#[derive(Debug, Clone, Serialize)]
130#[cfg_attr(feature = "openapi", derive(ToSchema))]
131#[serde(rename_all = "camelCase")]
132pub struct FileLockResponseDto {
133 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
134 pub id: Uuid,
135 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
136 pub file_id: Uuid,
137 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
138 pub user_id: Uuid,
139 #[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
140 pub locked_at: DateTime<Utc>,
141 #[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
142 pub expires_at: DateTime<Utc>,
143 pub refreshed_at: Option<DateTime<Utc>>,
144 pub status: LockStatus,
145 pub metadata: AuditMetadata,
146}
147
148#[derive(Debug, Clone, Serialize)]
157#[cfg_attr(feature = "openapi", derive(ToSchema))]
158#[serde(rename_all = "camelCase")]
159pub struct FileLockListResponseDto {
160 pub items: Vec<FileLockResponseDto>,
162 pub total: u64,
164 pub page: u32,
166 pub per_page: u32,
168 pub total_pages: u32,
170 pub has_next: bool,
172 pub has_prev: bool,
174}
175
176impl FileLockListResponseDto {
177 pub fn new(items: Vec<FileLockResponseDto>, total: u64, page: u32, per_page: u32) -> Self {
179 let total_pages = if per_page > 0 {
180 ((total as f64) / (per_page as f64)).ceil() as u32
181 } else {
182 0
183 };
184 Self {
185 items,
186 total,
187 page,
188 per_page,
189 total_pages,
190 has_next: page < total_pages,
191 has_prev: page > 1,
192 }
193 }
194}
195
196#[derive(Debug, Clone, Serialize)]
198#[cfg_attr(feature = "openapi", derive(ToSchema))]
199#[serde(rename_all = "camelCase")]
200pub struct FileLockSummaryDto {
201 pub id: Uuid,
202 pub file_id: Uuid,
203 pub user_id: Uuid,
204 pub locked_at: DateTime<Utc>,
205 pub created_at: Option<DateTime<Utc>>,
206}
207
208impl From<FileLock> for FileLockResponseDto {
213 fn from(entity: FileLock) -> Self {
214 Self {
215 id: entity.id,
216 file_id: entity.file_id,
217 user_id: entity.user_id,
218 locked_at: entity.locked_at,
219 expires_at: entity.expires_at,
220 refreshed_at: entity.refreshed_at,
221 status: entity.status,
222 metadata: entity.metadata,
223 }
224 }
225}
226
227impl From<FileLock> for FileLockSummaryDto {
228 fn from(entity: FileLock) -> Self {
229 let created_at = backbone_core::PersistentEntity::created_at(&entity);
230 Self {
231 id: entity.id,
232 file_id: entity.file_id,
233 user_id: entity.user_id,
234 locked_at: entity.locked_at,
235 created_at,
236 }
237 }
238}
239
240impl From<CreateFileLockDto> for FileLock {
241 fn from(dto: CreateFileLockDto) -> Self {
242 Self {
243 id: Uuid::new_v4(),
244 file_id: dto.file_id,
245 user_id: dto.user_id,
246 locked_at: dto.locked_at,
247 expires_at: dto.expires_at,
248 refreshed_at: dto.refreshed_at,
249 status: dto.status,
250 metadata: AuditMetadata::default(),
251 }
252 }
253}
254
255impl From<&FileLock> for FileLockResponseDto {
256 fn from(entity: &FileLock) -> Self {
257 Self {
258 id: entity.id.clone(),
259 file_id: entity.file_id.clone(),
260 user_id: entity.user_id.clone(),
261 locked_at: entity.locked_at.clone(),
262 expires_at: entity.expires_at.clone(),
263 refreshed_at: entity.refreshed_at.clone(),
264 status: entity.status.clone(),
265 metadata: entity.metadata.clone(),
266 }
267 }
268}
269
270impl backbone_core::FromCreateDto<CreateFileLockDto> for FileLock {
271 fn from_create_dto(dto: CreateFileLockDto) -> backbone_core::ServiceResult<Self> {
272 Ok(FileLock::from(dto))
273 }
274}
275
276impl backbone_core::ApplyUpdateDto<UpdateFileLockDto> for FileLock {
277 fn apply_update(mut self, dto: UpdateFileLockDto) -> backbone_core::ServiceResult<Self> {
278 self.file_id = dto.file_id;
279 self.user_id = dto.user_id;
280 self.locked_at = dto.locked_at;
281 self.expires_at = dto.expires_at;
282 self.refreshed_at = dto.refreshed_at;
283 self.status = dto.status;
284 Ok(self)
285 }
286}
287
288