From bc7feb09d41ce29f715a822e51998d2cfdd4ea27 Mon Sep 17 00:00:00 2001 From: Ninjdai Date: Fri, 15 Aug 2025 13:46:21 +0200 Subject: [PATCH] feat: bulk book instance sell --- src/lib.rs | 1 + src/routes/book_instance.rs | 53 +++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 8a0d8b8..3f2750f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -157,6 +157,7 @@ pub async fn run_server(db: Arc, port: u16, serve_docs: bool .routes(routes!(routes::book_instance::create_book_instance)) .routes(routes!(routes::book_instance::update_book_instance)) .routes(routes!(routes::book_instance::sell_book_instance)) + .routes(routes!(routes::book_instance::bulk_sell_book_instance)) .routes(routes!(routes::book_instance::bulk_create_book_instance)) .routes(routes!(routes::book_instance::get_bal_owner_book_instances)) .routes(routes!(routes::book_instance::get_bal_book_instances_by_ean)) diff --git a/src/routes/book_instance.rs b/src/routes/book_instance.rs index 3fc3e9b..91ea012 100644 --- a/src/routes/book_instance.rs +++ b/src/routes/book_instance.rs @@ -167,6 +167,7 @@ pub struct BookInstanceSaleParams { security(("jwt" = [])), responses( (status = OK, body = book_instance::Model, description = "Successfully sold book instance"), + (status = CONFLICT, description = "The specified book instance is not available"), (status = NOT_FOUND, description = "No book instance with specified id was found"), (status = FORBIDDEN, description = "You don't own the specified book instance"), ), @@ -185,6 +186,9 @@ pub async fn sell_book_instance( } if let Ok(Some(book_instance)) = BookInstance::find_by_id(id).one(state.db_conn.as_ref()).await { + if !book_instance.available { + return (StatusCode::CONFLICT, Json(None)); + } let mut book_instance: book_instance::ActiveModel = book_instance.into(); book_instance.sold_price = Set(Some(instance_payload.price)); book_instance.available = Set(false); @@ -204,6 +208,55 @@ pub async fn sell_book_instance( } } +#[axum::debug_handler] +#[utoipa::path( + post, + path = "/book_instance/sell/bulk", + request_body = HashMap, + security(("jwt" = [])), + responses( + (status = OK, description = "Successfully sold book instances"), + (status = CONFLICT, description = "One of the specified book instances is not available"), + (status = NOT_FOUND, description = "One of the specified book instances was not found"), + (status = FORBIDDEN, description = "You don't own one of the specified book instance"), + ), + summary = "Sell book instances in bulk", + description = "Sell book instances in bulk. Payload: {book_instance_id: sold_price, ...}", + tag = "book-instance-api", +)] +pub async fn bulk_sell_book_instance( + State(state): State>, + claims: Claims, + Json(payload): Json>, +) -> StatusCode { + // Here, we trust the client inputs valid book instances most of the time, so we already + // allocate vector memory the size of the input map + let mut book_instance_list: Vec<(book_instance::Model, f32)> = Vec::with_capacity(payload.len()); + + for instance in payload { + if let Ok(Some(book_instance)) = BookInstance::find_by_id(instance.0).one(state.db_conn.as_ref()).await { + if !user_is_bal_owner(claims.user_id, book_instance.bal_id, state.db_conn.as_ref()).await { + return StatusCode::FORBIDDEN + } else if !book_instance.available { + return StatusCode::CONFLICT + } else { + book_instance_list.push((book_instance, instance.1)); + } + } else { + return StatusCode::NOT_FOUND + } + } + + for instance in book_instance_list { + let mut book_instance: book_instance::ActiveModel = instance.0.into(); + book_instance.sold_price = Set(Some(instance.1)); + book_instance.available = Set(false); + + let _ = book_instance.update(state.db_conn.as_ref()).await.unwrap(); + } + StatusCode::OK +} + #[axum::debug_handler] #[utoipa::path( post,