feat(drive): complete updated_by created_by

This commit is contained in:
Edouard Vanbelle
2026-06-19 10:51:13 +02:00
parent 06116dc6e7
commit e7f4826778
34 changed files with 987 additions and 230 deletions
+18
View File
@@ -3,6 +3,7 @@ use std::sync::Arc;
use crate::domain::entities::file::File;
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
use uuid::Uuid;
use super::display_helpers::{
category_for, format_file_size, icon_class_for, icon_special_class_for,
@@ -75,6 +76,19 @@ pub struct FileDto {
/// through `If-Match` / `If-None-Match` on download / mutation
/// endpoints without a separate HEAD round-trip.
pub etag: String,
/// §14 provenance: user that originally created this file.
/// `None` when the referenced user has been deleted (FK is
/// `ON DELETE SET NULL`) or for stub/legacy files.
#[serde(skip_serializing_if = "Option::is_none")]
pub created_by: Option<Uuid>,
/// §14 provenance: user that performed the most recent mutation
/// that bumped `updated_at`. Authorship signal — distinct from
/// `owner_id`. `None` when the referenced user is deleted or for
/// stub/legacy files.
#[serde(skip_serializing_if = "Option::is_none")]
pub updated_by: Option<Uuid>,
}
impl From<File> for FileDto {
@@ -114,6 +128,8 @@ impl From<File> for FileDto {
sort_date: None,
content_hash,
etag,
created_by: parts.created_by,
updated_by: parts.updated_by,
}
}
}
@@ -171,6 +187,8 @@ impl FileDto {
content_hash: String::new(),
etag: String::new(),
sort_date: None,
created_by: None,
updated_by: None,
}
}
}
+17
View File
@@ -86,6 +86,19 @@ pub struct FolderDto {
/// pass it back through `If-Match` on rename / move endpoints
/// without a separate HEAD round-trip.
pub etag: String,
/// §14 provenance: user that originally created this folder.
/// `None` when the referenced user has been deleted (FK is
/// `ON DELETE SET NULL`) or for stub/legacy folders.
#[serde(skip_serializing_if = "Option::is_none")]
pub created_by: Option<Uuid>,
/// §14 provenance: user that performed the most recent mutation
/// that bumped `updated_at`. Authorship signal — distinct from
/// `owner_id`. `None` when the referenced user is deleted or for
/// stub/legacy folders.
#[serde(skip_serializing_if = "Option::is_none")]
pub updated_by: Option<Uuid>,
}
impl From<Folder> for FolderDto {
@@ -107,6 +120,8 @@ impl From<Folder> for FolderDto {
icon_special_class: Arc::from("folder-icon"),
category: Arc::from("Folder"),
etag,
created_by: folder.created_by(),
updated_by: folder.updated_by(),
}
}
}
@@ -159,6 +174,8 @@ impl FolderDto {
icon_special_class: Arc::from("folder-icon"),
category: Arc::from("Folder"),
etag: String::new(),
created_by: None,
updated_by: None,
}
}
}
+15
View File
@@ -44,12 +44,20 @@ pub trait FileUploadUseCase: Send + Sync + 'static {
/// Register a new file row pointing at an already-ingested blob.
///
/// Takes ownership of the blob's reference (released on failure).
///
/// `caller_id` is plumbed down into
/// `FileWritePort::save_file_with_blob` so the §14 `created_by` /
/// `updated_by` columns record the principal performing the upload —
/// not the parent folder's owner. D2 shared drives surface this
/// most clearly: Adam upload into Alice's folder must record
/// `created_by = adam.id`.
async fn upload_file_streaming(
&self,
name: String,
folder_id: Option<String>,
content_type: String,
blob: StoredBlob,
caller_id: Uuid,
) -> Result<FileDto, DomainError>;
/// Replace the content of the file at `path` with an already-ingested
@@ -61,6 +69,12 @@ pub trait FileUploadUseCase: Send + Sync + 'static {
/// and the parent-folder resolution (`get_parent_folder_id`) — the
/// handler is responsible for deriving it from its protocol context
/// (NC chroot, native default-drive lookup, WOPI default-drive).
///
/// `caller_id` is plumbed down into
/// `FileWritePort::update_file_content_with_blob` so the §14
/// `updated_by` column reflects the principal that performed the
/// PUT — not the file's existing owner (D2 shared drives let
/// non-owners overwrite content).
async fn update_file_streaming(
&self,
path: &str,
@@ -68,6 +82,7 @@ pub trait FileUploadUseCase: Send + Sync + 'static {
blob: StoredBlob,
content_type: &str,
modified_at: Option<i64>,
caller_id: Uuid,
) -> Result<FileDto, DomainError>;
}
+41 -6
View File
@@ -284,6 +284,13 @@ pub trait FileWritePort: Send + Sync + 'static {
///
/// Takes ownership of one blob reference: on any failure the reference
/// is released before the error is returned.
///
/// `caller_id` is stamped into both `created_by` and `updated_by`
/// (§14 provenance — authorship belongs to the caller, not to the
/// parent folder's owner). In D2 shared drives, a non-owner member
/// can upload into a folder owned by someone else; the previous
/// `created_by = parent.user_id` would have silently recorded the
/// wrong principal.
async fn save_file_with_blob(
&self,
name: String,
@@ -291,17 +298,29 @@ pub trait FileWritePort: Send + Sync + 'static {
content_type: String,
blob_hash: &str,
size: u64,
caller_id: Uuid,
) -> Result<File, DomainError>;
/// Moves a file to another folder.
/// Moves a file to another folder. `caller_id` is stamped into
/// `updated_by` alongside the `updated_at = NOW()` bump
/// (§14 provenance — authorship belongs to the caller, not to
/// the destination folder's owner).
async fn move_file(
&self,
file_id: &str,
target_folder_id: Option<String>,
caller_id: Uuid,
) -> Result<File, DomainError>;
/// Renames a file (same folder, different name).
async fn rename_file(&self, file_id: &str, new_name: &str) -> Result<File, DomainError>;
/// Renames a file (same folder, different name). `caller_id` is
/// stamped into `updated_by` alongside the `updated_at = NOW()`
/// bump (§14 provenance).
async fn rename_file(
&self,
file_id: &str,
new_name: &str,
caller_id: Uuid,
) -> Result<File, DomainError>;
/// Deletes a file.
async fn delete_file(&self, id: &str) -> Result<(), DomainError>;
@@ -315,24 +334,32 @@ pub trait FileWritePort: Send + Sync + 'static {
/// Returns `(new_blob_hash, updated_at_epoch)` — everything a caller
/// needs to rebuild the fresh entity/ETag from a `File` it already
/// holds, without re-reading the row it just updated.
///
/// `caller_id` is stamped into `updated_by` alongside the
/// `updated_at` bump (§14 provenance).
async fn update_file_content_with_blob(
&self,
file_id: &str,
blob_hash: &str,
size: u64,
modified_at: Option<i64>,
caller_id: Uuid,
) -> Result<(String, i64), DomainError>;
/// Registers file metadata WITHOUT writing content to disk (write-behind).
///
/// Returns `(File, PathBuf)` where `PathBuf` is the destination path for the
/// deferred write that the `WriteBehindCache` will perform.
///
/// `caller_id` is stamped into both `created_by` and `updated_by`
/// (§14 provenance — see `save_file_with_blob`).
async fn register_file_deferred(
&self,
name: String,
folder_id: Option<String>,
content_type: String,
size: u64,
caller_id: Uuid,
) -> Result<(File, PathBuf), DomainError>;
/// Copies a file to a (possibly different) folder.
@@ -344,11 +371,16 @@ pub trait FileWritePort: Send + Sync + 'static {
/// the same folder always collides on the source's filename. WebDAV
/// COPY uses this for the "same folder, different name" case (the
/// classic `COPY /a.txt → /b.txt` pattern).
///
/// `caller_id` is stamped into both `created_by` and `updated_by`
/// on the new row (§14 provenance — the caller authored this copy,
/// not the destination folder's owner).
async fn copy_file(
&self,
file_id: &str,
target_folder_id: Option<String>,
new_name: Option<&str>,
caller_id: Uuid,
) -> Result<File, DomainError>;
/// Copies an entire folder subtree atomically using ltree.
@@ -375,14 +407,17 @@ pub trait FileWritePort: Send + Sync + 'static {
// ── Trash operations ──
/// Moves a file to the trash
async fn move_to_trash(&self, file_id: &str) -> Result<(), DomainError>;
/// Moves a file to the trash. `caller_id` is stamped into
/// `updated_by` (§14 provenance).
async fn move_to_trash(&self, file_id: &str, caller_id: Uuid) -> Result<(), DomainError>;
/// Restores a file from the trash to its original location
/// Restores a file from the trash to its original location.
/// `caller_id` is stamped into `updated_by` (§14 provenance).
async fn restore_from_trash(
&self,
file_id: &str,
original_path: &str,
caller_id: Uuid,
) -> Result<(), DomainError>;
/// Permanently deletes a file (used by the trash)
@@ -623,6 +623,7 @@ impl DeltaUploadService {
Some(folder_id.clone()),
content_type,
blob,
caller_id,
)
.await
}
@@ -98,6 +98,7 @@ impl FileManagementService {
&self,
file_id: &str,
folder_id: Option<String>,
caller_id: Uuid,
) -> Result<FileDto, DomainError> {
info!(
"Moving file with ID: {} to folder: {:?}",
@@ -106,7 +107,7 @@ impl FileManagementService {
let moved_file = self
.file_repository
.move_file(file_id, folder_id)
.move_file(file_id, folder_id, caller_id)
.await
.map_err(|e| {
error!("Error moving file (ID: {}): {}", file_id, e);
@@ -128,6 +129,7 @@ impl FileManagementService {
file_id: &str,
target_folder_id: Option<String>,
new_name: Option<&str>,
caller_id: Uuid,
) -> Result<FileDto, DomainError> {
info!(
"Copying file with ID: {} to folder: {:?} as {:?}",
@@ -136,7 +138,7 @@ impl FileManagementService {
let copied_file = self
.file_repository
.copy_file(file_id, target_folder_id, new_name)
.copy_file(file_id, target_folder_id, new_name, caller_id)
.await
.map_err(|e| {
error!("Error copying file (ID: {}): {}", file_id, e);
@@ -157,7 +159,12 @@ impl FileManagementService {
Ok(dto)
}
async fn rename_file(&self, file_id: &str, new_name: &str) -> Result<FileDto, DomainError> {
async fn rename_file(
&self,
file_id: &str,
new_name: &str,
caller_id: Uuid,
) -> Result<FileDto, DomainError> {
if let Err(reason) = validate_storage_name(new_name) {
return Err(DomainError::validation_error(format!(
"Invalid file name '{new_name}': {reason}"
@@ -168,7 +175,7 @@ impl FileManagementService {
let renamed_file = self
.file_repository
.rename_file(file_id, new_name)
.rename_file(file_id, new_name, caller_id)
.await
.map_err(|e| {
error!("Error renaming file (ID: {}): {}", file_id, e);
@@ -253,7 +260,7 @@ impl FileManagementUseCase for FileManagementService {
.await?;
self.require_target_folder_perm(folder_id.as_deref(), Permission::Create, caller_id)
.await?;
self.move_file(file_id, folder_id).await
self.move_file(file_id, folder_id, caller_id).await
}
async fn copy_file_with_perms(
@@ -268,7 +275,7 @@ impl FileManagementUseCase for FileManagementService {
.await?;
self.require_target_folder_perm(target_folder_id.as_deref(), Permission::Create, caller_id)
.await?;
self.copy_file(file_id, target_folder_id, new_name.as_deref())
self.copy_file(file_id, target_folder_id, new_name.as_deref(), caller_id)
.await
}
@@ -280,7 +287,7 @@ impl FileManagementUseCase for FileManagementService {
) -> Result<FileDto, DomainError> {
self.require_file_perm(file_id, Permission::Update, caller_id)
.await?;
self.rename_file(file_id, new_name).await
self.rename_file(file_id, new_name, caller_id).await
}
async fn delete_file_with_perms(&self, id: &str, caller_id: Uuid) -> Result<(), DomainError> {
@@ -209,6 +209,7 @@ impl FileUploadService {
size: metadata.size,
is_new_blob: false,
},
caller_id,
)
.await?;
@@ -255,7 +256,7 @@ impl FileUploadService {
let file = file_read.get_file(file_id).await?;
let (new_hash, updated_at) = self
.file_write
.update_file_content_with_blob(file_id, &blob.hash, blob.size, None)
.update_file_content_with_blob(file_id, &blob.hash, blob.size, None, caller_id)
.await?;
// The file maps to a different blob now — stale cached content must
// never be served for the rest of its TTI window.
@@ -326,10 +327,18 @@ impl FileUploadUseCase for FileUploadService {
folder_id: Option<String>,
content_type: String,
blob: StoredBlob,
caller_id: Uuid,
) -> Result<FileDto, DomainError> {
let file = self
.file_write
.save_file_with_blob(name.clone(), folder_id, content_type, &blob.hash, blob.size)
.save_file_with_blob(
name.clone(),
folder_id,
content_type,
&blob.hash,
blob.size,
caller_id,
)
.await?;
let dto = FileDto::from(file);
info!(
@@ -352,6 +361,7 @@ impl FileUploadUseCase for FileUploadService {
blob: StoredBlob,
content_type: &str,
modified_at: Option<i64>,
caller_id: Uuid,
) -> Result<FileDto, DomainError> {
// Try to find the existing file first
if let Some(file_read) = &self.file_read
@@ -360,7 +370,13 @@ impl FileUploadUseCase for FileUploadService {
let file_id = file.id().to_string();
let (new_hash, updated_at) = self
.file_write
.update_file_content_with_blob(&file_id, &blob.hash, blob.size, modified_at)
.update_file_content_with_blob(
&file_id,
&blob.hash,
blob.size,
modified_at,
caller_id,
)
.await?;
// Invalidate content cache — file content has changed.
if let Some(cc) = &self.content_cache {
@@ -428,6 +444,7 @@ impl FileUploadUseCase for FileUploadService {
content_type.to_string(),
&blob.hash,
blob.size,
caller_id,
)
.await?;
let dto = FileDto::from(created);
+3 -3
View File
@@ -234,7 +234,7 @@ impl FolderUseCase for FolderService {
let folder = self
.folder_storage
.create_folder(dto.name, dto.parent_id)
.create_folder(dto.name, dto.parent_id, caller_id)
.await?;
Ok(FolderDto::from(folder))
}
@@ -489,7 +489,7 @@ impl FolderUseCase for FolderService {
let folder = self
.folder_storage
.rename_folder(id, dto.name)
.rename_folder(id, dto.name, caller_id)
.await
.map_err(|e| {
DomainError::internal_error(
@@ -541,7 +541,7 @@ impl FolderUseCase for FolderService {
let parent_ref = dto.parent_id.as_deref();
let folder = self
.folder_storage
.move_folder(id, parent_ref)
.move_folder(id, parent_ref, caller_id)
.await
.map_err(|e| {
DomainError::internal_error(
@@ -184,6 +184,7 @@ impl FileWritePort for MockFileWritePort {
_content_type: String,
_blob_hash: &str,
_size: u64,
_caller_id: Uuid,
) -> Result<File, DomainError> {
unimplemented!()
}
@@ -192,6 +193,7 @@ impl FileWritePort for MockFileWritePort {
&self,
file_id: &str,
_target_folder_id: Option<String>,
_caller_id: Uuid,
) -> Result<File, DomainError> {
let files = self.files.lock().unwrap();
files
@@ -200,7 +202,12 @@ impl FileWritePort for MockFileWritePort {
.ok_or_else(|| DomainError::not_found("File", file_id.to_string()))
}
async fn rename_file(&self, file_id: &str, _new_name: &str) -> Result<File, DomainError> {
async fn rename_file(
&self,
file_id: &str,
_new_name: &str,
_caller_id: Uuid,
) -> Result<File, DomainError> {
let files = self.files.lock().unwrap();
files
.get(file_id)
@@ -218,6 +225,7 @@ impl FileWritePort for MockFileWritePort {
_blob_hash: &str,
_size: u64,
_modified_at: Option<i64>,
_caller_id: Uuid,
) -> Result<(String, i64), DomainError> {
Ok((String::new(), 0))
}
@@ -228,6 +236,7 @@ impl FileWritePort for MockFileWritePort {
_folder_id: Option<String>,
_content_type: String,
_size: u64,
_caller_id: Uuid,
) -> Result<(File, PathBuf), DomainError> {
unimplemented!()
}
@@ -237,11 +246,12 @@ impl FileWritePort for MockFileWritePort {
_file_id: &str,
_target_folder_id: Option<String>,
_new_name: Option<&str>,
_caller_id: Uuid,
) -> Result<File, DomainError> {
unimplemented!()
}
async fn move_to_trash(&self, _file_id: &str) -> Result<(), DomainError> {
async fn move_to_trash(&self, _file_id: &str, _caller_id: Uuid) -> Result<(), DomainError> {
Ok(())
}
@@ -249,6 +259,7 @@ impl FileWritePort for MockFileWritePort {
&self,
_file_id: &str,
_original_path: &str,
_caller_id: Uuid,
) -> Result<(), DomainError> {
Ok(())
}
+9 -1
View File
@@ -906,6 +906,7 @@ mod tests {
&self,
_name: String,
_parent_id: Option<String>,
_caller_id: uuid::Uuid,
) -> Result<crate::domain::entities::folder::Folder, DomainError> {
unimplemented!()
}
@@ -980,6 +981,7 @@ mod tests {
&self,
_id: &str,
_new_name: String,
_caller_id: Uuid,
) -> Result<crate::domain::entities::folder::Folder, DomainError> {
unimplemented!()
}
@@ -988,6 +990,7 @@ mod tests {
&self,
_id: &str,
_new_parent_id: Option<&str>,
_caller_id: Uuid,
) -> Result<crate::domain::entities::folder::Folder, DomainError> {
unimplemented!()
}
@@ -1011,7 +1014,11 @@ mod tests {
unimplemented!()
}
async fn move_to_trash(&self, _folder_id: &str) -> Result<(), DomainError> {
async fn move_to_trash(
&self,
_folder_id: &str,
_caller_id: Uuid,
) -> Result<(), DomainError> {
unimplemented!()
}
@@ -1019,6 +1026,7 @@ mod tests {
&self,
_folder_id: &str,
_original_path: &str,
_caller_id: Uuid,
) -> Result<(), DomainError> {
unimplemented!()
}
+14 -6
View File
@@ -253,9 +253,10 @@ impl TrashUseCase for TrashService {
}
};
// Then physically move the file to trash
// Then physically move the file to trash.
// §14: caller_id stamps `updated_by` on the trashed row.
info!("Physically moving file to trash: {}", item_id);
match self.file_write_port.move_to_trash(item_id).await {
match self.file_write_port.move_to_trash(item_id, user_id).await {
Ok(_) => {
debug!("File physically moved to trash successfully: {}", item_id);
}
@@ -320,9 +321,10 @@ impl TrashUseCase for TrashService {
}
};
// Then physically move the folder to trash
// Then physically move the folder to trash.
// §14: caller_id stamps `updated_by` on every cascade-trashed row.
self.folder_storage_port
.move_to_trash(item_id)
.move_to_trash(item_id, user_id)
.await
.map_err(|e| {
DomainError::new(
@@ -391,7 +393,7 @@ impl TrashUseCase for TrashService {
);
match self
.file_write_port
.restore_from_trash(&file_id, &original_path)
.restore_from_trash(&file_id, &original_path, user_id)
.await
{
Ok(_) => {
@@ -431,7 +433,7 @@ impl TrashUseCase for TrashService {
);
match self
.folder_storage_port
.restore_from_trash(&folder_id, &original_path)
.restore_from_trash(&folder_id, &original_path, user_id)
.await
{
Ok(_) => {
@@ -831,6 +833,9 @@ fn row_to_item_dto(row: TrashResourceRow) -> TrashResourceItemDto {
icon_class: std::sync::Arc::from("fas fa-folder"),
icon_special_class: std::sync::Arc::from("folder-icon"),
category: std::sync::Arc::from("Folder"),
// §14 provenance not selected by the trash listing query.
created_by: None,
updated_by: None,
};
TrashResourceItemDto {
resource_type: ResourceTypeDto::Folder,
@@ -871,6 +876,9 @@ fn row_to_item_dto(row: TrashResourceRow) -> TrashResourceItemDto {
sort_date: None,
content_hash,
etag,
// §14 provenance not selected by the trash listing query.
created_by: None,
updated_by: None,
};
TrashResourceItemDto {
resource_type: ResourceTypeDto::File,
+25 -6
View File
@@ -139,7 +139,7 @@ where
)
})?;
self.file_write_port
.move_to_trash(item_id)
.move_to_trash(item_id, user_id)
.await
.map_err(|e| {
DomainError::new(
@@ -181,7 +181,7 @@ where
)
})?;
self.folder_storage_port
.move_to_trash(item_id)
.move_to_trash(item_id, user_id)
.await
.map_err(|e| {
DomainError::new(
@@ -215,7 +215,7 @@ where
let original_path = item.original_path().to_string();
let result = self
.file_write_port
.restore_from_trash(&file_id, &original_path)
.restore_from_trash(&file_id, &original_path, user_id)
.await;
if let Err(e) = result
&& !format!("{}", e).contains("not found")
@@ -232,7 +232,7 @@ where
let original_path = item.original_path().to_string();
let result = self
.folder_storage_port
.restore_from_trash(&folder_id, &original_path)
.restore_from_trash(&folder_id, &original_path, user_id)
.await;
if let Err(e) = result
&& !format!("{}", e).contains("not found")
@@ -566,6 +566,7 @@ impl FileWritePort for MockFileRepository {
_content_type: String,
_blob_hash: &str,
_size: u64,
_caller_id: Uuid,
) -> std::result::Result<File, DomainError> {
unimplemented!()
}
@@ -574,6 +575,7 @@ impl FileWritePort for MockFileRepository {
&self,
_file_id: &str,
_target_folder_id: Option<String>,
_caller_id: Uuid,
) -> std::result::Result<File, DomainError> {
unimplemented!()
}
@@ -582,6 +584,7 @@ impl FileWritePort for MockFileRepository {
&self,
_file_id: &str,
_new_name: &str,
_caller_id: Uuid,
) -> std::result::Result<File, DomainError> {
unimplemented!()
}
@@ -596,6 +599,7 @@ impl FileWritePort for MockFileRepository {
_blob_hash: &str,
_size: u64,
_modified_at: Option<i64>,
_caller_id: Uuid,
) -> std::result::Result<(String, i64), DomainError> {
Ok((String::new(), 0))
}
@@ -606,6 +610,7 @@ impl FileWritePort for MockFileRepository {
_folder_id: Option<String>,
_content_type: String,
_size: u64,
_caller_id: Uuid,
) -> std::result::Result<(File, PathBuf), DomainError> {
unimplemented!()
}
@@ -615,11 +620,16 @@ impl FileWritePort for MockFileRepository {
_file_id: &str,
_target_folder_id: Option<String>,
_new_name: Option<&str>,
_caller_id: Uuid,
) -> std::result::Result<File, DomainError> {
unimplemented!()
}
async fn move_to_trash(&self, id: &str) -> std::result::Result<(), DomainError> {
async fn move_to_trash(
&self,
id: &str,
_caller_id: Uuid,
) -> std::result::Result<(), DomainError> {
let mut files = self.files.lock().unwrap();
let mut trashed = self.trashed_files.lock().unwrap();
@@ -635,6 +645,7 @@ impl FileWritePort for MockFileRepository {
&self,
id: &str,
_original_path: &str,
_caller_id: Uuid,
) -> std::result::Result<(), DomainError> {
let mut files = self.files.lock().unwrap();
let mut trashed = self.trashed_files.lock().unwrap();
@@ -698,6 +709,7 @@ impl FolderRepository for MockFolderRepository {
&self,
_name: String,
_parent_id: Option<String>,
_caller_id: Uuid,
) -> std::result::Result<Folder, DomainError> {
unimplemented!()
}
@@ -759,6 +771,7 @@ impl FolderRepository for MockFolderRepository {
&self,
_id: &str,
_new_name: String,
_caller_id: Uuid,
) -> std::result::Result<Folder, DomainError> {
unimplemented!()
}
@@ -767,6 +780,7 @@ impl FolderRepository for MockFolderRepository {
&self,
_id: &str,
_new_parent_id: Option<&str>,
_caller_id: Uuid,
) -> std::result::Result<Folder, DomainError> {
unimplemented!()
}
@@ -787,7 +801,11 @@ impl FolderRepository for MockFolderRepository {
Ok(StoragePath::from_string("/"))
}
async fn move_to_trash(&self, id: &str) -> std::result::Result<(), DomainError> {
async fn move_to_trash(
&self,
id: &str,
_caller_id: Uuid,
) -> std::result::Result<(), DomainError> {
let mut folders = self.folders.lock().unwrap();
let mut trashed = self.trashed_folders.lock().unwrap();
@@ -803,6 +821,7 @@ impl FolderRepository for MockFolderRepository {
&self,
id: &str,
_original_path: &str,
_caller_id: Uuid,
) -> std::result::Result<(), DomainError> {
let mut folders = self.folders.lock().unwrap();
let mut trashed = self.trashed_folders.lock().unwrap();