diff --git a/src/routes/household.rs b/src/routes/household.rs index 1106471..4ea4a7b 100644 --- a/src/routes/household.rs +++ b/src/routes/household.rs @@ -6,7 +6,7 @@ use axum::{ http::request::Parts, Json, }; -use sea_orm::{prelude::*, ActiveValue}; +use sea_orm::{prelude::*, ActiveValue, DatabaseTransaction, TransactionTrait}; use sea_query::OnConflict; use api::{ @@ -132,27 +132,44 @@ pub(super) async fn add_member( Ok(Json(EmptyResponse {})) } +async fn delete_household( + household: household::Model, + txn: &DatabaseTransaction, +) -> Result<(), RouteError> { + household.delete(txn).await?; + + Ok(()) +} + pub(super) async fn leave( AuthorizedHousehold(household): AuthorizedHousehold, user: AuthenticatedUser, state: State, ) -> super::JsonResult { - HouseholdMembers::delete_by_id((household.id, user.model.id)) - .exec(&state.db) - .await?; + state + .db + .transaction(|txn| { + Box::pin(async move { + HouseholdMembers::delete_by_id((household.id, user.model.id)) + .exec(txn) + .await?; - let Some(household) = Household::find_by_id(household.id) - .one(&state.db) - .await? else { - return Ok(Json(EmptyResponse {})); - }; + let Some(household) = Household::find_by_id(household.id) + .one(txn) + .await? else { + return Ok(Json(EmptyResponse {})); + }; - let member_count = household.find_related(User).count(&state.db).await?; - if member_count == 0 { - household.delete(&state.db).await?; - } + let member_count = household.find_related(User).count(txn).await?; + if member_count == 0 { + delete_household(household, txn).await?; + } - Ok(Json(EmptyResponse {})) + Ok(Json(EmptyResponse {})) + }) + }) + .await + .map_err(Into::into) } pub(super) async fn rename( diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 898b37e..a56620f 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -15,7 +15,7 @@ use axum::{ Json, Router, TypedHeader, }; use jwt_simple::prelude::*; -use sea_orm::prelude::*; +use sea_orm::{prelude::*, TransactionError}; use sha2::{Digest, Sha512}; use tower_http::cors::{self, AllowOrigin, CorsLayer}; @@ -42,11 +42,20 @@ enum RouteError { PathRejection(#[from] axum::extract::rejection::PathRejection), #[error("The supplied ressource does not exist")] RessourceNotFound, + #[error("Error in DB transaction")] + TxnError(#[from] TransactionError>), +} + +impl From for Box { + fn from(value: DbErr) -> Self { + Box::new(value.into()) + } } impl IntoResponse for RouteError { fn into_response(self) -> axum::response::Response { match self { + RouteError::TxnError(TransactionError::Transaction(e)) => e.into_response(), RouteError::UnknownAccount => { (StatusCode::NOT_FOUND, "Account not found").into_response() }