diff --git a/crates/sql-backend-handler/src/sql_user_backend_handler.rs b/crates/sql-backend-handler/src/sql_user_backend_handler.rs index db495a8..c34e12a 100644 --- a/crates/sql-backend-handler/src/sql_user_backend_handler.rs +++ b/crates/sql-backend-handler/src/sql_user_backend_handler.rs @@ -395,24 +395,70 @@ impl UserBackendHandler for SqlBackendHandler { #[instrument(skip_all, level = "debug", err, fields(user_id = ?user_id.as_str(), group_id))] async fn add_user_to_group(&self, user_id: &UserId, group_id: GroupId) -> Result<()> { - let new_membership = model::memberships::ActiveModel { - user_id: ActiveValue::Set(user_id.clone()), - group_id: ActiveValue::Set(group_id), - }; - new_membership.insert(&self.sql_pool).await?; + let user_id_owned = user_id.clone(); + self.sql_pool + .transaction::<_, _, sea_orm::DbErr>(|transaction| { + Box::pin(async move { + let new_membership = model::memberships::ActiveModel { + user_id: ActiveValue::Set(user_id_owned), + group_id: ActiveValue::Set(group_id), + }; + new_membership.insert(transaction).await?; + + // Update group modification time + let now = chrono::Utc::now().naive_utc(); + let update_group = model::groups::ActiveModel { + group_id: Set(group_id), + modified_date: Set(now), + ..Default::default() + }; + update_group.update(transaction).await?; + + Ok(()) + }) + }) + .await?; Ok(()) } #[instrument(skip_all, level = "debug", err, fields(user_id = ?user_id.as_str(), group_id))] async fn remove_user_from_group(&self, user_id: &UserId, group_id: GroupId) -> Result<()> { - let res = model::Membership::delete_by_id((user_id.clone(), group_id)) - .exec(&self.sql_pool) - .await?; - if res.rows_affected == 0 { - return Err(DomainError::EntityNotFound(format!( - "No such membership: '{user_id}' -> {group_id:?}" - ))); - } + let user_id_owned = user_id.clone(); + self.sql_pool + .transaction::<_, _, sea_orm::DbErr>(|transaction| { + Box::pin(async move { + let res = model::Membership::delete_by_id((user_id_owned.clone(), group_id)) + .exec(transaction) + .await?; + if res.rows_affected == 0 { + return Err(sea_orm::DbErr::Custom(format!( + "No such membership: '{user_id_owned}' -> {group_id:?}" + ))); + } + + // Update group modification time + let now = chrono::Utc::now().naive_utc(); + let update_group = model::groups::ActiveModel { + group_id: Set(group_id), + modified_date: Set(now), + ..Default::default() + }; + update_group.update(transaction).await?; + + Ok(()) + }) + }) + .await + .map_err(|e| match e { + sea_orm::TransactionError::Connection(sea_orm::DbErr::Custom(msg)) => { + DomainError::EntityNotFound(msg) + } + sea_orm::TransactionError::Transaction(sea_orm::DbErr::Custom(msg)) => { + DomainError::EntityNotFound(msg) + } + sea_orm::TransactionError::Connection(e) => DomainError::DatabaseError(e), + sea_orm::TransactionError::Transaction(e) => DomainError::DatabaseError(e), + })?; Ok(()) } }