folder create idempotency

This commit is contained in:
2025-10-30 02:15:53 +01:00
parent f89c6bb259
commit e5d44c13b4
3 changed files with 45 additions and 27 deletions
+4 -1
View File
@@ -502,7 +502,10 @@ mod doc {
post, post,
path = "/api/folders", path = "/api/folders",
request_body = CreateFolderRequest, request_body = CreateFolderRequest,
responses((status = 200, description = "Folder created", body = FolderResponse)), responses(
(status = 201, description = "Folder created", body = FolderResponse),
(status = 200, description = "Folder already existed", body = FolderResponse)
),
tag = "Folders" tag = "Folders"
)] )]
pub(super) fn create_folder() {} pub(super) fn create_folder() {}
+23 -8
View File
@@ -179,7 +179,7 @@ pub async fn create_folder(
.. ..
}: TenantScopedConn, }: TenantScopedConn,
Json(payload): Json<CreateFolderRequest>, Json(payload): Json<CreateFolderRequest>,
) -> AppResult<Json<FolderResponse>> { ) -> AppResult<(StatusCode, Json<FolderResponse>)> {
if payload.name.trim().is_empty() { if payload.name.trim().is_empty() {
return Err(AppError::bad_request("name must not be empty")); return Err(AppError::bad_request("name must not be empty"));
} }
@@ -202,8 +202,8 @@ pub async fn create_folder(
.optional()? .optional()?
}; };
let folder: Folder = if let Some(folder) = existing { let (folder, created): (Folder, bool) = if let Some(folder) = existing {
folder (folder, false)
} else { } else {
let new_folder = NewFolder { let new_folder = NewFolder {
id: Uuid::new_v4(), id: Uuid::new_v4(),
@@ -220,28 +220,43 @@ pub async fn create_folder(
.optional()?; .optional()?;
if let Some(id) = inserted_id { if let Some(id) = inserted_id {
(
folders::table folders::table
.find(id) .find(id)
.filter(folders::tenant_id.eq(tenant_id)) .filter(folders::tenant_id.eq(tenant_id))
.first(&mut conn)? .first(&mut conn)?,
true,
)
} else if let Some(parent_id) = payload.parent_id { } else if let Some(parent_id) = payload.parent_id {
(
folders::table folders::table
.filter(folders::tenant_id.eq(tenant_id)) .filter(folders::tenant_id.eq(tenant_id))
.filter(folders::parent_id.eq(Some(parent_id))) .filter(folders::parent_id.eq(Some(parent_id)))
.filter(folders::name.eq(name)) .filter(folders::name.eq(name))
.first(&mut conn)? .first(&mut conn)?,
false,
)
} else { } else {
(
folders::table folders::table
.filter(folders::tenant_id.eq(tenant_id)) .filter(folders::tenant_id.eq(tenant_id))
.filter(folders::parent_id.is_null()) .filter(folders::parent_id.is_null())
.filter(folders::name.eq(name)) .filter(folders::name.eq(name))
.first(&mut conn)? .first(&mut conn)?,
false,
)
} }
}; };
Ok(Json(FolderResponse { let response = Json(FolderResponse {
folder: folder_to_info(folder), folder: folder_to_info(folder),
})) });
if created {
Ok((StatusCode::CREATED, response))
} else {
Ok((StatusCode::OK, response))
}
} }
pub async fn list_folder_contents( pub async fn list_folder_contents(
+7 -7
View File
@@ -73,7 +73,7 @@ async fn folder_move_and_delete_flow() -> Result<()> {
Some(&token), Some(&token),
) )
.await?; .await?;
assert_eq!(folder_resp.status(), StatusCode::OK); assert_eq!(folder_resp.status(), StatusCode::CREATED);
let folder_body = body_to_vec(folder_resp.into_body()).await?; let folder_body = body_to_vec(folder_resp.into_body()).await?;
let folder: FolderResponse = serde_json::from_slice(&folder_body)?; let folder: FolderResponse = serde_json::from_slice(&folder_body)?;
@@ -164,7 +164,7 @@ async fn update_folder_parent_to_root() -> Result<()> {
Some(&token), Some(&token),
) )
.await?; .await?;
assert_eq!(parent_resp.status(), StatusCode::OK); assert_eq!(parent_resp.status(), StatusCode::CREATED);
let parent_body = body_to_vec(parent_resp.into_body()).await?; let parent_body = body_to_vec(parent_resp.into_body()).await?;
let parent: FolderResponse = serde_json::from_slice(&parent_body)?; let parent: FolderResponse = serde_json::from_slice(&parent_body)?;
@@ -179,7 +179,7 @@ async fn update_folder_parent_to_root() -> Result<()> {
Some(&token), Some(&token),
) )
.await?; .await?;
assert_eq!(child_resp.status(), StatusCode::OK); assert_eq!(child_resp.status(), StatusCode::CREATED);
let child_body = body_to_vec(child_resp.into_body()).await?; let child_body = body_to_vec(child_resp.into_body()).await?;
let child: FolderResponse = serde_json::from_slice(&child_body)?; let child: FolderResponse = serde_json::from_slice(&child_body)?;
@@ -310,14 +310,14 @@ async fn create_folder_is_idempotent() -> Result<()> {
let first_resp = app let first_resp = app
.post_json("/api/folders", &payload, Some(&token)) .post_json("/api/folders", &payload, Some(&token))
.await?; .await?;
assert!(first_resp.status().is_success()); assert_eq!(first_resp.status(), StatusCode::CREATED);
let first_body = body_to_vec(first_resp.into_body()).await?; let first_body = body_to_vec(first_resp.into_body()).await?;
let first_folder: FolderResponse = serde_json::from_slice(&first_body)?; let first_folder: FolderResponse = serde_json::from_slice(&first_body)?;
let second_resp = app let second_resp = app
.post_json("/api/folders", &payload, Some(&token)) .post_json("/api/folders", &payload, Some(&token))
.await?; .await?;
assert!(second_resp.status().is_success()); assert_eq!(second_resp.status(), StatusCode::OK);
let second_body = body_to_vec(second_resp.into_body()).await?; let second_body = body_to_vec(second_resp.into_body()).await?;
let second_folder: FolderResponse = serde_json::from_slice(&second_body)?; let second_folder: FolderResponse = serde_json::from_slice(&second_body)?;
@@ -424,7 +424,7 @@ async fn folder_rename_updates_name_and_child_paths() -> Result<()> {
Some(&token), Some(&token),
) )
.await?; .await?;
assert_eq!(parent_resp.status(), StatusCode::OK); assert_eq!(parent_resp.status(), StatusCode::CREATED);
let parent_body = body_to_vec(parent_resp.into_body()).await?; let parent_body = body_to_vec(parent_resp.into_body()).await?;
let parent: FolderResponse = serde_json::from_slice(&parent_body)?; let parent: FolderResponse = serde_json::from_slice(&parent_body)?;
@@ -438,7 +438,7 @@ async fn folder_rename_updates_name_and_child_paths() -> Result<()> {
Some(&token), Some(&token),
) )
.await?; .await?;
assert_eq!(child_resp.status(), StatusCode::OK); assert_eq!(child_resp.status(), StatusCode::CREATED);
let child_body = body_to_vec(child_resp.into_body()).await?; let child_body = body_to_vec(child_resp.into_body()).await?;
let child: FolderResponse = serde_json::from_slice(&child_body)?; let child: FolderResponse = serde_json::from_slice(&child_body)?;