From fc494d213589ded8ebc38962aec4f0826de8a087 Mon Sep 17 00:00:00 2001 From: Ninjdai Date: Mon, 4 Aug 2025 13:16:24 +0200 Subject: [PATCH] feat: get bal owner book instances endpoint --- src/main.rs | 1 + src/routes/bal.rs | 4 +-- src/routes/book.rs | 4 +-- src/routes/book_instance.rs | 51 ++++++++++++++++++++++++++++++++----- src/routes/owner.rs | 4 +-- 5 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/main.rs b/src/main.rs index 34ea4f4..f91ebb8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -170,6 +170,7 @@ async fn run_server(db: Arc) { .routes(routes!(routes::book_instance::update_book_instance)) .routes(routes!(routes::book_instance::sell_book_instance)) .routes(routes!(routes::book_instance::bulk_create_book_instance)) + .routes(routes!(routes::book_instance::get_bal_owner_book_instances)) // Owner API .routes(routes!(routes::owner::get_owner_by_id)) .routes(routes!(routes::owner::create_owner)) diff --git a/src/routes/bal.rs b/src/routes/bal.rs index ec3a43b..f7691f7 100644 --- a/src/routes/bal.rs +++ b/src/routes/bal.rs @@ -57,7 +57,7 @@ pub struct BalCreateParams { request_body = BalCreateParams, security(("jwt" = [])), responses( - (status = OK, body = bal::Model, description = "Successfully created BAL"), + (status = CREATED, body = bal::Model, description = "Successfully created BAL"), ), summary = "Create a new bal", description = "Create a new bal", @@ -80,7 +80,7 @@ pub async fn create_bal( log::error!(target: "api", "Error while inserting new bal: {:#?}", e); (StatusCode::BAD_REQUEST, Json(None)) }, - Ok(res) => (StatusCode::OK, Json(Some(res.try_into_model().expect("All fields should be set once the bal is saved")))) + Ok(res) => (StatusCode::CREATED, Json(Some(res.try_into_model().expect("All fields should be set once the bal is saved")))) } } diff --git a/src/routes/book.rs b/src/routes/book.rs index bff6126..eb43a97 100644 --- a/src/routes/book.rs +++ b/src/routes/book.rs @@ -101,7 +101,7 @@ pub struct BookCreateParams { request_body = BookCreateParams, security(("jwt" = [])), responses( - (status = OK, body = book::Model, description = "Successfully saved book data"), + (status = CREATED, body = book::Model, description = "Successfully saved book data"), (status = CONFLICT, body = book::Model, description = "A book with the same EAN already exists. Replies with the data of the already saved book."), ), summary = "Manually add book data to the database", @@ -129,6 +129,6 @@ pub async fn create_book( log::error!(target: "api", "Error while inserting new book: {:#?}", e); (StatusCode::BAD_REQUEST, Json(None)) }, - Ok(res) => (StatusCode::OK, Json(Some(res.try_into_model().expect("All fields should be set once the book is saved")))) + Ok(res) => (StatusCode::CREATED, Json(Some(res.try_into_model().expect("All fields should be set once the book is saved")))) } } diff --git a/src/routes/book_instance.rs b/src/routes/book_instance.rs index ef94481..93b740b 100644 --- a/src/routes/book_instance.rs +++ b/src/routes/book_instance.rs @@ -2,11 +2,11 @@ use std::sync::Arc; use axum::{extract::{Path, State}, Json}; use reqwest::{StatusCode}; -use sea_orm::{ActiveModelTrait, ActiveValue::{NotSet, Set}, EntityTrait, TryIntoModel}; +use sea_orm::{ActiveModelTrait, ActiveValue::{NotSet, Set}, ColumnTrait, EntityTrait, QueryFilter, TryIntoModel}; use serde::{Deserialize, Serialize}; use utoipa::IntoParams; -use crate::{entities::{book_instance, prelude::*}, routes::auth::Claims, utils::auth::{user_is_bal_owner, user_is_book_instance_owner}, AppState}; +use crate::{entities::{book_instance, prelude::*}, routes::auth::Claims, utils::auth::{user_is_bal_owner, user_is_book_instance_owner, user_is_owner_owner}, AppState}; #[derive(IntoParams)] @@ -59,7 +59,7 @@ pub struct BookInstanceCreateParams { request_body = BookInstanceCreateParams, security(("jwt" = [])), responses( - (status = OK, body = book_instance::Model, description = "Successfully created book instance"), + (status = CREATED, body = book_instance::Model, description = "Successfully created book instance"), (status = FORBIDDEN, description = "You don't own the specified book instance"), ), summary = "Create a new book instance", @@ -91,7 +91,7 @@ pub async fn create_book_instance( log::error!(target: "api", "Error while inserting new book instance: {:#?}", e); (StatusCode::BAD_REQUEST, Json(None)) }, - Ok(res) => (StatusCode::OK, Json(Some(res.try_into_model().expect("All fields should be set once the book instance is saved")))) + Ok(res) => (StatusCode::CREATED, Json(Some(res.try_into_model().expect("All fields should be set once the book instance is saved")))) } } @@ -216,7 +216,7 @@ pub async fn sell_book_instance( request_body = Vec, security(("jwt" = [])), responses( - (status = OK, description = "Successfully created book instances"), + (status = CREATED, description = "Successfully created book instances"), (status = FORBIDDEN, description = "You don't own at least one specified bal of the book instances sent"), ), summary = "Create new book instances in bulk", @@ -253,6 +253,45 @@ pub async fn bulk_create_book_instance( log::error!(target: "api", "Error while bulk inserting new book instances: {:#?}", e); StatusCode::INTERNAL_SERVER_ERROR }, - Ok(_) => StatusCode::OK + Ok(_) => StatusCode::CREATED + } +} + +#[derive(IntoParams)] +#[into_params(names("bal_id", "owner_id"), parameter_in = Path)] +#[allow(dead_code)] +struct BalOwnerByIdParams(u32, u32); + +#[axum::debug_handler] +#[utoipa::path( + get, + path = "/bal/{bal_id}/owner/{owner_id}/book_instances", + params(BalOwnerByIdParams), + security(("jwt" = [])), + responses( + (status = OK, body = Vec, description = "Found book instances in the database"), + (status = FORBIDDEN, description = "You do not own the specified owner"), + ), + summary = "Get books instances from an owner in a bal", + description = "Lists all book instances an owner has in a bal. WARNING: If the bal or owner don't exist, the endpoint will return 200/OK with an empty list", + tag = "book-instance-api", +)] +pub async fn get_bal_owner_book_instances( + State(state): State>, + claims: Claims, + Path((bal_id, owner_id)): Path<(u32, u32)>, +) -> (StatusCode, Json>) { + if !user_is_owner_owner(claims.user_id, owner_id, state.db_conn.as_ref()).await { + return (StatusCode::FORBIDDEN, Json(vec![])); + }// If a user owns an owner, it will own the bal the owner has books in, + // so checking for bal ownership is unnecessary + if let Ok(res) = BookInstance::find() + .filter(book_instance::Column::BalId.eq(bal_id)) + .filter(book_instance::Column::OwnerId.eq(owner_id)) + .all(state.db_conn.as_ref()).await + { + (StatusCode::OK, Json(res)) + } else { + (StatusCode::INTERNAL_SERVER_ERROR, Json(vec![])) } } diff --git a/src/routes/owner.rs b/src/routes/owner.rs index d941c7e..2957af1 100644 --- a/src/routes/owner.rs +++ b/src/routes/owner.rs @@ -59,7 +59,7 @@ pub struct OwnerCreateParams { request_body = OwnerCreateParams, security(("jwt" = [])), responses( - (status = OK, body = owner::Model, description = "Successfully created owner"), + (status = CREATED, body = owner::Model, description = "Successfully created owner"), ), summary = "Create a new owner", description = "Create a new owner", @@ -88,7 +88,7 @@ pub async fn create_owner( Ok(res) => { let model = res.try_into_model().expect("All fields should be set once the owner is saved"); let _ = state.event_bus.send(Event::WebsocketBroadcast(WebsocketMessage::NewOwner(Arc::new(model.clone())))); - (StatusCode::OK, Json(Some(model))) + (StatusCode::CREATED, Json(Some(model))) } } }