mirror of https://github.com/buster-so/buster.git
sharing work
This commit is contained in:
parent
ff8a443e42
commit
709cff0483
|
@ -15,22 +15,19 @@ use uuid::Uuid;
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `metric_id` - The UUID of the metric to create sharing permissions for
|
/// * `metric_id` - The UUID of the metric to create sharing permissions for
|
||||||
/// * `user_id` - The UUID of the user making the request
|
/// * `user_id` - The UUID of the user making the request
|
||||||
/// * `emails` - List of emails to share the metric with
|
/// * `emails_and_roles` - List of tuples containing (email, role) pairs
|
||||||
/// * `role` - The role to assign to the shared users
|
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
/// * `Result<()>` - Success if all sharing permissions were created
|
/// * `Result<()>` - Success if all sharing permissions were created
|
||||||
pub async fn create_metric_sharing_handler(
|
pub async fn create_metric_sharing_handler(
|
||||||
metric_id: &Uuid,
|
metric_id: &Uuid,
|
||||||
user_id: &Uuid,
|
user_id: &Uuid,
|
||||||
emails: Vec<String>,
|
emails_and_roles: Vec<(String, AssetPermissionRole)>,
|
||||||
role: AssetPermissionRole,
|
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
info!(
|
info!(
|
||||||
metric_id = %metric_id,
|
metric_id = %metric_id,
|
||||||
user_id = %user_id,
|
user_id = %user_id,
|
||||||
emails_count = emails.len(),
|
recipients_count = emails_and_roles.len(),
|
||||||
role = ?role,
|
|
||||||
"Creating sharing permissions for metric"
|
"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"));
|
return Err(anyhow!("User does not have permission to share this metric"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Process each email and create sharing permissions
|
// 3. Process each email-role pair and create sharing permissions
|
||||||
for email in emails {
|
for (email, role) in emails_and_roles {
|
||||||
// Validate email format
|
// Validate email format
|
||||||
if !email.contains('@') {
|
if !email.contains('@') {
|
||||||
return Err(anyhow!("Invalid email format: {}", email));
|
return Err(anyhow!("Invalid email format: {}", email));
|
||||||
|
@ -69,7 +66,7 @@ pub async fn create_metric_sharing_handler(
|
||||||
*user_id,
|
*user_id,
|
||||||
).await {
|
).await {
|
||||||
Ok(_) => {
|
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) => {
|
Err(e) => {
|
||||||
return Err(anyhow!("Failed to create sharing for email {}: {}", email, 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() {
|
async fn test_create_metric_sharing_invalid_email() {
|
||||||
let metric_id = Uuid::new_v4();
|
let metric_id = Uuid::new_v4();
|
||||||
let user_id = Uuid::new_v4();
|
let user_id = Uuid::new_v4();
|
||||||
let role = AssetPermissionRole::CanView;
|
let emails_and_roles = vec![("invalid-email-format".to_string(), AssetPermissionRole::CanView)];
|
||||||
let emails = vec!["invalid-email-format".to_string()];
|
|
||||||
|
|
||||||
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());
|
assert!(result.is_err());
|
||||||
let error = result.unwrap_err().to_string();
|
let error = result.unwrap_err().to_string();
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::{Path, Json},
|
extract::{Json, Path},
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
Extension,
|
Extension,
|
||||||
};
|
};
|
||||||
use database::enums::AssetPermissionRole;
|
use database::enums::AssetPermissionRole;
|
||||||
use handlers::metrics::sharing::create_metric_sharing_handler;
|
use handlers::metrics::sharing::create_metric_sharing_handler;
|
||||||
use middleware::AuthenticatedUser;
|
use middleware::AuthenticatedUser;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::Deserialize;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::routes::rest::ApiResponse;
|
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)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct SharingRequest {
|
pub struct ShareRecipient {
|
||||||
pub emails: Vec<String>,
|
pub email: String,
|
||||||
pub role: AssetPermissionRole,
|
pub role: AssetPermissionRole,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,27 +22,44 @@ pub struct SharingRequest {
|
||||||
pub async fn create_metric_sharing_rest_handler(
|
pub async fn create_metric_sharing_rest_handler(
|
||||||
Extension(user): Extension<AuthenticatedUser>,
|
Extension(user): Extension<AuthenticatedUser>,
|
||||||
Path(id): Path<Uuid>,
|
Path(id): Path<Uuid>,
|
||||||
Json(request): Json<SharingRequest>,
|
Json(request): Json<Vec<ShareRecipient>>,
|
||||||
) -> Result<ApiResponse<String>, (StatusCode, String)> {
|
) -> Result<ApiResponse<String>, (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 {
|
let emails_and_roles: Vec<(String, AssetPermissionRole)> = request
|
||||||
Ok(_) => Ok(ApiResponse::JsonData("Sharing permissions created successfully".to_string())),
|
.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) => {
|
Err(e) => {
|
||||||
tracing::error!("Error creating sharing permissions: {}", e);
|
tracing::error!("Error creating sharing permissions: {}", e);
|
||||||
|
|
||||||
// Map specific errors to appropriate status codes
|
// Map specific errors to appropriate status codes
|
||||||
let error_message = e.to_string();
|
let error_message = e.to_string();
|
||||||
|
|
||||||
if error_message.contains("not found") {
|
if error_message.contains("not found") {
|
||||||
return Err((StatusCode::NOT_FOUND, format!("Metric not found: {}", e)));
|
return Err((StatusCode::NOT_FOUND, format!("Metric not found: {}", e)));
|
||||||
} else if error_message.contains("permission") {
|
} 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") {
|
} else if error_message.contains("Invalid email") {
|
||||||
return Err((StatusCode::BAD_REQUEST, format!("Invalid email: {}", e)));
|
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),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,35 +10,41 @@ use uuid::Uuid;
|
||||||
|
|
||||||
use crate::routes::rest::ApiResponse;
|
use crate::routes::rest::ApiResponse;
|
||||||
|
|
||||||
/// Request structure for deleting sharing permissions
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub struct DeleteSharingRequest {
|
|
||||||
pub emails: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// REST handler for deleting sharing permissions for a metric
|
/// REST handler for deleting sharing permissions for a metric
|
||||||
pub async fn delete_metric_sharing_rest_handler(
|
pub async fn delete_metric_sharing_rest_handler(
|
||||||
Extension(user): Extension<AuthenticatedUser>,
|
Extension(user): Extension<AuthenticatedUser>,
|
||||||
Path(id): Path<Uuid>,
|
Path(id): Path<Uuid>,
|
||||||
Json(request): Json<DeleteSharingRequest>,
|
Json(request): Json<Vec<String>>,
|
||||||
) -> Result<ApiResponse<String>, (StatusCode, String)> {
|
) -> Result<ApiResponse<String>, (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 {
|
match delete_metric_sharing_handler(&id, &user.id, request).await {
|
||||||
Ok(_) => Ok(ApiResponse::JsonData("Sharing permissions deleted successfully".to_string())),
|
Ok(_) => Ok(ApiResponse::JsonData(
|
||||||
|
"Sharing permissions deleted successfully".to_string(),
|
||||||
|
)),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Error deleting sharing permissions: {}", e);
|
tracing::error!("Error deleting sharing permissions: {}", e);
|
||||||
|
|
||||||
// Map specific errors to appropriate status codes
|
// Map specific errors to appropriate status codes
|
||||||
if e.to_string().contains("not found") {
|
if e.to_string().contains("not found") {
|
||||||
return Err((StatusCode::NOT_FOUND, format!("Metric not found: {}", e)));
|
return Err((StatusCode::NOT_FOUND, format!("Metric not found: {}", e)));
|
||||||
} else if e.to_string().contains("permission") {
|
} 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") {
|
} else if e.to_string().contains("invalid email") {
|
||||||
return Err((StatusCode::BAD_REQUEST, format!("Invalid email: {}", e)));
|
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),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
use axum::{
|
use axum::{extract::Path, http::StatusCode, Extension};
|
||||||
extract::Path,
|
|
||||||
http::StatusCode,
|
|
||||||
Extension,
|
|
||||||
};
|
|
||||||
use handlers::metrics::sharing::list_metric_sharing_handler;
|
use handlers::metrics::sharing::list_metric_sharing_handler;
|
||||||
use middleware::AuthenticatedUser;
|
use middleware::AuthenticatedUser;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -30,34 +26,42 @@ pub struct SharingPermission {
|
||||||
pub async fn list_metric_sharing_rest_handler(
|
pub async fn list_metric_sharing_rest_handler(
|
||||||
Extension(user): Extension<AuthenticatedUser>,
|
Extension(user): Extension<AuthenticatedUser>,
|
||||||
Path(id): Path<Uuid>,
|
Path(id): Path<Uuid>,
|
||||||
) -> Result<ApiResponse<SharingResponse>, (StatusCode, String)> {
|
) -> Result<ApiResponse<Vec<SharingPermission>>, (StatusCode, String)> {
|
||||||
tracing::info!("Processing GET request for metric sharing with ID: {}, user_id: {}", id, user.id);
|
tracing::info!(
|
||||||
|
"Processing GET request for metric sharing with ID: {}, user_id: {}",
|
||||||
|
id,
|
||||||
|
user.id
|
||||||
|
);
|
||||||
|
|
||||||
match list_metric_sharing_handler(&id, &user.id).await {
|
match list_metric_sharing_handler(&id, &user.id).await {
|
||||||
Ok(permissions) => {
|
Ok(permissions) => {
|
||||||
let response = SharingResponse {
|
let response = permissions
|
||||||
permissions: permissions.into_iter().map(|p| SharingPermission {
|
.into_iter()
|
||||||
|
.map(|p| SharingPermission {
|
||||||
user_id: p.user.as_ref().map(|u| u.id).unwrap_or_default(),
|
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(),
|
email: p.user.as_ref().map(|u| u.email.clone()).unwrap_or_default(),
|
||||||
name: p.user.as_ref().and_then(|u| u.name.clone()),
|
name: p.user.as_ref().and_then(|u| u.name.clone()),
|
||||||
avatar_url: p.user.as_ref().and_then(|u| u.avatar_url.clone()),
|
avatar_url: p.user.as_ref().and_then(|u| u.avatar_url.clone()),
|
||||||
role: p.permission.role,
|
role: p.permission.role,
|
||||||
}).collect(),
|
})
|
||||||
};
|
.collect();
|
||||||
Ok(ApiResponse::JsonData(response))
|
Ok(ApiResponse::JsonData(response))
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Error listing sharing permissions: {}", e);
|
tracing::error!("Error listing sharing permissions: {}", e);
|
||||||
let error_message = e.to_string();
|
let error_message = e.to_string();
|
||||||
|
|
||||||
// Return appropriate status code based on error message
|
// Return appropriate status code based on error message
|
||||||
if error_message.contains("not found") {
|
if error_message.contains("not found") {
|
||||||
return Err((StatusCode::NOT_FOUND, format!("Metric not found: {}", e)));
|
return Err((StatusCode::NOT_FOUND, format!("Metric not found: {}", e)));
|
||||||
} else if error_message.contains("permission") {
|
} else if error_message.contains("permission") {
|
||||||
return Err((StatusCode::FORBIDDEN, format!("Permission denied: {}", e)));
|
return Err((StatusCode::FORBIDDEN, format!("Permission denied: {}", e)));
|
||||||
} else {
|
} 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),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,8 +74,12 @@ async fn test_create_metric_sharing_success() {
|
||||||
.post(&format!("/metrics/{}/sharing", metric.id))
|
.post(&format!("/metrics/{}/sharing", metric.id))
|
||||||
.with_auth(&owner.id.to_string())
|
.with_auth(&owner.id.to_string())
|
||||||
.json(&json!({
|
.json(&json!({
|
||||||
"emails": ["shared@example.com"],
|
"recipients": [
|
||||||
"role": "CanView"
|
{
|
||||||
|
"email": "shared@example.com",
|
||||||
|
"role": "CanView"
|
||||||
|
}
|
||||||
|
]
|
||||||
}))
|
}))
|
||||||
.send()
|
.send()
|
||||||
.await;
|
.await;
|
||||||
|
|
Loading…
Reference in New Issue