server: Leave households in a transaction if deleting fails

This commit is contained in:
traxys 2023-06-17 18:15:17 +02:00
parent c41b67b258
commit b354741245
2 changed files with 41 additions and 15 deletions

View file

@ -6,7 +6,7 @@ use axum::{
http::request::Parts, http::request::Parts,
Json, Json,
}; };
use sea_orm::{prelude::*, ActiveValue}; use sea_orm::{prelude::*, ActiveValue, DatabaseTransaction, TransactionTrait};
use sea_query::OnConflict; use sea_query::OnConflict;
use api::{ use api::{
@ -132,27 +132,44 @@ pub(super) async fn add_member(
Ok(Json(EmptyResponse {})) Ok(Json(EmptyResponse {}))
} }
async fn delete_household(
household: household::Model,
txn: &DatabaseTransaction,
) -> Result<(), RouteError> {
household.delete(txn).await?;
Ok(())
}
pub(super) async fn leave( pub(super) async fn leave(
AuthorizedHousehold(household): AuthorizedHousehold, AuthorizedHousehold(household): AuthorizedHousehold,
user: AuthenticatedUser, user: AuthenticatedUser,
state: State<AppState>, state: State<AppState>,
) -> super::JsonResult<EmptyResponse> { ) -> super::JsonResult<EmptyResponse> {
HouseholdMembers::delete_by_id((household.id, user.model.id)) state
.exec(&state.db) .db
.await?; .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) let Some(household) = Household::find_by_id(household.id)
.one(&state.db) .one(txn)
.await? else { .await? else {
return Ok(Json(EmptyResponse {})); return Ok(Json(EmptyResponse {}));
}; };
let member_count = household.find_related(User).count(&state.db).await?; let member_count = household.find_related(User).count(txn).await?;
if member_count == 0 { if member_count == 0 {
household.delete(&state.db).await?; delete_household(household, txn).await?;
} }
Ok(Json(EmptyResponse {})) Ok(Json(EmptyResponse {}))
})
})
.await
.map_err(Into::into)
} }
pub(super) async fn rename( pub(super) async fn rename(

View file

@ -15,7 +15,7 @@ use axum::{
Json, Router, TypedHeader, Json, Router, TypedHeader,
}; };
use jwt_simple::prelude::*; use jwt_simple::prelude::*;
use sea_orm::prelude::*; use sea_orm::{prelude::*, TransactionError};
use sha2::{Digest, Sha512}; use sha2::{Digest, Sha512};
use tower_http::cors::{self, AllowOrigin, CorsLayer}; use tower_http::cors::{self, AllowOrigin, CorsLayer};
@ -42,11 +42,20 @@ enum RouteError {
PathRejection(#[from] axum::extract::rejection::PathRejection), PathRejection(#[from] axum::extract::rejection::PathRejection),
#[error("The supplied ressource does not exist")] #[error("The supplied ressource does not exist")]
RessourceNotFound, RessourceNotFound,
#[error("Error in DB transaction")]
TxnError(#[from] TransactionError<Box<RouteError>>),
}
impl From<DbErr> for Box<RouteError> {
fn from(value: DbErr) -> Self {
Box::new(value.into())
}
} }
impl IntoResponse for RouteError { impl IntoResponse for RouteError {
fn into_response(self) -> axum::response::Response { fn into_response(self) -> axum::response::Response {
match self { match self {
RouteError::TxnError(TransactionError::Transaction(e)) => e.into_response(),
RouteError::UnknownAccount => { RouteError::UnknownAccount => {
(StatusCode::NOT_FOUND, "Account not found").into_response() (StatusCode::NOT_FOUND, "Account not found").into_response()
} }