Skip to main content

backbone_bucket/presentation/dto/
file_comment_dto.rs

1//! FileComment 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::FileComment;
20use crate::domain::entity::AuditMetadata;
21use crate::domain::entity::CommentStatus;
22
23// =============================================================================
24// Create DTO
25// =============================================================================
26
27/// Request DTO for creating a new FileComment
28///
29/// Used in POST requests to create new entities.
30/// Excludes auto-generated fields (id, created_at, updated_at, deleted_at).
31#[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 CreateFileCommentDto {
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    #[serde(default, skip_serializing_if = "Option::is_none", alias = "parent_id")]
43    pub parent_id: Option<Uuid>,
44    #[cfg_attr(feature = "validation", validate(length(max = 10000)))]
45    #[cfg_attr(feature = "openapi", schema(example = "example"))]
46    pub content: String,
47    #[serde(default, skip_serializing_if = "Option::is_none", alias = "annotation_region")]
48    pub annotation_region: Option<serde_json::Value>,
49    pub mentions: Vec<Uuid>,
50    #[cfg_attr(feature = "openapi", schema(example = true))]
51    pub resolved: bool,
52    #[serde(default, skip_serializing_if = "Option::is_none", alias = "resolved_by")]
53    pub resolved_by: Option<Uuid>,
54    #[serde(default, skip_serializing_if = "Option::is_none", alias = "resolved_at")]
55    pub resolved_at: Option<DateTime<Utc>>,
56    pub status: CommentStatus,
57}
58
59// =============================================================================
60// Update DTO
61// =============================================================================
62
63/// Request DTO for full update of a FileComment
64///
65/// Used in PUT requests for full entity replacement.
66/// All fields are required (except auto-generated ones).
67#[derive(Debug, Clone, Deserialize)]
68#[cfg_attr(feature = "openapi", derive(ToSchema))]
69#[cfg_attr(feature = "validation", derive(Validate))]
70#[serde(rename_all = "camelCase")]
71pub struct UpdateFileCommentDto {
72    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
73    #[serde(alias = "file_id")]
74    pub file_id: Uuid,
75    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
76    #[serde(alias = "user_id")]
77    pub user_id: Uuid,
78    #[serde(default, skip_serializing_if = "Option::is_none", alias = "parent_id")]
79    pub parent_id: Option<Uuid>,
80    #[cfg_attr(feature = "validation", validate(length(max = 10000)))]
81    #[cfg_attr(feature = "openapi", schema(example = "example"))]
82    pub content: String,
83    #[serde(default, skip_serializing_if = "Option::is_none", alias = "annotation_region")]
84    pub annotation_region: Option<serde_json::Value>,
85    pub mentions: Vec<Uuid>,
86    #[cfg_attr(feature = "openapi", schema(example = true))]
87    pub resolved: bool,
88    #[serde(default, skip_serializing_if = "Option::is_none", alias = "resolved_by")]
89    pub resolved_by: Option<Uuid>,
90    #[serde(default, skip_serializing_if = "Option::is_none", alias = "resolved_at")]
91    pub resolved_at: Option<DateTime<Utc>>,
92    pub status: CommentStatus,
93}
94
95// =============================================================================
96// Patch DTO
97// =============================================================================
98
99/// Request DTO for partial update of a FileComment
100///
101/// Used in PATCH requests for partial updates.
102/// All fields are optional - only provided fields will be updated.
103#[derive(Debug, Clone, Default, Deserialize)]
104#[cfg_attr(feature = "openapi", derive(ToSchema))]
105#[cfg_attr(feature = "validation", derive(Validate))]
106#[serde(rename_all = "camelCase")]
107pub struct PatchFileCommentDto {
108    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
109    #[serde(skip_serializing_if = "Option::is_none", alias = "file_id")]
110    pub file_id: Option<Uuid>,
111    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
112    #[serde(skip_serializing_if = "Option::is_none", alias = "user_id")]
113    pub user_id: Option<Uuid>,
114    #[serde(skip_serializing_if = "Option::is_none", alias = "parent_id")]
115    pub parent_id: Option<Uuid>,
116    #[cfg_attr(feature = "validation", validate(length(max = 10000)))]
117    #[cfg_attr(feature = "openapi", schema(example = "example"))]
118    #[serde(skip_serializing_if = "Option::is_none")]
119    pub content: Option<String>,
120    #[serde(skip_serializing_if = "Option::is_none", alias = "annotation_region")]
121    pub annotation_region: Option<serde_json::Value>,
122    #[serde(skip_serializing_if = "Option::is_none")]
123    pub mentions: Option<Vec<Uuid>>,
124    #[cfg_attr(feature = "openapi", schema(example = true))]
125    #[serde(skip_serializing_if = "Option::is_none")]
126    pub resolved: Option<bool>,
127    #[serde(skip_serializing_if = "Option::is_none", alias = "resolved_by")]
128    pub resolved_by: Option<Uuid>,
129    #[serde(skip_serializing_if = "Option::is_none", alias = "resolved_at")]
130    pub resolved_at: Option<DateTime<Utc>>,
131    #[serde(skip_serializing_if = "Option::is_none")]
132    pub status: Option<CommentStatus>,
133}
134
135impl PatchFileCommentDto {
136    /// Check if any field is set
137    pub fn has_changes(&self) -> bool {
138        self.file_id.is_some() || self.user_id.is_some() || self.parent_id.is_some() || self.content.is_some() || self.annotation_region.is_some() || self.mentions.is_some() || self.resolved.is_some() || self.resolved_by.is_some() || self.resolved_at.is_some() || self.status.is_some()
139    }
140}
141
142// =============================================================================
143// Response DTO
144// =============================================================================
145
146/// Response DTO for FileComment entity
147///
148/// Used in API responses for single entity.
149/// Includes all fields including metadata.
150#[derive(Debug, Clone, Serialize)]
151#[cfg_attr(feature = "openapi", derive(ToSchema))]
152#[serde(rename_all = "camelCase")]
153pub struct FileCommentResponseDto {
154    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
155    pub id: Uuid,
156    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
157    pub file_id: Uuid,
158    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
159    pub user_id: Uuid,
160    pub parent_id: Option<Uuid>,
161    #[cfg_attr(feature = "openapi", schema(example = "example"))]
162    pub content: String,
163    pub annotation_region: Option<serde_json::Value>,
164    pub mentions: Vec<Uuid>,
165    #[cfg_attr(feature = "openapi", schema(example = true))]
166    pub resolved: bool,
167    pub resolved_by: Option<Uuid>,
168    pub resolved_at: Option<DateTime<Utc>>,
169    pub status: CommentStatus,
170    pub metadata: AuditMetadata,
171}
172
173// =============================================================================
174// List Response DTO
175// =============================================================================
176
177/// Paginated list response for FileComment entities
178///
179/// Used in API responses for list endpoints.
180/// Includes pagination metadata.
181#[derive(Debug, Clone, Serialize)]
182#[cfg_attr(feature = "openapi", derive(ToSchema))]
183#[serde(rename_all = "camelCase")]
184pub struct FileCommentListResponseDto {
185    /// List of items
186    pub items: Vec<FileCommentResponseDto>,
187    /// Total number of items (across all pages)
188    pub total: u64,
189    /// Current page number (1-indexed)
190    pub page: u32,
191    /// Number of items per page
192    pub per_page: u32,
193    /// Total number of pages
194    pub total_pages: u32,
195    /// Whether there is a next page
196    pub has_next: bool,
197    /// Whether there is a previous page
198    pub has_prev: bool,
199}
200
201impl FileCommentListResponseDto {
202    /// Create a new list response from items and pagination info
203    pub fn new(items: Vec<FileCommentResponseDto>, total: u64, page: u32, per_page: u32) -> Self {
204        let total_pages = if per_page > 0 {
205            ((total as f64) / (per_page as f64)).ceil() as u32
206        } else {
207            0
208        };
209        Self {
210            items,
211            total,
212            page,
213            per_page,
214            total_pages,
215            has_next: page < total_pages,
216            has_prev: page > 1,
217        }
218    }
219}
220
221/// Summary DTO for list views (compact version)
222#[derive(Debug, Clone, Serialize)]
223#[cfg_attr(feature = "openapi", derive(ToSchema))]
224#[serde(rename_all = "camelCase")]
225pub struct FileCommentSummaryDto {
226    pub id: Uuid,
227    pub file_id: Uuid,
228    pub user_id: Uuid,
229    pub parent_id: Option<Uuid>,
230    pub created_at: Option<DateTime<Utc>>,
231}
232
233// =============================================================================
234// Conversions
235// =============================================================================
236
237impl From<FileComment> for FileCommentResponseDto {
238    fn from(entity: FileComment) -> Self {
239        Self {
240            id: entity.id,
241            file_id: entity.file_id,
242            user_id: entity.user_id,
243            parent_id: entity.parent_id,
244            content: entity.content,
245            annotation_region: entity.annotation_region,
246            mentions: entity.mentions,
247            resolved: entity.resolved,
248            resolved_by: entity.resolved_by,
249            resolved_at: entity.resolved_at,
250            status: entity.status,
251            metadata: entity.metadata,
252        }
253    }
254}
255
256impl From<FileComment> for FileCommentSummaryDto {
257    fn from(entity: FileComment) -> Self {
258        let created_at = backbone_core::PersistentEntity::created_at(&entity);
259        Self {
260            id: entity.id,
261            file_id: entity.file_id,
262            user_id: entity.user_id,
263            parent_id: entity.parent_id,
264            created_at,
265        }
266    }
267}
268
269impl From<CreateFileCommentDto> for FileComment {
270    fn from(dto: CreateFileCommentDto) -> Self {
271        Self {
272            id: Uuid::new_v4(),
273            file_id: dto.file_id,
274            user_id: dto.user_id,
275            parent_id: dto.parent_id,
276            content: dto.content,
277            annotation_region: dto.annotation_region,
278            mentions: dto.mentions,
279            resolved: dto.resolved,
280            resolved_by: dto.resolved_by,
281            resolved_at: dto.resolved_at,
282            status: dto.status,
283            metadata: AuditMetadata::default(),
284        }
285    }
286}
287
288impl From<&FileComment> for FileCommentResponseDto {
289    fn from(entity: &FileComment) -> Self {
290        Self {
291            id: entity.id.clone(),
292            file_id: entity.file_id.clone(),
293            user_id: entity.user_id.clone(),
294            parent_id: entity.parent_id.clone(),
295            content: entity.content.clone(),
296            annotation_region: entity.annotation_region.clone(),
297            mentions: entity.mentions.clone(),
298            resolved: entity.resolved.clone(),
299            resolved_by: entity.resolved_by.clone(),
300            resolved_at: entity.resolved_at.clone(),
301            status: entity.status.clone(),
302            metadata: entity.metadata.clone(),
303        }
304    }
305}
306
307impl backbone_core::FromCreateDto<CreateFileCommentDto> for FileComment {
308    fn from_create_dto(dto: CreateFileCommentDto) -> backbone_core::ServiceResult<Self> {
309        Ok(FileComment::from(dto))
310    }
311}
312
313impl backbone_core::ApplyUpdateDto<UpdateFileCommentDto> for FileComment {
314    fn apply_update(mut self, dto: UpdateFileCommentDto) -> backbone_core::ServiceResult<Self> {
315        self.file_id = dto.file_id;
316        self.user_id = dto.user_id;
317        self.parent_id = dto.parent_id;
318        self.content = dto.content;
319        self.annotation_region = dto.annotation_region;
320        self.mentions = dto.mentions;
321        self.resolved = dto.resolved;
322        self.resolved_by = dto.resolved_by;
323        self.resolved_at = dto.resolved_at;
324        self.status = dto.status;
325        Ok(self)
326    }
327}
328
329// =============================================================================
330// Custom DTOs
331// =============================================================================
332
333// Add custom DTOs specific to FileComment here.
334// This section will be preserved during regeneration.
335// >>> END CUSTOM DTOs
336