backbone_bucket/presentation/dto/
content_hash_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
16use crate::domain::entity::ContentHash;
17use crate::domain::entity::AuditMetadata;
18use crate::domain::entity::StorageBackend;
19
20#[derive(Debug, Clone, Deserialize)]
29#[cfg_attr(feature = "openapi", derive(ToSchema))]
30#[serde(rename_all = "camelCase")]
31pub struct CreateContentHashDto {
32 #[cfg_attr(feature = "openapi", schema(example = "example"))]
33 pub hash: String,
34 #[cfg_attr(feature = "openapi", schema(example = 42))]
35 #[serde(alias = "size_bytes")]
36 pub size_bytes: i64,
37 #[cfg_attr(feature = "openapi", schema(example = "example"))]
38 #[serde(alias = "storage_key")]
39 pub storage_key: String,
40 #[serde(alias = "storage_backend")]
41 pub storage_backend: StorageBackend,
42 #[cfg_attr(feature = "openapi", schema(example = 42))]
43 #[serde(alias = "reference_count")]
44 pub reference_count: i32,
45 #[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
46 #[serde(alias = "first_uploaded_at")]
47 pub first_uploaded_at: DateTime<Utc>,
48 #[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
49 #[serde(alias = "last_referenced_at")]
50 pub last_referenced_at: DateTime<Utc>,
51 #[serde(default, skip_serializing_if = "Option::is_none")]
52 pub fingerprint: Option<String>,
53}
54
55#[derive(Debug, Clone, Deserialize)]
64#[cfg_attr(feature = "openapi", derive(ToSchema))]
65#[serde(rename_all = "camelCase")]
66pub struct UpdateContentHashDto {
67 #[cfg_attr(feature = "openapi", schema(example = "example"))]
68 pub hash: String,
69 #[cfg_attr(feature = "openapi", schema(example = 42))]
70 #[serde(alias = "size_bytes")]
71 pub size_bytes: i64,
72 #[cfg_attr(feature = "openapi", schema(example = "example"))]
73 #[serde(alias = "storage_key")]
74 pub storage_key: String,
75 #[serde(alias = "storage_backend")]
76 pub storage_backend: StorageBackend,
77 #[cfg_attr(feature = "openapi", schema(example = 42))]
78 #[serde(alias = "reference_count")]
79 pub reference_count: i32,
80 #[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
81 #[serde(alias = "first_uploaded_at")]
82 pub first_uploaded_at: DateTime<Utc>,
83 #[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
84 #[serde(alias = "last_referenced_at")]
85 pub last_referenced_at: DateTime<Utc>,
86 #[serde(default, skip_serializing_if = "Option::is_none")]
87 pub fingerprint: Option<String>,
88}
89
90#[derive(Debug, Clone, Default, Deserialize)]
99#[cfg_attr(feature = "openapi", derive(ToSchema))]
100#[serde(rename_all = "camelCase")]
101pub struct PatchContentHashDto {
102 #[cfg_attr(feature = "openapi", schema(example = "example"))]
103 #[serde(skip_serializing_if = "Option::is_none")]
104 pub hash: Option<String>,
105 #[cfg_attr(feature = "openapi", schema(example = 42))]
106 #[serde(skip_serializing_if = "Option::is_none", alias = "size_bytes")]
107 pub size_bytes: Option<i64>,
108 #[cfg_attr(feature = "openapi", schema(example = "example"))]
109 #[serde(skip_serializing_if = "Option::is_none", alias = "storage_key")]
110 pub storage_key: Option<String>,
111 #[serde(skip_serializing_if = "Option::is_none", alias = "storage_backend")]
112 pub storage_backend: Option<StorageBackend>,
113 #[cfg_attr(feature = "openapi", schema(example = 42))]
114 #[serde(skip_serializing_if = "Option::is_none", alias = "reference_count")]
115 pub reference_count: Option<i32>,
116 #[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
117 #[serde(skip_serializing_if = "Option::is_none", alias = "first_uploaded_at")]
118 pub first_uploaded_at: Option<DateTime<Utc>>,
119 #[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
120 #[serde(skip_serializing_if = "Option::is_none", alias = "last_referenced_at")]
121 pub last_referenced_at: Option<DateTime<Utc>>,
122 #[serde(skip_serializing_if = "Option::is_none")]
123 pub fingerprint: Option<String>,
124}
125
126impl PatchContentHashDto {
127 pub fn has_changes(&self) -> bool {
129 self.hash.is_some() || self.size_bytes.is_some() || self.storage_key.is_some() || self.storage_backend.is_some() || self.reference_count.is_some() || self.first_uploaded_at.is_some() || self.last_referenced_at.is_some() || self.fingerprint.is_some()
130 }
131}
132
133#[derive(Debug, Clone, Serialize)]
142#[cfg_attr(feature = "openapi", derive(ToSchema))]
143#[serde(rename_all = "camelCase")]
144pub struct ContentHashResponseDto {
145 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
146 pub id: Uuid,
147 #[cfg_attr(feature = "openapi", schema(example = "example"))]
148 pub hash: String,
149 #[cfg_attr(feature = "openapi", schema(example = 42))]
150 pub size_bytes: i64,
151 #[cfg_attr(feature = "openapi", schema(example = "example"))]
152 pub storage_key: String,
153 pub storage_backend: StorageBackend,
154 #[cfg_attr(feature = "openapi", schema(example = 42))]
155 pub reference_count: i32,
156 #[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
157 pub first_uploaded_at: DateTime<Utc>,
158 #[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
159 pub last_referenced_at: DateTime<Utc>,
160 pub fingerprint: Option<String>,
161 pub metadata: AuditMetadata,
162}
163
164#[derive(Debug, Clone, Serialize)]
173#[cfg_attr(feature = "openapi", derive(ToSchema))]
174#[serde(rename_all = "camelCase")]
175pub struct ContentHashListResponseDto {
176 pub items: Vec<ContentHashResponseDto>,
178 pub total: u64,
180 pub page: u32,
182 pub per_page: u32,
184 pub total_pages: u32,
186 pub has_next: bool,
188 pub has_prev: bool,
190}
191
192impl ContentHashListResponseDto {
193 pub fn new(items: Vec<ContentHashResponseDto>, total: u64, page: u32, per_page: u32) -> Self {
195 let total_pages = if per_page > 0 {
196 ((total as f64) / (per_page as f64)).ceil() as u32
197 } else {
198 0
199 };
200 Self {
201 items,
202 total,
203 page,
204 per_page,
205 total_pages,
206 has_next: page < total_pages,
207 has_prev: page > 1,
208 }
209 }
210}
211
212#[derive(Debug, Clone, Serialize)]
214#[cfg_attr(feature = "openapi", derive(ToSchema))]
215#[serde(rename_all = "camelCase")]
216pub struct ContentHashSummaryDto {
217 pub id: Uuid,
218 pub hash: String,
219 pub size_bytes: i64,
220 pub storage_key: String,
221 pub created_at: Option<DateTime<Utc>>,
222}
223
224impl From<ContentHash> for ContentHashResponseDto {
229 fn from(entity: ContentHash) -> Self {
230 Self {
231 id: entity.id,
232 hash: entity.hash,
233 size_bytes: entity.size_bytes,
234 storage_key: entity.storage_key,
235 storage_backend: entity.storage_backend,
236 reference_count: entity.reference_count,
237 first_uploaded_at: entity.first_uploaded_at,
238 last_referenced_at: entity.last_referenced_at,
239 fingerprint: entity.fingerprint,
240 metadata: entity.metadata,
241 }
242 }
243}
244
245impl From<ContentHash> for ContentHashSummaryDto {
246 fn from(entity: ContentHash) -> Self {
247 let created_at = backbone_core::PersistentEntity::created_at(&entity);
248 Self {
249 id: entity.id,
250 hash: entity.hash,
251 size_bytes: entity.size_bytes,
252 storage_key: entity.storage_key,
253 created_at,
254 }
255 }
256}
257
258impl From<CreateContentHashDto> for ContentHash {
259 fn from(dto: CreateContentHashDto) -> Self {
260 Self {
261 id: Uuid::new_v4(),
262 hash: dto.hash,
263 size_bytes: dto.size_bytes,
264 storage_key: dto.storage_key,
265 storage_backend: dto.storage_backend,
266 reference_count: dto.reference_count,
267 first_uploaded_at: dto.first_uploaded_at,
268 last_referenced_at: dto.last_referenced_at,
269 fingerprint: dto.fingerprint,
270 metadata: AuditMetadata::default(),
271 }
272 }
273}
274
275impl From<&ContentHash> for ContentHashResponseDto {
276 fn from(entity: &ContentHash) -> Self {
277 Self {
278 id: entity.id.clone(),
279 hash: entity.hash.clone(),
280 size_bytes: entity.size_bytes.clone(),
281 storage_key: entity.storage_key.clone(),
282 storage_backend: entity.storage_backend.clone(),
283 reference_count: entity.reference_count.clone(),
284 first_uploaded_at: entity.first_uploaded_at.clone(),
285 last_referenced_at: entity.last_referenced_at.clone(),
286 fingerprint: entity.fingerprint.clone(),
287 metadata: entity.metadata.clone(),
288 }
289 }
290}
291
292impl backbone_core::FromCreateDto<CreateContentHashDto> for ContentHash {
293 fn from_create_dto(dto: CreateContentHashDto) -> backbone_core::ServiceResult<Self> {
294 Ok(ContentHash::from(dto))
295 }
296}
297
298impl backbone_core::ApplyUpdateDto<UpdateContentHashDto> for ContentHash {
299 fn apply_update(mut self, dto: UpdateContentHashDto) -> backbone_core::ServiceResult<Self> {
300 self.hash = dto.hash;
301 self.size_bytes = dto.size_bytes;
302 self.storage_key = dto.storage_key;
303 self.storage_backend = dto.storage_backend;
304 self.reference_count = dto.reference_count;
305 self.first_uploaded_at = dto.first_uploaded_at;
306 self.last_referenced_at = dto.last_referenced_at;
307 self.fingerprint = dto.fingerprint;
308 Ok(self)
309 }
310}
311
312