diff --git a/Cargo.lock b/Cargo.lock index 7017df0..5253f1f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,7 +61,6 @@ dependencies = [ "tokio", "utoipa", "utoipa-axum", - "utoipa-redoc", "utoipa-swagger-ui", ] @@ -3590,18 +3589,6 @@ dependencies = [ "syn 2.0.104", ] -[[package]] -name = "utoipa-redoc" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6427547f6db7ec006cbbef95f7565952a16f362e298b416d2d497d9706fef72d" -dependencies = [ - "axum", - "serde", - "serde_json", - "utoipa", -] - [[package]] name = "utoipa-swagger-ui" version = "9.0.2" diff --git a/Cargo.toml b/Cargo.toml index 5bce6cf..6d82748 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,6 @@ tokio = { version = "1.46.1", features = [ "full" ] } utoipa = "5.4.0" utoipa-axum = "0.2.0" utoipa-swagger-ui = { version = "9", features = ["axum", "reqwest"] } -utoipa-redoc = { version = "6", features = ["axum"] } futures-util = "0.3.31" log = "0.4.27" pretty_env_logger = "0.5.0" diff --git a/src/main.rs b/src/main.rs index f927c1f..3aeb291 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,6 @@ use sea_orm::{ConnectionTrait, Database, DatabaseConnection, EntityTrait, Pagina use tokio::{sync::broadcast::{self, Sender}}; use utoipa::{openapi::{security::{HttpAuthScheme, HttpBuilder, SecurityScheme}, ContactBuilder, InfoBuilder, LicenseBuilder}, Modify, OpenApi}; use utoipa_axum::router::OpenApiRouter; -use utoipa_redoc::{Redoc, Servable}; use utoipa_swagger_ui::{Config, SwaggerUi}; use utoipa_axum::routes; @@ -160,6 +159,7 @@ async fn run_server(db: Arc) { // Book API .routes(routes!(routes::book::get_book_by_ean)) .routes(routes!(routes::book::get_book_by_id)) + .routes(routes!(routes::book::create_book)) // Book Instance API .routes(routes!(routes::book_instance::get_book_instance_by_id)) .routes(routes!(routes::book_instance::create_book_instance)) @@ -195,14 +195,14 @@ async fn run_server(db: Arc) { api.merge(ApiDoc::openapi()); - let redoc = Redoc::with_url("/docs/", api.clone()); - let swagger = SwaggerUi::new("/docs2/") - .url("/docs2/openapi.json", api) + let swagger = SwaggerUi::new("/docs/") + .url("/docs/openapi.json", api) .config(Config::default() .try_it_out_enabled(true) + .filter(true) + .display_request_duration(true) ); - let router = router.merge(redoc); let router = router.merge(swagger); let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); diff --git a/src/routes/book.rs b/src/routes/book.rs index 6b350c6..bff6126 100644 --- a/src/routes/book.rs +++ b/src/routes/book.rs @@ -2,7 +2,8 @@ use std::sync::Arc; use axum::{extract::{Path, State}, Json}; use reqwest::{StatusCode}; -use sea_orm::{ColumnTrait, EntityTrait, QueryFilter}; +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}; @@ -85,3 +86,49 @@ pub async fn get_book_by_ean( } } } + +#[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>, + Json(instance_payload): Json, +) -> (StatusCode, Json>) { + 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")))) + } +}