diff --git a/api/migrations/2025-01-21-184456_fix_dataset_to_dataset_groups_table/down.sql b/api/migrations/2025-01-21-184456_fix_dataset_to_dataset_groups_table/down.sql new file mode 100644 index 000000000..1c21f8c37 --- /dev/null +++ b/api/migrations/2025-01-21-184456_fix_dataset_to_dataset_groups_table/down.sql @@ -0,0 +1,4 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE datasets_to_dataset_groups +DROP COLUMN deleted_at, +DROP COLUMN updated_at; diff --git a/api/migrations/2025-01-21-184456_fix_dataset_to_dataset_groups_table/up.sql b/api/migrations/2025-01-21-184456_fix_dataset_to_dataset_groups_table/up.sql new file mode 100644 index 000000000..8b91852b4 --- /dev/null +++ b/api/migrations/2025-01-21-184456_fix_dataset_to_dataset_groups_table/up.sql @@ -0,0 +1,4 @@ +-- Your SQL goes here +ALTER TABLE datasets_to_dataset_groups +ADD COLUMN updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), +ADD COLUMN deleted_at TIMESTAMPTZ; diff --git a/api/src/database/models.rs b/api/src/database/models.rs index bd7f3de4b..c4e9be478 100644 --- a/api/src/database/models.rs +++ b/api/src/database/models.rs @@ -558,3 +558,15 @@ pub struct DatasetGroupPermission { pub updated_at: DateTime, pub deleted_at: Option>, } + +#[derive(Queryable, Insertable, Associations, Debug)] +#[diesel(belongs_to(Dataset, foreign_key = dataset_id))] +#[diesel(belongs_to(DatasetGroup, foreign_key = dataset_group_id))] +#[diesel(table_name = datasets_to_dataset_groups)] +pub struct DatasetToDatasetGroup { + pub dataset_id: Uuid, + pub dataset_group_id: Uuid, + pub created_at: DateTime, + pub updated_at: DateTime, + pub deleted_at: Option>, +} diff --git a/api/src/database/schema.rs b/api/src/database/schema.rs index 8dac857e8..710a4789a 100644 --- a/api/src/database/schema.rs +++ b/api/src/database/schema.rs @@ -260,6 +260,8 @@ diesel::table! { dataset_id -> Uuid, dataset_group_id -> Uuid, created_at -> Timestamptz, + updated_at -> Timestamptz, + deleted_at -> Nullable, } } diff --git a/api/src/routes/rest/routes/dataset_groups/assets/list_datasets.rs b/api/src/routes/rest/routes/dataset_groups/assets/list_datasets.rs new file mode 100644 index 000000000..98daab286 --- /dev/null +++ b/api/src/routes/rest/routes/dataset_groups/assets/list_datasets.rs @@ -0,0 +1,86 @@ +use anyhow::Result; +use axum::extract::Path; +use axum::http::StatusCode; +use axum::Extension; +use diesel::prelude::*; +use diesel_async::RunQueryDsl; +use serde::Serialize; +use uuid::Uuid; + +use crate::database::lib::get_pg_pool; +use crate::database::models::User; +use crate::database::schema::{datasets, datasets_to_dataset_groups}; +use crate::routes::rest::ApiResponse; +use crate::utils::security::checks::is_user_workspace_admin_or_data_admin; +use crate::utils::user::user_info::get_user_organization_id; + +/// Represents dataset information with its assignment status to a dataset group +#[derive(Debug, Serialize)] +pub struct DatasetInfo { + pub id: Uuid, + pub name: String, + pub assigned: bool, +} + +/// List datasets that can be associated with a dataset group +/// Returns datasets with their current assignment status to the specified dataset group +pub async fn list_datasets( + Extension(user): Extension, + Path(dataset_group_id): Path, +) -> Result>, (StatusCode, &'static str)> { + let datasets = match list_datasets_handler(user, dataset_group_id).await { + Ok(datasets) => datasets, + Err(e) => { + tracing::error!("Error listing datasets for dataset group: {:?}", e); + return Err(( + StatusCode::INTERNAL_SERVER_ERROR, + "Error listing datasets for dataset group", + )); + } + }; + + Ok(ApiResponse::JsonData(datasets)) +} + +async fn list_datasets_handler(user: User, dataset_group_id: Uuid) -> Result> { + let mut conn = get_pg_pool().get().await?; + let organization_id = get_user_organization_id(&user.id).await?; + + if !is_user_workspace_admin_or_data_admin(&user, &organization_id).await? { + return Err(anyhow::anyhow!( + "User is not authorized to list datasets for dataset group" + )); + } + + // Query datasets and their assignment status to the dataset group + let datasets = datasets::table + .left_join( + datasets_to_dataset_groups::table.on( + datasets_to_dataset_groups::dataset_id + .eq(datasets::id) + .and(datasets_to_dataset_groups::dataset_group_id.eq(dataset_group_id)) + .and(datasets_to_dataset_groups::deleted_at.is_null()), + ), + ) + .select(( + datasets::id, + datasets::name, + diesel::dsl::sql::( + "datasets_to_dataset_groups.dataset_id IS NOT NULL", + ), + )) + .filter(datasets::organization_id.eq(organization_id)) + .filter(datasets::deleted_at.is_null()) + .order_by(datasets::created_at.desc()) + .load::<(Uuid, String, bool)>(&mut *conn) + .await?; + + Ok(datasets + .into_iter() + .map(|(id, name, assigned)| DatasetInfo { + id, + name, + assigned, + }) + .collect()) +} \ No newline at end of file diff --git a/api/src/routes/rest/routes/dataset_groups/assets/list_permission_groups.rs b/api/src/routes/rest/routes/dataset_groups/assets/list_permission_groups.rs new file mode 100644 index 000000000..71f4e968d --- /dev/null +++ b/api/src/routes/rest/routes/dataset_groups/assets/list_permission_groups.rs @@ -0,0 +1,112 @@ +use anyhow::Result; +use axum::extract::Path; +use axum::http::StatusCode; +use axum::Extension; +use diesel::prelude::*; +use diesel_async::RunQueryDsl; +use serde::Serialize; +use uuid::Uuid; + +use crate::database::enums::IdentityType; +use crate::database::lib::get_pg_pool; +use crate::database::models::User; +use crate::database::schema::{ + dataset_groups_permissions, permission_groups, permission_groups_to_identities, +}; +use crate::routes::rest::ApiResponse; +use crate::utils::security::checks::is_user_workspace_admin_or_data_admin; +use crate::utils::user::user_info::get_user_organization_id; + +/// Represents permission group information with its assignment status to a dataset group +#[derive(Debug, Serialize)] +pub struct PermissionGroupInfo { + pub id: Uuid, + pub name: String, + pub user_count: i64, + pub assigned: bool, +} + +/// List permission groups that can be associated with a dataset group +/// Returns permission groups with their current assignment status to the specified dataset group +/// and the count of users in each permission group +pub async fn list_permission_groups( + Extension(user): Extension, + Path(dataset_group_id): Path, +) -> Result>, (StatusCode, &'static str)> { + let permission_groups = match list_permission_groups_handler(user, dataset_group_id).await { + Ok(groups) => groups, + Err(e) => { + tracing::error!("Error listing permission groups for dataset group: {:?}", e); + return Err(( + StatusCode::INTERNAL_SERVER_ERROR, + "Error listing permission groups for dataset group", + )); + } + }; + + Ok(ApiResponse::JsonData(permission_groups)) +} + +async fn list_permission_groups_handler( + user: User, + dataset_group_id: Uuid, +) -> Result> { + let mut conn = get_pg_pool().get().await?; + let organization_id = get_user_organization_id(&user.id).await?; + + if !is_user_workspace_admin_or_data_admin(&user, &organization_id).await? { + return Err(anyhow::anyhow!( + "User is not authorized to list permission groups for dataset group" + )); + } + + // Query permission groups with their user count and assignment status + let groups = permission_groups::table + .left_join( + dataset_groups_permissions::table.on( + dataset_groups_permissions::permission_id + .eq(permission_groups::id) + .and(dataset_groups_permissions::dataset_group_id.eq(dataset_group_id)) + .and(dataset_groups_permissions::permission_type.eq("permission_group")) + .and(dataset_groups_permissions::deleted_at.is_null()), + ), + ) + .left_join( + permission_groups_to_identities::table.on( + permission_groups_to_identities::permission_group_id + .eq(permission_groups::id) + .and(permission_groups_to_identities::identity_type.eq(IdentityType::User)) + .and(permission_groups_to_identities::deleted_at.is_null()), + ), + ) + .select(( + permission_groups::id, + permission_groups::name, + diesel::dsl::sql::( + "COALESCE(COUNT(DISTINCT permission_groups_to_identities.identity_id), 0)", + ), + diesel::dsl::sql::( + "dataset_groups_permissions.id IS NOT NULL", + ), + )) + .group_by(( + permission_groups::id, + permission_groups::name, + dataset_groups_permissions::id, + )) + .filter(permission_groups::organization_id.eq(organization_id)) + .filter(permission_groups::deleted_at.is_null()) + .order_by(permission_groups::created_at.desc()) + .load::<(Uuid, String, i64, bool)>(&mut *conn) + .await?; + + Ok(groups + .into_iter() + .map(|(id, name, user_count, assigned)| PermissionGroupInfo { + id, + name, + user_count, + assigned, + }) + .collect()) +} \ No newline at end of file diff --git a/api/src/routes/rest/routes/dataset_groups/assets/list_users.rs b/api/src/routes/rest/routes/dataset_groups/assets/list_users.rs new file mode 100644 index 000000000..3039b27aa --- /dev/null +++ b/api/src/routes/rest/routes/dataset_groups/assets/list_users.rs @@ -0,0 +1,94 @@ +use anyhow::Result; +use axum::extract::Path; +use axum::http::StatusCode; +use axum::Extension; +use diesel::prelude::*; +use diesel_async::RunQueryDsl; +use serde::Serialize; +use uuid::Uuid; + +use crate::database::lib::get_pg_pool; +use crate::database::models::User; +use crate::database::schema::{dataset_groups_permissions, users, users_to_organizations}; +use crate::routes::rest::ApiResponse; +use crate::utils::security::checks::is_user_workspace_admin_or_data_admin; +use crate::utils::user::user_info::get_user_organization_id; + +/// Represents user information with their assignment status to a dataset group +#[derive(Debug, Serialize)] +pub struct UserInfo { + pub id: Uuid, + pub name: String, + pub email: String, + pub assigned: bool, +} + +/// List users that can be associated with a dataset group +/// Returns users with their current assignment status to the specified dataset group +pub async fn list_users( + Extension(user): Extension, + Path(dataset_group_id): Path, +) -> Result>, (StatusCode, &'static str)> { + let users = match list_users_handler(user, dataset_group_id).await { + Ok(users) => users, + Err(e) => { + tracing::error!("Error listing users for dataset group: {:?}", e); + return Err(( + StatusCode::INTERNAL_SERVER_ERROR, + "Error listing users for dataset group", + )); + } + }; + + Ok(ApiResponse::JsonData(users)) +} + +async fn list_users_handler(user: User, dataset_group_id: Uuid) -> Result> { + let mut conn = get_pg_pool().get().await?; + let organization_id = get_user_organization_id(&user.id).await?; + + if !is_user_workspace_admin_or_data_admin(&user, &organization_id).await? { + return Err(anyhow::anyhow!( + "User is not authorized to list users for dataset group" + )); + } + + // Query users and their assignment status to the dataset group + let users = users::table + .left_join( + dataset_groups_permissions::table.on( + dataset_groups_permissions::permission_id + .eq(users::id) + .and(dataset_groups_permissions::dataset_group_id.eq(dataset_group_id)) + .and(dataset_groups_permissions::permission_type.eq("user")) + .and(dataset_groups_permissions::deleted_at.is_null()), + ), + ) + .inner_join( + users_to_organizations::table.on(users_to_organizations::user_id + .eq(users::id) + .and(users_to_organizations::organization_id.eq(organization_id)) + .and(users_to_organizations::deleted_at.is_null())), + ) + .select(( + users::id, + users::name.nullable(), + users::email, + diesel::dsl::sql::( + "dataset_groups_permissions.id IS NOT NULL", + ), + )) + .order_by(users::created_at.desc()) + .load::<(Uuid, Option, String, bool)>(&mut *conn) + .await?; + + Ok(users + .into_iter() + .map(|(id, name, email, assigned)| UserInfo { + id, + name: name.unwrap_or("".to_string()), + email, + assigned, + }) + .collect()) +} \ No newline at end of file diff --git a/api/src/routes/rest/routes/dataset_groups/assets/mod.rs b/api/src/routes/rest/routes/dataset_groups/assets/mod.rs new file mode 100644 index 000000000..222296327 --- /dev/null +++ b/api/src/routes/rest/routes/dataset_groups/assets/mod.rs @@ -0,0 +1,25 @@ +use axum::{routing::get, Router}; + +mod list_datasets; +mod list_permission_groups; +mod list_users; +mod put_datasets; +mod put_permission_groups; +mod put_users; + +pub use list_datasets::list_datasets; +pub use list_permission_groups::list_permission_groups; +pub use list_users::list_users; +pub use put_datasets::put_datasets; +pub use put_permission_groups::put_permission_groups; +pub use put_users::put_users; + +pub fn router() -> Router { + Router::new() + .route("/datasets", get(list_datasets).put(put_datasets)) + .route( + "/permission_groups", + get(list_permission_groups).put(put_permission_groups), + ) + .route("/users", get(list_users).put(put_users)) +} diff --git a/api/src/routes/rest/routes/dataset_groups/assets/put_datasets.rs b/api/src/routes/rest/routes/dataset_groups/assets/put_datasets.rs new file mode 100644 index 000000000..017cb653a --- /dev/null +++ b/api/src/routes/rest/routes/dataset_groups/assets/put_datasets.rs @@ -0,0 +1,113 @@ +use anyhow::Result; +use axum::extract::Path; +use axum::http::StatusCode; +use axum::{Extension, Json}; +use diesel::prelude::*; +use diesel_async::RunQueryDsl; +use serde::{Deserialize, Serialize}; +use tokio::spawn; +use uuid::Uuid; + +use crate::database::lib::get_pg_pool; +use crate::database::models::{DatasetToDatasetGroup, User}; +use crate::database::schema::datasets_to_dataset_groups; +use crate::routes::rest::ApiResponse; +use crate::utils::security::checks::is_user_workspace_admin_or_data_admin; +use crate::utils::user::user_info::get_user_organization_id; + +#[derive(Debug, Serialize, Deserialize)] +pub struct DatasetAssignment { + pub id: Uuid, + pub assigned: bool, +} + +/// Update dataset assignments for a dataset group +/// Accepts a list of dataset assignments to add or remove from the dataset group +pub async fn put_datasets( + Extension(user): Extension, + Path(dataset_group_id): Path, + Json(assignments): Json>, +) -> Result, (StatusCode, &'static str)> { + match put_datasets_handler(user, dataset_group_id, assignments).await { + Ok(_) => Ok(ApiResponse::NoContent), + Err(e) => { + tracing::error!("Error assigning datasets to dataset group: {:?}", e); + return Err(( + StatusCode::INTERNAL_SERVER_ERROR, + "Error assigning datasets to dataset group", + )); + } + } +} + +async fn put_datasets_handler( + user: User, + dataset_group_id: Uuid, + assignments: Vec, +) -> Result<()> { + let organization_id = get_user_organization_id(&user.id).await?; + + if !is_user_workspace_admin_or_data_admin(&user, &organization_id).await? { + return Err(anyhow::anyhow!( + "User is not authorized to assign datasets to dataset group" + )); + } + + let (to_assign, to_unassign): (Vec<_>, Vec<_>) = assignments.into_iter().partition(|a| a.assigned); + + let assign_handle = { + let dataset_group_id = dataset_group_id; + spawn(async move { + if !to_assign.is_empty() { + let mut conn = get_pg_pool().get().await?; + let values: Vec<_> = to_assign + .into_iter() + .map(|dataset| DatasetToDatasetGroup { + dataset_id: dataset.id, + dataset_group_id, + deleted_at: None, + created_at: chrono::Utc::now(), + updated_at: chrono::Utc::now(), + }) + .collect(); + + diesel::insert_into(datasets_to_dataset_groups::table) + .values(&values) + .on_conflict(( + datasets_to_dataset_groups::dataset_id, + datasets_to_dataset_groups::dataset_group_id, + )) + .do_update() + .set(datasets_to_dataset_groups::deleted_at.eq(None::>)) + .execute(&mut *conn) + .await?; + } + Ok::<_, anyhow::Error>(()) + }) + }; + + let unassign_handle = { + let dataset_group_id = dataset_group_id; + spawn(async move { + if !to_unassign.is_empty() { + let mut conn = get_pg_pool().get().await?; + diesel::update(datasets_to_dataset_groups::table) + .filter( + datasets_to_dataset_groups::dataset_id + .eq_any(to_unassign.iter().map(|a| a.id)) + .and(datasets_to_dataset_groups::dataset_group_id.eq(dataset_group_id)), + ) + .set(datasets_to_dataset_groups::deleted_at.eq(chrono::Utc::now())) + .execute(&mut *conn) + .await?; + } + Ok::<_, anyhow::Error>(()) + }) + }; + + let (assign_result, unassign_result) = tokio::try_join!(assign_handle, unassign_handle)?; + assign_result?; + unassign_result?; + + Ok(()) +} \ No newline at end of file diff --git a/api/src/routes/rest/routes/dataset_groups/assets/put_permission_groups.rs b/api/src/routes/rest/routes/dataset_groups/assets/put_permission_groups.rs new file mode 100644 index 000000000..3068175d4 --- /dev/null +++ b/api/src/routes/rest/routes/dataset_groups/assets/put_permission_groups.rs @@ -0,0 +1,120 @@ +use anyhow::Result; +use axum::extract::Path; +use axum::http::StatusCode; +use axum::{Extension, Json}; +use diesel::prelude::*; +use diesel_async::RunQueryDsl; +use serde::{Deserialize, Serialize}; +use tokio::spawn; +use uuid::Uuid; + +use crate::database::lib::get_pg_pool; +use crate::database::models::{DatasetGroupPermission, User}; +use crate::database::schema::dataset_groups_permissions; +use crate::routes::rest::ApiResponse; +use crate::utils::security::checks::is_user_workspace_admin_or_data_admin; +use crate::utils::user::user_info::get_user_organization_id; + +#[derive(Debug, Serialize, Deserialize)] +pub struct PermissionGroupAssignment { + pub id: Uuid, + pub assigned: bool, +} + +/// Update permission group assignments for a dataset group +/// Accepts a list of permission group assignments to add or remove from the dataset group +pub async fn put_permission_groups( + Extension(user): Extension, + Path(dataset_group_id): Path, + Json(assignments): Json>, +) -> Result, (StatusCode, &'static str)> { + match put_permission_groups_handler(user, dataset_group_id, assignments).await { + Ok(_) => Ok(ApiResponse::NoContent), + Err(e) => { + tracing::error!("Error assigning permission groups to dataset group: {:?}", e); + return Err(( + StatusCode::INTERNAL_SERVER_ERROR, + "Error assigning permission groups to dataset group", + )); + } + } +} + +async fn put_permission_groups_handler( + user: User, + dataset_group_id: Uuid, + assignments: Vec, +) -> Result<()> { + let organization_id = get_user_organization_id(&user.id).await?; + + if !is_user_workspace_admin_or_data_admin(&user, &organization_id).await? { + return Err(anyhow::anyhow!( + "User is not authorized to assign permission groups to dataset group" + )); + } + + let (to_assign, to_unassign): (Vec<_>, Vec<_>) = assignments.into_iter().partition(|a| a.assigned); + + let assign_handle = { + let dataset_group_id = dataset_group_id; + spawn(async move { + if !to_assign.is_empty() { + let mut conn = get_pg_pool().get().await?; + let values: Vec<_> = to_assign + .into_iter() + .map(|group| DatasetGroupPermission { + id: Uuid::new_v4(), + dataset_group_id, + permission_id: group.id, + permission_type: "permission_group".to_string(), + organization_id, + deleted_at: None, + created_at: chrono::Utc::now(), + updated_at: chrono::Utc::now(), + }) + .collect(); + + diesel::insert_into(dataset_groups_permissions::table) + .values(&values) + .on_conflict(( + dataset_groups_permissions::dataset_group_id, + dataset_groups_permissions::permission_id, + dataset_groups_permissions::permission_type, + )) + .do_update() + .set(dataset_groups_permissions::deleted_at.eq(None::>)) + .execute(&mut *conn) + .await?; + } + Ok::<_, anyhow::Error>(()) + }) + }; + + let unassign_handle = { + let dataset_group_id = dataset_group_id; + spawn(async move { + if !to_unassign.is_empty() { + let mut conn = get_pg_pool().get().await?; + diesel::update(dataset_groups_permissions::table) + .filter( + dataset_groups_permissions::dataset_group_id + .eq(dataset_group_id) + .and(dataset_groups_permissions::permission_id.eq_any( + to_unassign.iter().map(|a| a.id), + )) + .and(dataset_groups_permissions::permission_type.eq("permission_group")), + ) + .set(dataset_groups_permissions::deleted_at.eq(chrono::Utc::now())) + .execute(&mut *conn) + .await?; + } + Ok::<_, anyhow::Error>(()) + }) + }; + + let (assign_result, unassign_result) = tokio::try_join!(assign_handle, unassign_handle)?; + assign_result?; + unassign_result?; + + Ok(()) +} \ No newline at end of file diff --git a/api/src/routes/rest/routes/dataset_groups/assets/put_users.rs b/api/src/routes/rest/routes/dataset_groups/assets/put_users.rs new file mode 100644 index 000000000..ffd2cbba6 --- /dev/null +++ b/api/src/routes/rest/routes/dataset_groups/assets/put_users.rs @@ -0,0 +1,120 @@ +use anyhow::Result; +use axum::extract::Path; +use axum::http::StatusCode; +use axum::{Extension, Json}; +use diesel::prelude::*; +use diesel_async::RunQueryDsl; +use serde::{Deserialize, Serialize}; +use tokio::spawn; +use uuid::Uuid; + +use crate::database::lib::get_pg_pool; +use crate::database::models::{DatasetGroupPermission, User}; +use crate::database::schema::dataset_groups_permissions; +use crate::routes::rest::ApiResponse; +use crate::utils::security::checks::is_user_workspace_admin_or_data_admin; +use crate::utils::user::user_info::get_user_organization_id; + +#[derive(Debug, Serialize, Deserialize)] +pub struct UserAssignment { + pub id: Uuid, + pub assigned: bool, +} + +/// Update user assignments for a dataset group +/// Accepts a list of user assignments to add or remove from the dataset group +pub async fn put_users( + Extension(user): Extension, + Path(dataset_group_id): Path, + Json(assignments): Json>, +) -> Result, (StatusCode, &'static str)> { + match put_users_handler(user, dataset_group_id, assignments).await { + Ok(_) => Ok(ApiResponse::NoContent), + Err(e) => { + tracing::error!("Error assigning users to dataset group: {:?}", e); + return Err(( + StatusCode::INTERNAL_SERVER_ERROR, + "Error assigning users to dataset group", + )); + } + } +} + +async fn put_users_handler( + user: User, + dataset_group_id: Uuid, + assignments: Vec, +) -> Result<()> { + let organization_id = get_user_organization_id(&user.id).await?; + + if !is_user_workspace_admin_or_data_admin(&user, &organization_id).await? { + return Err(anyhow::anyhow!( + "User is not authorized to assign users to dataset group" + )); + } + + let (to_assign, to_unassign): (Vec<_>, Vec<_>) = assignments.into_iter().partition(|a| a.assigned); + + let assign_handle = { + let dataset_group_id = dataset_group_id; + spawn(async move { + if !to_assign.is_empty() { + let mut conn = get_pg_pool().get().await?; + let values: Vec<_> = to_assign + .into_iter() + .map(|user_assignment| DatasetGroupPermission { + id: Uuid::new_v4(), + dataset_group_id, + permission_id: user_assignment.id, + permission_type: "user".to_string(), + organization_id, + deleted_at: None, + created_at: chrono::Utc::now(), + updated_at: chrono::Utc::now(), + }) + .collect(); + + diesel::insert_into(dataset_groups_permissions::table) + .values(&values) + .on_conflict(( + dataset_groups_permissions::dataset_group_id, + dataset_groups_permissions::permission_id, + dataset_groups_permissions::permission_type, + )) + .do_update() + .set(dataset_groups_permissions::deleted_at.eq(None::>)) + .execute(&mut *conn) + .await?; + } + Ok::<_, anyhow::Error>(()) + }) + }; + + let unassign_handle = { + let dataset_group_id = dataset_group_id; + spawn(async move { + if !to_unassign.is_empty() { + let mut conn = get_pg_pool().get().await?; + diesel::update(dataset_groups_permissions::table) + .filter( + dataset_groups_permissions::dataset_group_id + .eq(dataset_group_id) + .and(dataset_groups_permissions::permission_id.eq_any( + to_unassign.iter().map(|a| a.id), + )) + .and(dataset_groups_permissions::permission_type.eq("user")), + ) + .set(dataset_groups_permissions::deleted_at.eq(chrono::Utc::now())) + .execute(&mut *conn) + .await?; + } + Ok::<_, anyhow::Error>(()) + }) + }; + + let (assign_result, unassign_result) = tokio::try_join!(assign_handle, unassign_handle)?; + assign_result?; + unassign_result?; + + Ok(()) +} \ No newline at end of file diff --git a/api/src/routes/rest/routes/dataset_groups/mod.rs b/api/src/routes/rest/routes/dataset_groups/mod.rs index f546f0da6..1b13db0ed 100644 --- a/api/src/routes/rest/routes/dataset_groups/mod.rs +++ b/api/src/routes/rest/routes/dataset_groups/mod.rs @@ -1,8 +1,9 @@ -pub mod delete_dataset_group; -pub mod get_dataset_group; -pub mod list_dataset_groups; -pub mod post_dataset_group; -pub mod put_dataset_group; +mod assets; +mod delete_dataset_group; +mod get_dataset_group; +mod list_dataset_groups; +mod post_dataset_group; +mod put_dataset_group; use axum::{ middleware, @@ -13,10 +14,8 @@ use axum::{ use crate::buster_middleware::auth::auth; use self::{ - delete_dataset_group::delete_dataset_group, - get_dataset_group::get_dataset_group, - list_dataset_groups::list_dataset_groups, - post_dataset_group::post_dataset_group, + delete_dataset_group::delete_dataset_group, get_dataset_group::get_dataset_group, + list_dataset_groups::list_dataset_groups, post_dataset_group::post_dataset_group, put_dataset_group::put_dataset_group, }; @@ -27,5 +26,6 @@ pub fn router() -> Router { .route("/:dataset_group_id", get(get_dataset_group)) .route("/:dataset_group_id", delete(delete_dataset_group)) .route("/", put(put_dataset_group)) + .nest("/:dataset_group_id", assets::router()) .route_layer(middleware::from_fn(auth)) -} \ No newline at end of file +}