From d9973a13dd2386083be86bcc55b146753720b00d Mon Sep 17 00:00:00 2001 From: dal Date: Wed, 5 Feb 2025 11:58:43 -0700 Subject: [PATCH] bugfix(datasets): add delete dataset route Implement a new DELETE route for removing datasets by their ID --- .../rest/routes/datasets/delete_dataset.rs | 81 +++++++++++++++++++ api/src/routes/rest/routes/datasets/mod.rs | 4 +- 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 api/src/routes/rest/routes/datasets/delete_dataset.rs diff --git a/api/src/routes/rest/routes/datasets/delete_dataset.rs b/api/src/routes/rest/routes/datasets/delete_dataset.rs new file mode 100644 index 000000000..2d9bb1a56 --- /dev/null +++ b/api/src/routes/rest/routes/datasets/delete_dataset.rs @@ -0,0 +1,81 @@ +use anyhow::{anyhow, Result}; +use axum::{extract::Path, http::StatusCode, Extension}; +use chrono::Utc; +use diesel::{update, ExpressionMethods, QueryDsl}; +use diesel_async::RunQueryDsl; +use serde_json::Value; +use uuid::Uuid; + +use crate::database::{lib::get_pg_pool, models::User, schema::datasets}; + +pub async fn delete_dataset( + Extension(user): Extension, + Path(dataset_id): Path, +) -> Result { + match delete_dataset_handler(&user, dataset_id).await { + Ok(_) => Ok(StatusCode::NO_CONTENT), + Err(e) => { + tracing::error!("Error deleting dataset: {}", e); + Err((StatusCode::INTERNAL_SERVER_ERROR, e.to_string())) + } + } +} + +async fn delete_dataset_handler(user: &User, dataset_id: Uuid) -> Result<()> { + let mut conn = get_pg_pool() + .get() + .await + .map_err(|e| anyhow!("Unable to get connection from pool: {}", e))?; + + // Get dataset's organization_id + let dataset = datasets::table + .select(datasets::organization_id) + .filter(datasets::id.eq(dataset_id)) + .filter(datasets::deleted_at.is_null()) + .first::(&mut conn) + .await + .map_err(|e| match e { + diesel::result::Error::NotFound => anyhow!("Dataset not found"), + _ => anyhow!("Error getting dataset: {}", e), + })?; + + // Check user's organization and role + let user_org_id = user + .attributes + .get("organization_id") + .and_then(|v| match v { + Value::String(s) => Some(s.as_str()), + _ => None, + }) + .ok_or_else(|| anyhow!("User organization id not found"))?; + + let user_org_id = + Uuid::parse_str(user_org_id).map_err(|_| anyhow!("Invalid organization id format"))?; + + if user_org_id != dataset { + return Err(anyhow!("User does not belong to dataset's organization")); + } + + let user_role = user + .attributes + .get("organization_role") + .and_then(|v| match v { + Value::String(s) => Some(s.as_str()), + _ => None, + }) + .ok_or_else(|| anyhow!("User role not found"))?; + + if !["workspace_admin", "data_admin"].contains(&user_role) { + return Err(anyhow!("User does not have required permissions")); + } + + // Soft delete the dataset + update(datasets::table) + .filter(datasets::id.eq(dataset_id)) + .set(datasets::deleted_at.eq(Some(Utc::now()))) + .execute(&mut conn) + .await + .map_err(|e| anyhow!("Error updating dataset: {}", e))?; + + Ok(()) +} diff --git a/api/src/routes/rest/routes/datasets/mod.rs b/api/src/routes/rest/routes/datasets/mod.rs index 6bc84f0e9..7bc000077 100644 --- a/api/src/routes/rest/routes/datasets/mod.rs +++ b/api/src/routes/rest/routes/datasets/mod.rs @@ -1,4 +1,5 @@ mod assets; +mod delete_dataset; mod deploy_datasets; mod get_dataset; mod get_dataset_data_sample; @@ -6,7 +7,7 @@ mod list_datasets; mod post_dataset; use axum::{ - routing::{get, post}, + routing::{get, post, delete}, Router, }; @@ -16,6 +17,7 @@ pub fn router() -> Router { .route("/", post(post_dataset::post_dataset)) .route("/deploy", post(deploy_datasets::deploy_datasets)) .route("/:dataset_id", get(get_dataset::get_dataset)) + .route("/:dataset_id", delete(delete_dataset::delete_dataset)) .route( "/:dataset_id/data/sample", get(get_dataset_data_sample::get_dataset_data_sample),