diff --git a/api/libs/handlers/src/metrics/sharing/create_sharing_handler.rs b/api/libs/handlers/src/metrics/sharing/create_sharing_handler.rs index a7d60891f..d10d32a7b 100644 --- a/api/libs/handlers/src/metrics/sharing/create_sharing_handler.rs +++ b/api/libs/handlers/src/metrics/sharing/create_sharing_handler.rs @@ -15,22 +15,19 @@ use uuid::Uuid; /// # Arguments /// * `metric_id` - The UUID of the metric to create sharing permissions for /// * `user_id` - The UUID of the user making the request -/// * `emails` - List of emails to share the metric with -/// * `role` - The role to assign to the shared users +/// * `emails_and_roles` - List of tuples containing (email, role) pairs /// /// # Returns /// * `Result<()>` - Success if all sharing permissions were created pub async fn create_metric_sharing_handler( metric_id: &Uuid, user_id: &Uuid, - emails: Vec, - role: AssetPermissionRole, + emails_and_roles: Vec<(String, AssetPermissionRole)>, ) -> Result<()> { info!( metric_id = %metric_id, user_id = %user_id, - emails_count = emails.len(), - role = ?role, + recipients_count = emails_and_roles.len(), "Creating sharing permissions for metric" ); @@ -53,8 +50,8 @@ pub async fn create_metric_sharing_handler( return Err(anyhow!("User does not have permission to share this metric")); } - // 3. Process each email and create sharing permissions - for email in emails { + // 3. Process each email-role pair and create sharing permissions + for (email, role) in emails_and_roles { // Validate email format if !email.contains('@') { return Err(anyhow!("Invalid email format: {}", email)); @@ -69,7 +66,7 @@ pub async fn create_metric_sharing_handler( *user_id, ).await { Ok(_) => { - info!("Created sharing permission for email: {} on metric: {}", email, metric_id); + info!("Created sharing permission for email: {} with role: {:?} on metric: {}", email, role, metric_id); }, Err(e) => { return Err(anyhow!("Failed to create sharing for email {}: {}", email, e)); @@ -89,10 +86,9 @@ mod tests { async fn test_create_metric_sharing_invalid_email() { let metric_id = Uuid::new_v4(); let user_id = Uuid::new_v4(); - let role = AssetPermissionRole::CanView; - let emails = vec!["invalid-email-format".to_string()]; + let emails_and_roles = vec![("invalid-email-format".to_string(), AssetPermissionRole::CanView)]; - let result = create_metric_sharing_handler(&metric_id, &user_id, emails, role).await; + let result = create_metric_sharing_handler(&metric_id, &user_id, emails_and_roles).await; assert!(result.is_err()); let error = result.unwrap_err().to_string(); diff --git a/api/src/routes/rest/routes/metrics/sharing/create_sharing.rs b/api/src/routes/rest/routes/metrics/sharing/create_sharing.rs index 4992ff877..9baa60671 100644 --- a/api/src/routes/rest/routes/metrics/sharing/create_sharing.rs +++ b/api/src/routes/rest/routes/metrics/sharing/create_sharing.rs @@ -1,20 +1,20 @@ use axum::{ - extract::{Path, Json}, + extract::{Json, Path}, http::StatusCode, Extension, }; use database::enums::AssetPermissionRole; use handlers::metrics::sharing::create_metric_sharing_handler; use middleware::AuthenticatedUser; -use serde::{Deserialize, Serialize}; +use serde::Deserialize; use uuid::Uuid; use crate::routes::rest::ApiResponse; -/// Request structure for sharing a metric with users +/// Structure for a single share recipient with their role #[derive(Debug, Deserialize)] -pub struct SharingRequest { - pub emails: Vec, +pub struct ShareRecipient { + pub email: String, pub role: AssetPermissionRole, } @@ -22,27 +22,44 @@ pub struct SharingRequest { pub async fn create_metric_sharing_rest_handler( Extension(user): Extension, Path(id): Path, - Json(request): Json, + Json(request): Json>, ) -> Result, (StatusCode, String)> { - tracing::info!("Processing POST request for metric sharing with ID: {}, user_id: {}", id, user.id); + tracing::info!( + "Processing POST request for metric sharing with ID: {}, user_id: {}", + id, + user.id + ); - match create_metric_sharing_handler(&id, &user.id, request.emails, request.role).await { - Ok(_) => Ok(ApiResponse::JsonData("Sharing permissions created successfully".to_string())), + let emails_and_roles: Vec<(String, AssetPermissionRole)> = request + .into_iter() + .map(|recipient| (recipient.email, recipient.role)) + .collect(); + + match create_metric_sharing_handler(&id, &user.id, emails_and_roles).await { + Ok(_) => Ok(ApiResponse::JsonData( + "Sharing permissions created successfully".to_string(), + )), Err(e) => { tracing::error!("Error creating sharing permissions: {}", e); - + // Map specific errors to appropriate status codes let error_message = e.to_string(); - + if error_message.contains("not found") { return Err((StatusCode::NOT_FOUND, format!("Metric not found: {}", e))); } else if error_message.contains("permission") { - return Err((StatusCode::FORBIDDEN, format!("Insufficient permissions: {}", e))); + return Err(( + StatusCode::FORBIDDEN, + format!("Insufficient permissions: {}", e), + )); } else if error_message.contains("Invalid email") { return Err((StatusCode::BAD_REQUEST, format!("Invalid email: {}", e))); } - - Err((StatusCode::INTERNAL_SERVER_ERROR, format!("Failed to create sharing permissions: {}", e))) + + Err(( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Failed to create sharing permissions: {}", e), + )) } } -} \ No newline at end of file +} diff --git a/api/src/routes/rest/routes/metrics/sharing/delete_sharing.rs b/api/src/routes/rest/routes/metrics/sharing/delete_sharing.rs index 75408aede..dbda55ed8 100644 --- a/api/src/routes/rest/routes/metrics/sharing/delete_sharing.rs +++ b/api/src/routes/rest/routes/metrics/sharing/delete_sharing.rs @@ -10,35 +10,41 @@ use uuid::Uuid; use crate::routes::rest::ApiResponse; -/// Request structure for deleting sharing permissions -#[derive(Debug, Deserialize)] -pub struct DeleteSharingRequest { - pub emails: Vec, -} - /// REST handler for deleting sharing permissions for a metric pub async fn delete_metric_sharing_rest_handler( Extension(user): Extension, Path(id): Path, - Json(request): Json, + Json(request): Json>, ) -> Result, (StatusCode, String)> { - tracing::info!("Processing DELETE request for metric sharing with ID: {}, user_id: {}", id, user.id); + tracing::info!( + "Processing DELETE request for metric sharing with ID: {}, user_id: {}", + id, + user.id + ); - match delete_metric_sharing_handler(&id, &user.id, request.emails).await { - Ok(_) => Ok(ApiResponse::JsonData("Sharing permissions deleted successfully".to_string())), + match delete_metric_sharing_handler(&id, &user.id, request).await { + Ok(_) => Ok(ApiResponse::JsonData( + "Sharing permissions deleted successfully".to_string(), + )), Err(e) => { tracing::error!("Error deleting sharing permissions: {}", e); - + // Map specific errors to appropriate status codes if e.to_string().contains("not found") { return Err((StatusCode::NOT_FOUND, format!("Metric not found: {}", e))); } else if e.to_string().contains("permission") { - return Err((StatusCode::FORBIDDEN, format!("Insufficient permissions: {}", e))); + return Err(( + StatusCode::FORBIDDEN, + format!("Insufficient permissions: {}", e), + )); } else if e.to_string().contains("invalid email") { return Err((StatusCode::BAD_REQUEST, format!("Invalid email: {}", e))); } - - Err((StatusCode::INTERNAL_SERVER_ERROR, format!("Failed to delete sharing permissions: {}", e))) + + Err(( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Failed to delete sharing permissions: {}", e), + )) } } -} \ No newline at end of file +} diff --git a/api/src/routes/rest/routes/metrics/sharing/list_sharing.rs b/api/src/routes/rest/routes/metrics/sharing/list_sharing.rs index c090fa614..a232cb640 100644 --- a/api/src/routes/rest/routes/metrics/sharing/list_sharing.rs +++ b/api/src/routes/rest/routes/metrics/sharing/list_sharing.rs @@ -1,8 +1,4 @@ -use axum::{ - extract::Path, - http::StatusCode, - Extension, -}; +use axum::{extract::Path, http::StatusCode, Extension}; use handlers::metrics::sharing::list_metric_sharing_handler; use middleware::AuthenticatedUser; use serde::{Deserialize, Serialize}; @@ -30,34 +26,42 @@ pub struct SharingPermission { pub async fn list_metric_sharing_rest_handler( Extension(user): Extension, Path(id): Path, -) -> Result, (StatusCode, String)> { - tracing::info!("Processing GET request for metric sharing with ID: {}, user_id: {}", id, user.id); +) -> Result>, (StatusCode, String)> { + tracing::info!( + "Processing GET request for metric sharing with ID: {}, user_id: {}", + id, + user.id + ); match list_metric_sharing_handler(&id, &user.id).await { Ok(permissions) => { - let response = SharingResponse { - permissions: permissions.into_iter().map(|p| SharingPermission { + let response = permissions + .into_iter() + .map(|p| SharingPermission { user_id: p.user.as_ref().map(|u| u.id).unwrap_or_default(), email: p.user.as_ref().map(|u| u.email.clone()).unwrap_or_default(), name: p.user.as_ref().and_then(|u| u.name.clone()), avatar_url: p.user.as_ref().and_then(|u| u.avatar_url.clone()), role: p.permission.role, - }).collect(), - }; + }) + .collect(); Ok(ApiResponse::JsonData(response)) - }, + } Err(e) => { tracing::error!("Error listing sharing permissions: {}", e); let error_message = e.to_string(); - + // Return appropriate status code based on error message if error_message.contains("not found") { return Err((StatusCode::NOT_FOUND, format!("Metric not found: {}", e))); } else if error_message.contains("permission") { return Err((StatusCode::FORBIDDEN, format!("Permission denied: {}", e))); } else { - return Err((StatusCode::INTERNAL_SERVER_ERROR, format!("Failed to list sharing permissions: {}", e))); + return Err(( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Failed to list sharing permissions: {}", e), + )); } } } -} \ No newline at end of file +} diff --git a/api/tests/integration/metrics/sharing/create_sharing_test.rs b/api/tests/integration/metrics/sharing/create_sharing_test.rs index 7556b5d03..312c5d0d4 100644 --- a/api/tests/integration/metrics/sharing/create_sharing_test.rs +++ b/api/tests/integration/metrics/sharing/create_sharing_test.rs @@ -74,8 +74,12 @@ async fn test_create_metric_sharing_success() { .post(&format!("/metrics/{}/sharing", metric.id)) .with_auth(&owner.id.to_string()) .json(&json!({ - "emails": ["shared@example.com"], - "role": "CanView" + "recipients": [ + { + "email": "shared@example.com", + "role": "CanView" + } + ] })) .send() .await;