Merge api_dashboard_delete_endpoint branch, resolving conflicts

Integrated delete dashboard endpoint with existing update endpoint:
- Fixed merge conflicts in dashboard project plan
- Combined routes in mod.rs to support GET, PUT, and DELETE endpoints
- Organized test module imports alphabetically
This commit is contained in:
dal 2025-03-19 21:30:20 -06:00
commit 956da48103
No known key found for this signature in database
GPG Key ID: 16F4B0E1E9F61122
8 changed files with 271 additions and 20 deletions

View File

@ -0,0 +1,86 @@
use anyhow::{anyhow, Result};
use chrono::Utc;
use database::pool::get_pg_pool;
use database::schema::dashboard_files;
use diesel::{ExpressionMethods, QueryDsl};
use diesel_async::RunQueryDsl;
use uuid::Uuid;
pub async fn delete_dashboard_handler(dashboard_id: Uuid, user_id: &Uuid) -> Result<()> {
let mut conn = get_pg_pool().get().await?;
// Check if the dashboard exists and is not already deleted
let dashboard_exists = diesel::select(diesel::dsl::exists(
dashboard_files::table
.filter(dashboard_files::id.eq(dashboard_id))
.filter(dashboard_files::deleted_at.is_null())
))
.get_result::<bool>(&mut conn)
.await?;
if !dashboard_exists {
return Err(anyhow!("Dashboard not found or already deleted"));
}
// Soft delete the dashboard by setting deleted_at to the current time
let now = Utc::now();
let rows_affected = diesel::update(dashboard_files::table)
.filter(dashboard_files::id.eq(dashboard_id))
.filter(dashboard_files::deleted_at.is_null())
.set(dashboard_files::deleted_at.eq(now))
.execute(&mut conn)
.await?;
if rows_affected == 0 {
return Err(anyhow!("Failed to delete dashboard"));
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use uuid::Uuid;
// Note: These tests would normally use a test database fixture
// For now, we'll just sketch the test structure
#[tokio::test]
async fn test_delete_dashboard_handler() {
// This would require setting up a test database with a dashboard
// For a real implementation, we would:
// 1. Create a test database
// 2. Insert a test dashboard
// 3. Call delete_dashboard_handler
// 4. Verify the dashboard is marked as deleted
// For now, just demonstrate the test structure
let dashboard_id = Uuid::new_v4();
let user_id = Uuid::new_v4();
// In a real test with fixtures:
// let result = delete_dashboard_handler(dashboard_id, &user_id).await;
// assert!(result.is_ok());
// Then verify the dashboard is marked as deleted in the database
}
#[tokio::test]
async fn test_delete_nonexistent_dashboard() {
// This would require setting up a test database
// For a real implementation, we would:
// 1. Create a test database
// 2. Call delete_dashboard_handler with a non-existent ID
// 3. Verify we get the expected error
// For now, just demonstrate the test structure
let dashboard_id = Uuid::new_v4(); // A random ID that doesn't exist
let user_id = Uuid::new_v4();
// In a real test with fixtures:
// let result = delete_dashboard_handler(dashboard_id, &user_id).await;
// assert!(result.is_err());
// assert!(result.unwrap_err().to_string().contains("not found"));
}
}

View File

@ -1,9 +1,11 @@
mod delete_dashboard_handler;
mod get_dashboard_handler;
mod list_dashboard_handler;
mod update_dashboard_handler;
mod types;
pub mod sharing;
pub use delete_dashboard_handler::*;
pub use get_dashboard_handler::*;
pub use list_dashboard_handler::*;
pub use update_dashboard_handler::*;

View File

@ -329,18 +329,18 @@ async fn test_delete_nonexistent_dashboard_endpoint() -> Result<()> {
## Implementation Plan
1. Create the business logic handler
2. Create the REST endpoint handler
3. Update module files
4. Add unit tests
5. Add integration tests
6. Manual testing
1. Create the business logic handler
2. Create the REST endpoint handler
3. Update module files
4. Add unit tests
5. Add integration tests
6. Manual testing
## Success Criteria
1. The endpoint successfully marks a dashboard as deleted
2. The endpoint returns a properly formatted response
3. Deleted dashboards are no longer accessible via the API
4. All tests pass
5. The endpoint is properly documented
6. The endpoint is secured with authentication
1. The endpoint successfully marks a dashboard as deleted
2. The endpoint returns a properly formatted response
3. Deleted dashboards are no longer accessible via the API
4. All tests pass
5. The endpoint is properly documented
6. The endpoint is secured with authentication

View File

@ -44,10 +44,10 @@ The implementation is divided into phases based on dependencies and complexity.
- Implement REST handler
- Update module files
- [Delete Dashboard Endpoint](mdc:prds/active/api_dashboard_delete_endpoint.md)
- Implement business logic handler
- Implement REST handler
- Update module files
- [Delete Dashboard Endpoint](mdc:prds/active/api_dashboard_delete_endpoint.md)
- Implement business logic handler
- Implement REST handler
- Update module files
**Rationale:**
- These endpoints have minimal dependencies on each other
@ -80,12 +80,12 @@ The implementation is divided into phases based on dependencies and complexity.
- Unit Tests for all endpoints
- [Create Dashboard](mdc:prds/active/api_dashboard_create_endpoint.md) tests
- [Update Dashboard](mdc:prds/active/api_dashboard_update_endpoint.md) tests ✅
- [Delete Dashboard](mdc:prds/active/api_dashboard_delete_endpoint.md) tests
- [Delete Dashboard](mdc:prds/active/api_dashboard_delete_endpoint.md) tests
- Integration Tests for all endpoints
- [Create Dashboard](mdc:prds/active/api_dashboard_create_endpoint.md) tests
- [Update Dashboard](mdc:prds/active/api_dashboard_update_endpoint.md) tests ✅
- [Delete Dashboard](mdc:prds/active/api_dashboard_delete_endpoint.md) tests
- [Delete Dashboard](mdc:prds/active/api_dashboard_delete_endpoint.md) tests
**Rationale:**
- Tests can be developed in parallel once the endpoints are implemented

View File

@ -0,0 +1,38 @@
use axum::{
extract::Path,
Extension,
Json,
};
use handlers::dashboards::delete_dashboard_handler;
use middleware::AuthenticatedUser;
use uuid::Uuid;
use axum::http::StatusCode;
use crate::routes::rest::ApiResponse;
pub async fn delete_dashboard_rest_handler(
Extension(user): Extension<AuthenticatedUser>,
Path(id): Path<Uuid>,
) -> Result<Json<serde_json::Value>, (StatusCode, String)> {
tracing::info!(
"Processing DELETE request for dashboard with ID: {}, user_id: {}",
id,
user.id
);
match delete_dashboard_handler(id, &user.id).await {
Ok(_) => Ok(Json(serde_json::json!({
"success": true,
"message": "Dashboard deleted successfully"
}))),
Err(e) => {
tracing::error!("Failed to delete dashboard: {}", e);
if e.to_string().contains("not found") {
Err((StatusCode::NOT_FOUND, format!("Dashboard not found: {}", e)))
} else {
Err((StatusCode::INTERNAL_SERVER_ERROR, format!("Failed to delete dashboard: {}", e)))
}
}
}
}

View File

@ -5,6 +5,7 @@ use axum::{
};
// Modules for dashboard endpoints
mod delete_dashboard;
mod get_dashboard;
mod list_dashboards;
mod update_dashboard;
@ -14,6 +15,7 @@ pub fn router() -> Router {
Router::new()
.route("/:id", get(get_dashboard::get_dashboard_rest_handler))
.route("/:id", put(update_dashboard::update_dashboard_rest_handler))
.route("/:id", delete(delete_dashboard::delete_dashboard_rest_handler))
.route("/", get(list_dashboards::list_dashboard_rest_handler))
.route(
"/:id/sharing",

View File

@ -0,0 +1,122 @@
use anyhow::Result;
use chrono::Utc;
use database::{
models::DashboardFile,
pool::get_pg_pool,
schema::dashboard_files,
types::VersionHistory,
};
use diesel::{ExpressionMethods, QueryDsl};
use diesel_async::RunQueryDsl;
use handlers::dashboards::delete_dashboard_handler;
use serde_json::json;
use tokio;
use uuid::Uuid;
use crate::common::{
db::TestDb,
env::setup_test_env,
fixtures,
};
#[tokio::test]
async fn test_delete_dashboard_handler() -> Result<()> {
// Setup test environment
setup_test_env();
// Initialize test database
let test_db = TestDb::new().await?;
let mut conn = test_db.get_conn().await?;
// Create test user and organization
let user_id = Uuid::new_v4();
let org_id = Uuid::new_v4();
// Create test dashboard
let test_dashboard = fixtures::dashboards::create_test_dashboard_file(&user_id, &org_id, Some("Test Dashboard For Deletion".to_string()));
let dashboard_id = test_dashboard.id;
// Insert test dashboard into database
diesel::insert_into(dashboard_files::table)
.values(&test_dashboard)
.execute(&mut conn)
.await?;
// Call the handler being tested
delete_dashboard_handler(dashboard_id, &user_id).await?;
// Fetch the deleted dashboard from the database
let db_dashboard = dashboard_files::table
.filter(dashboard_files::id.eq(dashboard_id))
.first::<DashboardFile>(&mut conn)
.await?;
// Verify it has been soft deleted (deleted_at is set)
assert!(db_dashboard.deleted_at.is_some());
// Trying to delete it again should return an error
let result = delete_dashboard_handler(dashboard_id, &user_id).await;
assert!(result.is_err());
Ok(())
}
#[tokio::test]
async fn test_delete_dashboard_handler_not_found() -> Result<()> {
// Setup test environment
setup_test_env();
// Initialize test database
let _test_db = TestDb::new().await?;
// Create test user
let user_id = Uuid::new_v4();
// Use a random UUID that doesn't exist
let nonexistent_dashboard_id = Uuid::new_v4();
// Call the handler being tested - should fail
let result = delete_dashboard_handler(nonexistent_dashboard_id, &user_id).await;
// Verify the error
assert!(result.is_err());
let error = result.unwrap_err().to_string();
assert!(error.contains("not found"));
Ok(())
}
#[tokio::test]
async fn test_delete_already_deleted_dashboard() -> Result<()> {
// Setup test environment
setup_test_env();
// Initialize test database
let test_db = TestDb::new().await?;
let mut conn = test_db.get_conn().await?;
// Create test user and organization
let user_id = Uuid::new_v4();
let org_id = Uuid::new_v4();
// Create test dashboard with deleted_at already set
let mut test_dashboard = fixtures::dashboards::create_test_dashboard_file(&user_id, &org_id, Some("Already Deleted Dashboard".to_string()));
test_dashboard.deleted_at = Some(Utc::now());
let dashboard_id = test_dashboard.id;
// Insert test dashboard into database
diesel::insert_into(dashboard_files::table)
.values(&test_dashboard)
.execute(&mut conn)
.await?;
// Call the handler being tested - should fail because it's already deleted
let result = delete_dashboard_handler(dashboard_id, &user_id).await;
// Verify the error
assert!(result.is_err());
let error = result.unwrap_err().to_string();
assert!(error.contains("not found") || error.contains("already deleted"));
Ok(())
}

View File

@ -1,3 +1,4 @@
pub mod sharing;
pub mod delete_dashboard_test;
pub mod get_dashboard_test;
pub mod sharing;
pub mod update_dashboard_test;