diff --git a/Cargo.lock b/Cargo.lock index 5253f1f..7017df0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,6 +61,7 @@ dependencies = [ "tokio", "utoipa", "utoipa-axum", + "utoipa-redoc", "utoipa-swagger-ui", ] @@ -3589,6 +3590,18 @@ 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 6d82748..5bce6cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ 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 3aeb291..f927c1f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ 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; @@ -159,7 +160,6 @@ 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 swagger = SwaggerUi::new("/docs/") - .url("/docs/openapi.json", api) + let redoc = Redoc::with_url("/docs/", api.clone()); + let swagger = SwaggerUi::new("/docs2/") + .url("/docs2/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 bff6126..6b350c6 100644 --- a/src/routes/book.rs +++ b/src/routes/book.rs @@ -2,8 +2,7 @@ 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 sea_orm::{ColumnTrait, EntityTrait, QueryFilter}; use utoipa::IntoParams; use crate::{entities::{book, prelude::{Book}}, utils::open_library}; @@ -86,49 +85,3 @@ 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")))) - } -}