feat: book instance search API endpoint

This commit is contained in:
Ninjdai 2025-08-08 18:58:04 +02:00
parent 44be2c83ba
commit 5d4ece3c34
3 changed files with 62 additions and 3 deletions

View file

@ -184,6 +184,7 @@ async fn run_server(db: Arc<DatabaseConnection>, port: u16) {
.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))
.routes(routes!(routes::book_instance::search_bal_book_instances))
// Owner API
.routes(routes!(routes::owner::get_owner_by_id))
.routes(routes!(routes::owner::create_owner))
@ -230,6 +231,7 @@ async fn run_server(db: Arc<DatabaseConnection>, port: u16) {
.try_it_out_enabled(true)
.filter(true)
.display_request_duration(true)
.persist_authorization(true)
);
let router = router.merge(swagger);

View file

@ -12,7 +12,7 @@ use crate::{entities::{bal, prelude::*}, routes::auth::Claims, AppState};
#[derive(IntoParams)]
#[into_params(names("id"), parameter_in = Path)]
#[allow(dead_code)]
struct BalByIdParams(u32);
pub struct BalByIdParams(u32);
#[axum::debug_handler]
#[utoipa::path(

View file

@ -1,4 +1,4 @@
use std::sync::Arc;
use std::{collections::HashMap, sync::Arc};
use axum::{extract::{Path, State}, Json};
use reqwest::{StatusCode};
@ -6,7 +6,7 @@ use sea_orm::{ActiveModelTrait, ActiveValue::{NotSet, Set}, ColumnTrait, EntityT
use serde::{Deserialize, Serialize};
use utoipa::IntoParams;
use crate::{entities::{book, book_instance, prelude::*}, routes::auth::Claims, utils::auth::{user_is_bal_owner, user_is_book_instance_owner, user_is_owner_owner}, AppState};
use crate::{entities::{book, book_instance, prelude::*}, routes::{auth::Claims, bal}, utils::auth::{user_is_bal_owner, user_is_book_instance_owner, user_is_owner_owner}, AppState};
#[derive(IntoParams)]
@ -329,3 +329,60 @@ pub async fn get_bal_book_instances_by_ean(
(StatusCode::INTERNAL_SERVER_ERROR, Json(vec![]))
}
}
#[derive(Deserialize, Serialize, utoipa::ToSchema)]
pub struct BookInstanceSearchParams {
title: String,
}
#[derive(Serialize, utoipa::ToSchema)]
pub struct BookInstanceSearchResults {
book_instances: Vec<book_instance::Model>,
books: HashMap<u32, book::Model>,
}
#[axum::debug_handler]
#[utoipa::path(
post,
path = "/bal/{id}/search",
params(bal::BalByIdParams),
request_body = BookInstanceSearchParams,
security(("jwt" = [])),
responses(
(status = OK, body = BookInstanceSearchResults, description = "Found book instances in the database"),
(status = FORBIDDEN, description = "You do not own the specified bal"),
),
summary = "Search a BAL for books instances",
description = "Lists all book instances that match the requested parameters in a bal",
tag = "book-instance-api",
)]
pub async fn search_bal_book_instances(
State(state): State<Arc<AppState>>,
claims: Claims,
Path(bal_id): Path<u32>,
Json(instance_payload): Json<BookInstanceSearchParams>,
) -> (StatusCode, Json<Option<BookInstanceSearchResults>>) {
if !user_is_bal_owner(claims.user_id, bal_id, state.db_conn.as_ref()).await {
return (StatusCode::FORBIDDEN, Json(None));
}
if let Ok(res) = BookInstance::find()
.filter(book_instance::Column::BalId.eq(bal_id))
.filter(book_instance::Column::Available.eq(true))
.join(JoinType::InnerJoin, book_instance::Relation::Book.def())
.filter(book::Column::Title.like(format!("%{}%", instance_payload.title)))
.all(state.db_conn.as_ref()).await
{
let mut book_id_map = HashMap::new();
for instance in &res {
if book_id_map.get(&instance.book_id).is_none() {
book_id_map.insert(instance.book_id, Book::find_by_id(instance.book_id).one(state.db_conn.as_ref()).await.unwrap().unwrap());
}
}
(StatusCode::OK, Json(Some(BookInstanceSearchResults {
book_instances: res,
books: book_id_map
})))
} else {
(StatusCode::INTERNAL_SERVER_ERROR, Json(None))
}
}