Skip to main content

backbone_bucket/presentation/dto/
file_version_dto.rs

1//! FileVersion DTOs (Data Transfer Objects)
2//!
3//! Generated by metaphor-schema. Do not edit manually.
4//!
5//! DTOs provide a clean separation between domain entities and API
6//! representations, with validation and OpenAPI documentation support.
7
8use 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::FileVersion;
20use crate::domain::entity::AuditMetadata;
21use crate::domain::entity::FileVersionStatus;
22use crate::domain::entity::VersionType;
23
24// =============================================================================
25// Create DTO
26// =============================================================================
27
28/// Request DTO for creating a new FileVersion
29///
30/// Used in POST requests to create new entities.
31/// Excludes auto-generated fields (id, created_at, updated_at, deleted_at).
32#[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 CreateFileVersionDto {
37    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
38    #[serde(alias = "file_id")]
39    pub file_id: Uuid,
40    #[cfg_attr(feature = "openapi", schema(example = 42))]
41    #[serde(alias = "version_number")]
42    pub version_number: i32,
43    #[serde(alias = "version_type")]
44    pub version_type: VersionType,
45    #[cfg_attr(feature = "openapi", schema(example = "example"))]
46    #[serde(alias = "storage_key")]
47    pub storage_key: String,
48    #[cfg_attr(feature = "openapi", schema(example = "example"))]
49    #[serde(alias = "storage_backend")]
50    pub storage_backend: String,
51    #[cfg_attr(feature = "validation", validate(length(max = 255)))]
52    #[cfg_attr(feature = "openapi", schema(example = "example"))]
53    pub name: String,
54    #[cfg_attr(feature = "openapi", schema(example = "example"))]
55    #[serde(alias = "mime_type")]
56    pub mime_type: String,
57    #[cfg_attr(feature = "validation", validate(length(max = 32)))]
58    #[serde(default, skip_serializing_if = "Option::is_none", alias = "checksum_md5")]
59    pub checksum_md5: Option<String>,
60    #[cfg_attr(feature = "validation", validate(length(max = 64)))]
61    #[serde(default, skip_serializing_if = "Option::is_none", alias = "checksum_sha256")]
62    pub checksum_sha256: Option<String>,
63    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
64    #[serde(alias = "created_by_id")]
65    pub created_by_id: Uuid,
66    #[cfg_attr(feature = "validation", validate(length(max = 500)))]
67    #[serde(default, skip_serializing_if = "Option::is_none", alias = "change_summary")]
68    pub change_summary: Option<String>,
69    #[cfg_attr(feature = "openapi", schema(example = true))]
70    #[serde(alias = "is_current")]
71    pub is_current: bool,
72    #[serde(default, skip_serializing_if = "Option::is_none", alias = "restored_from_id")]
73    pub restored_from_id: Option<Uuid>,
74    #[serde(default, skip_serializing_if = "Option::is_none", alias = "expires_at")]
75    pub expires_at: Option<DateTime<Utc>>,
76    pub status: FileVersionStatus,
77    #[cfg_attr(feature = "openapi", schema(example = 42))]
78    #[serde(alias = "size_bytes")]
79    pub size_bytes: i64,
80}
81
82// =============================================================================
83// Update DTO
84// =============================================================================
85
86/// Request DTO for full update of a FileVersion
87///
88/// Used in PUT requests for full entity replacement.
89/// All fields are required (except auto-generated ones).
90#[derive(Debug, Clone, Deserialize)]
91#[cfg_attr(feature = "openapi", derive(ToSchema))]
92#[cfg_attr(feature = "validation", derive(Validate))]
93#[serde(rename_all = "camelCase")]
94pub struct UpdateFileVersionDto {
95    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
96    #[serde(alias = "file_id")]
97    pub file_id: Uuid,
98    #[cfg_attr(feature = "openapi", schema(example = 42))]
99    #[serde(alias = "version_number")]
100    pub version_number: i32,
101    #[serde(alias = "version_type")]
102    pub version_type: VersionType,
103    #[cfg_attr(feature = "openapi", schema(example = "example"))]
104    #[serde(alias = "storage_key")]
105    pub storage_key: String,
106    #[cfg_attr(feature = "openapi", schema(example = "example"))]
107    #[serde(alias = "storage_backend")]
108    pub storage_backend: String,
109    #[cfg_attr(feature = "validation", validate(length(max = 255)))]
110    #[cfg_attr(feature = "openapi", schema(example = "example"))]
111    pub name: String,
112    #[cfg_attr(feature = "openapi", schema(example = "example"))]
113    #[serde(alias = "mime_type")]
114    pub mime_type: String,
115    #[cfg_attr(feature = "validation", validate(length(max = 32)))]
116    #[serde(default, skip_serializing_if = "Option::is_none", alias = "checksum_md5")]
117    pub checksum_md5: Option<String>,
118    #[cfg_attr(feature = "validation", validate(length(max = 64)))]
119    #[serde(default, skip_serializing_if = "Option::is_none", alias = "checksum_sha256")]
120    pub checksum_sha256: Option<String>,
121    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
122    #[serde(alias = "created_by_id")]
123    pub created_by_id: Uuid,
124    #[cfg_attr(feature = "validation", validate(length(max = 500)))]
125    #[serde(default, skip_serializing_if = "Option::is_none", alias = "change_summary")]
126    pub change_summary: Option<String>,
127    #[cfg_attr(feature = "openapi", schema(example = true))]
128    #[serde(alias = "is_current")]
129    pub is_current: bool,
130    #[serde(default, skip_serializing_if = "Option::is_none", alias = "restored_from_id")]
131    pub restored_from_id: Option<Uuid>,
132    #[serde(default, skip_serializing_if = "Option::is_none", alias = "expires_at")]
133    pub expires_at: Option<DateTime<Utc>>,
134    pub status: FileVersionStatus,
135    #[cfg_attr(feature = "openapi", schema(example = 42))]
136    #[serde(alias = "size_bytes")]
137    pub size_bytes: i64,
138}
139
140// =============================================================================
141// Patch DTO
142// =============================================================================
143
144/// Request DTO for partial update of a FileVersion
145///
146/// Used in PATCH requests for partial updates.
147/// All fields are optional - only provided fields will be updated.
148#[derive(Debug, Clone, Default, Deserialize)]
149#[cfg_attr(feature = "openapi", derive(ToSchema))]
150#[cfg_attr(feature = "validation", derive(Validate))]
151#[serde(rename_all = "camelCase")]
152pub struct PatchFileVersionDto {
153    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
154    #[serde(skip_serializing_if = "Option::is_none", alias = "file_id")]
155    pub file_id: Option<Uuid>,
156    #[cfg_attr(feature = "openapi", schema(example = 42))]
157    #[serde(skip_serializing_if = "Option::is_none", alias = "version_number")]
158    pub version_number: Option<i32>,
159    #[serde(skip_serializing_if = "Option::is_none", alias = "version_type")]
160    pub version_type: Option<VersionType>,
161    #[cfg_attr(feature = "openapi", schema(example = "example"))]
162    #[serde(skip_serializing_if = "Option::is_none", alias = "storage_key")]
163    pub storage_key: Option<String>,
164    #[cfg_attr(feature = "openapi", schema(example = "example"))]
165    #[serde(skip_serializing_if = "Option::is_none", alias = "storage_backend")]
166    pub storage_backend: Option<String>,
167    #[cfg_attr(feature = "validation", validate(length(max = 255)))]
168    #[cfg_attr(feature = "openapi", schema(example = "example"))]
169    #[serde(skip_serializing_if = "Option::is_none")]
170    pub name: Option<String>,
171    #[cfg_attr(feature = "openapi", schema(example = "example"))]
172    #[serde(skip_serializing_if = "Option::is_none", alias = "mime_type")]
173    pub mime_type: Option<String>,
174    #[cfg_attr(feature = "validation", validate(length(max = 32)))]
175    #[serde(skip_serializing_if = "Option::is_none", alias = "checksum_md5")]
176    pub checksum_md5: Option<String>,
177    #[cfg_attr(feature = "validation", validate(length(max = 64)))]
178    #[serde(skip_serializing_if = "Option::is_none", alias = "checksum_sha256")]
179    pub checksum_sha256: Option<String>,
180    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
181    #[serde(skip_serializing_if = "Option::is_none", alias = "created_by_id")]
182    pub created_by_id: Option<Uuid>,
183    #[cfg_attr(feature = "validation", validate(length(max = 500)))]
184    #[serde(skip_serializing_if = "Option::is_none", alias = "change_summary")]
185    pub change_summary: Option<String>,
186    #[cfg_attr(feature = "openapi", schema(example = true))]
187    #[serde(skip_serializing_if = "Option::is_none", alias = "is_current")]
188    pub is_current: Option<bool>,
189    #[serde(skip_serializing_if = "Option::is_none", alias = "restored_from_id")]
190    pub restored_from_id: Option<Uuid>,
191    #[serde(skip_serializing_if = "Option::is_none", alias = "expires_at")]
192    pub expires_at: Option<DateTime<Utc>>,
193    #[serde(skip_serializing_if = "Option::is_none")]
194    pub status: Option<FileVersionStatus>,
195    #[cfg_attr(feature = "openapi", schema(example = 42))]
196    #[serde(skip_serializing_if = "Option::is_none", alias = "size_bytes")]
197    pub size_bytes: Option<i64>,
198}
199
200impl PatchFileVersionDto {
201    /// Check if any field is set
202    pub fn has_changes(&self) -> bool {
203        self.file_id.is_some() || self.version_number.is_some() || self.version_type.is_some() || self.storage_key.is_some() || self.storage_backend.is_some() || self.name.is_some() || self.mime_type.is_some() || self.checksum_md5.is_some() || self.checksum_sha256.is_some() || self.created_by_id.is_some() || self.change_summary.is_some() || self.is_current.is_some() || self.restored_from_id.is_some() || self.expires_at.is_some() || self.status.is_some() || self.size_bytes.is_some()
204    }
205}
206
207// =============================================================================
208// Response DTO
209// =============================================================================
210
211/// Response DTO for FileVersion entity
212///
213/// Used in API responses for single entity.
214/// Includes all fields including metadata.
215#[derive(Debug, Clone, Serialize)]
216#[cfg_attr(feature = "openapi", derive(ToSchema))]
217#[serde(rename_all = "camelCase")]
218pub struct FileVersionResponseDto {
219    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
220    pub id: Uuid,
221    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
222    pub file_id: Uuid,
223    #[cfg_attr(feature = "openapi", schema(example = 42))]
224    pub version_number: i32,
225    pub version_type: VersionType,
226    #[cfg_attr(feature = "openapi", schema(example = "example"))]
227    pub storage_key: String,
228    #[cfg_attr(feature = "openapi", schema(example = "example"))]
229    pub storage_backend: String,
230    #[cfg_attr(feature = "openapi", schema(example = "example"))]
231    pub name: String,
232    #[cfg_attr(feature = "openapi", schema(example = "example"))]
233    pub mime_type: String,
234    pub checksum_md5: Option<String>,
235    pub checksum_sha256: Option<String>,
236    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
237    pub created_by_id: Uuid,
238    pub change_summary: Option<String>,
239    #[cfg_attr(feature = "openapi", schema(example = true))]
240    pub is_current: bool,
241    pub restored_from_id: Option<Uuid>,
242    pub expires_at: Option<DateTime<Utc>>,
243    pub status: FileVersionStatus,
244    pub deleted_at: Option<DateTime<Utc>>,
245    #[cfg_attr(feature = "openapi", schema(example = 42))]
246    pub size_bytes: i64,
247    pub metadata: AuditMetadata,
248}
249
250// =============================================================================
251// List Response DTO
252// =============================================================================
253
254/// Paginated list response for FileVersion entities
255///
256/// Used in API responses for list endpoints.
257/// Includes pagination metadata.
258#[derive(Debug, Clone, Serialize)]
259#[cfg_attr(feature = "openapi", derive(ToSchema))]
260#[serde(rename_all = "camelCase")]
261pub struct FileVersionListResponseDto {
262    /// List of items
263    pub items: Vec<FileVersionResponseDto>,
264    /// Total number of items (across all pages)
265    pub total: u64,
266    /// Current page number (1-indexed)
267    pub page: u32,
268    /// Number of items per page
269    pub per_page: u32,
270    /// Total number of pages
271    pub total_pages: u32,
272    /// Whether there is a next page
273    pub has_next: bool,
274    /// Whether there is a previous page
275    pub has_prev: bool,
276}
277
278impl FileVersionListResponseDto {
279    /// Create a new list response from items and pagination info
280    pub fn new(items: Vec<FileVersionResponseDto>, total: u64, page: u32, per_page: u32) -> Self {
281        let total_pages = if per_page > 0 {
282            ((total as f64) / (per_page as f64)).ceil() as u32
283        } else {
284            0
285        };
286        Self {
287            items,
288            total,
289            page,
290            per_page,
291            total_pages,
292            has_next: page < total_pages,
293            has_prev: page > 1,
294        }
295    }
296}
297
298/// Summary DTO for list views (compact version)
299#[derive(Debug, Clone, Serialize)]
300#[cfg_attr(feature = "openapi", derive(ToSchema))]
301#[serde(rename_all = "camelCase")]
302pub struct FileVersionSummaryDto {
303    pub id: Uuid,
304    pub file_id: Uuid,
305    pub version_number: i32,
306    pub version_type: VersionType,
307    pub created_at: Option<DateTime<Utc>>,
308}
309
310// =============================================================================
311// Conversions
312// =============================================================================
313
314impl From<FileVersion> for FileVersionResponseDto {
315    fn from(entity: FileVersion) -> Self {
316        Self {
317            id: entity.id,
318            file_id: entity.file_id,
319            version_number: entity.version_number,
320            version_type: entity.version_type,
321            storage_key: entity.storage_key,
322            storage_backend: entity.storage_backend,
323            name: entity.name,
324            mime_type: entity.mime_type,
325            checksum_md5: entity.checksum_md5,
326            checksum_sha256: entity.checksum_sha256,
327            created_by_id: entity.created_by_id,
328            change_summary: entity.change_summary,
329            is_current: entity.is_current,
330            restored_from_id: entity.restored_from_id,
331            expires_at: entity.expires_at,
332            status: entity.status,
333            deleted_at: entity.deleted_at,
334            size_bytes: entity.size_bytes,
335            metadata: entity.metadata,
336        }
337    }
338}
339
340impl From<FileVersion> for FileVersionSummaryDto {
341    fn from(entity: FileVersion) -> Self {
342        let created_at = backbone_core::PersistentEntity::created_at(&entity);
343        Self {
344            id: entity.id,
345            file_id: entity.file_id,
346            version_number: entity.version_number,
347            version_type: entity.version_type,
348            created_at,
349        }
350    }
351}
352
353impl From<CreateFileVersionDto> for FileVersion {
354    fn from(dto: CreateFileVersionDto) -> Self {
355        Self {
356            id: Uuid::new_v4(),
357            file_id: dto.file_id,
358            version_number: dto.version_number,
359            version_type: dto.version_type,
360            storage_key: dto.storage_key,
361            storage_backend: dto.storage_backend,
362            name: dto.name,
363            mime_type: dto.mime_type,
364            checksum_md5: dto.checksum_md5,
365            checksum_sha256: dto.checksum_sha256,
366            created_by_id: dto.created_by_id,
367            change_summary: dto.change_summary,
368            is_current: dto.is_current,
369            restored_from_id: dto.restored_from_id,
370            expires_at: dto.expires_at,
371            status: dto.status,
372            deleted_at: None,
373            size_bytes: dto.size_bytes,
374            metadata: AuditMetadata::default(),
375        }
376    }
377}
378
379impl From<&FileVersion> for FileVersionResponseDto {
380    fn from(entity: &FileVersion) -> Self {
381        Self {
382            id: entity.id.clone(),
383            file_id: entity.file_id.clone(),
384            version_number: entity.version_number.clone(),
385            version_type: entity.version_type.clone(),
386            storage_key: entity.storage_key.clone(),
387            storage_backend: entity.storage_backend.clone(),
388            name: entity.name.clone(),
389            mime_type: entity.mime_type.clone(),
390            checksum_md5: entity.checksum_md5.clone(),
391            checksum_sha256: entity.checksum_sha256.clone(),
392            created_by_id: entity.created_by_id.clone(),
393            change_summary: entity.change_summary.clone(),
394            is_current: entity.is_current.clone(),
395            restored_from_id: entity.restored_from_id.clone(),
396            expires_at: entity.expires_at.clone(),
397            status: entity.status.clone(),
398            deleted_at: entity.deleted_at.clone(),
399            size_bytes: entity.size_bytes.clone(),
400            metadata: entity.metadata.clone(),
401        }
402    }
403}
404
405impl backbone_core::FromCreateDto<CreateFileVersionDto> for FileVersion {
406    fn from_create_dto(dto: CreateFileVersionDto) -> backbone_core::ServiceResult<Self> {
407        Ok(FileVersion::from(dto))
408    }
409}
410
411impl backbone_core::ApplyUpdateDto<UpdateFileVersionDto> for FileVersion {
412    fn apply_update(mut self, dto: UpdateFileVersionDto) -> backbone_core::ServiceResult<Self> {
413        self.file_id = dto.file_id;
414        self.version_number = dto.version_number;
415        self.version_type = dto.version_type;
416        self.storage_key = dto.storage_key;
417        self.storage_backend = dto.storage_backend;
418        self.name = dto.name;
419        self.mime_type = dto.mime_type;
420        self.checksum_md5 = dto.checksum_md5;
421        self.checksum_sha256 = dto.checksum_sha256;
422        self.created_by_id = dto.created_by_id;
423        self.change_summary = dto.change_summary;
424        self.is_current = dto.is_current;
425        self.restored_from_id = dto.restored_from_id;
426        self.expires_at = dto.expires_at;
427        self.status = dto.status;
428        self.size_bytes = dto.size_bytes;
429        Ok(self)
430    }
431}
432
433// =============================================================================
434// Custom DTOs
435// =============================================================================
436
437// Add custom DTOs specific to FileVersion here.
438// This section will be preserved during regeneration.
439// >>> END CUSTOM DTOs
440