134 lines
5 KiB
Rust
134 lines
5 KiB
Rust
use std::sync::Arc;
|
|
|
|
use axum::{extract::{Path, State}, Json};
|
|
use reqwest::{StatusCode};
|
|
use sea_orm::{ActiveModelTrait, ActiveValue::{NotSet, Set}, ColumnTrait, EntityTrait, QueryFilter, TryIntoModel};
|
|
use serde::{Deserialize, Serialize};
|
|
use utoipa::IntoParams;
|
|
|
|
use crate::{entities::{book, prelude::{Book}}, utils::open_library};
|
|
|
|
use crate::AppState;
|
|
|
|
|
|
#[derive(IntoParams)]
|
|
#[into_params(names("id"), parameter_in = Path)]
|
|
#[allow(dead_code)]
|
|
struct BookByIdParams(u32);
|
|
|
|
#[axum::debug_handler]
|
|
#[utoipa::path(
|
|
get,
|
|
path = "/book/id/{id}",
|
|
params(BookByIdParams),
|
|
security(("jwt" = [])),
|
|
responses(
|
|
(status = OK, body = book::Model, description = "Found book with corresponding ID in the database", examples(
|
|
("Found regular book" = (value = json!({"author": "Pierre Bottero", "ean": "9782700234015", "id": 5642, "title": "Ellana l'envol"}))),
|
|
("Book doesn't have an EAN" = (value = json!({"author": "Author B. Ook", "ean": "", "id": 1465312, "title": "Itsabook"})))
|
|
)),
|
|
(status = NOT_FOUND, description = "No book with this id exists in the database")
|
|
),
|
|
summary = "Get a book by its internal ID",
|
|
description = "Get a book from its ID in Alexandria's internal database",
|
|
tag = "book-api",
|
|
)]
|
|
pub async fn get_book_by_id(
|
|
State(state): State<Arc<AppState>>,
|
|
Path(id): Path<u32>,
|
|
) -> (StatusCode, Json<Option<book::Model>>) {
|
|
if let Ok(Some(res)) = Book::find_by_id(id).one(state.db_conn.as_ref()).await {
|
|
(StatusCode::OK, Json(Some(res)))
|
|
} else {
|
|
(StatusCode::NOT_FOUND, Json(None))
|
|
}
|
|
}
|
|
|
|
#[derive(IntoParams)]
|
|
#[into_params(names("ean"), parameter_in = Path)]
|
|
#[allow(dead_code)]
|
|
struct BookByEanParams(String);
|
|
|
|
#[axum::debug_handler]
|
|
#[utoipa::path(
|
|
get,
|
|
path = "/book/ean/{ean}",
|
|
params(BookByEanParams),
|
|
security(("jwt" = [])),
|
|
responses(
|
|
(status = OK, body = book::Model, description = "Found book with corresponding EAN", examples(
|
|
("Found regular book" = (value = json!({"author": "Pierre Bottero", "ean": "9782700234015", "id": 5642, "title": "Ellana l'envol"})))
|
|
)),
|
|
(status = NOT_FOUND, description = "No book with this EAN found in the database or on openlibrary")
|
|
),
|
|
summary = "Get a book by its EAN",
|
|
description = "Get a book from its EAN. If it doesn't exist in its database, Alexandria will try to find it using openlibrary.org's API",
|
|
tag = "book-api"
|
|
)]
|
|
pub async fn get_book_by_ean(
|
|
State(state): State<Arc<AppState>>,
|
|
Path(ean): Path<String>,
|
|
) -> (StatusCode, Json<Option<book::Model>>) {
|
|
if let Ok(Some(res)) = Book::find().filter(book::Column::Ean.eq(&ean)).one(state.db_conn.as_ref()).await {
|
|
(StatusCode::OK, Json(Some(res)))
|
|
} else {
|
|
let fetched_book = open_library::fetch_book_by_ean(&state.web_client, &ean).await;
|
|
if let Some(book) = fetched_book {
|
|
let res = Book::insert(book.to_active_model()).exec(state.db_conn.as_ref()).await.unwrap();
|
|
(StatusCode::OK, Json(Some(book::Model {
|
|
id: res.last_insert_id,
|
|
ean: ean,
|
|
title: book.title,
|
|
author: book.author
|
|
})))
|
|
} else {
|
|
(StatusCode::NOT_FOUND, Json(None))
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Serialize, utoipa::ToSchema)]
|
|
pub struct BookCreateParams {
|
|
ean: String,
|
|
title: String,
|
|
author: String
|
|
}
|
|
|
|
#[axum::debug_handler]
|
|
#[utoipa::path(
|
|
post,
|
|
path = "/book",
|
|
request_body = BookCreateParams,
|
|
security(("jwt" = [])),
|
|
responses(
|
|
(status = OK, 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",
|
|
description = "Should be used when needing to insert data for an EAN that doesn't already exist in the database and couldn't be fetched automatically, ie the /book/ean/{ean} endpoint returned a 404 NOT FOUND error",
|
|
tag = "book-api",
|
|
)]
|
|
pub async fn create_book(
|
|
State(state): State<Arc<AppState>>,
|
|
Json(instance_payload): Json<BookCreateParams>,
|
|
) -> (StatusCode, Json<Option<book::Model>>) {
|
|
if let Some(book) = Book::find().filter(book::Column::Ean.eq(&instance_payload.ean)).one(state.db_conn.as_ref()).await.unwrap() {
|
|
return (StatusCode::CONFLICT, Json(Some(book)));
|
|
}
|
|
|
|
let book = book::ActiveModel {
|
|
ean: Set(instance_payload.ean),
|
|
title: Set(instance_payload.title),
|
|
author: Set(instance_payload.author),
|
|
id: NotSet
|
|
};
|
|
|
|
let b = book.save(state.db_conn.as_ref()).await;
|
|
match b {
|
|
Err(e) => {
|
|
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"))))
|
|
}
|
|
}
|