diff --git a/backend/api/Cargo.toml b/backend/api/Cargo.toml index 874615b..bf9c08d 100644 --- a/backend/api/Cargo.toml +++ b/backend/api/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] booksman-orm = { path = "../orm" } +entity = { path = "../entity" } axum = "^0.5" axum-extra = { version = "^0.3", features = ["spa"] } clap = { version = "^3", features = ["derive"] } diff --git a/backend/api/src/lib.rs b/backend/api/src/lib.rs index 729938e..5f1f5e5 100644 --- a/backend/api/src/lib.rs +++ b/backend/api/src/lib.rs @@ -1,8 +1,9 @@ use axum::{ http::{HeaderValue, Method}, response::IntoResponse, - routing::get, + routing::{get,post}, Json, Router, + extract::{Extension, Form, Path, Query}, }; use axum_extra::routing::SpaRouter; use clap::Parser; @@ -13,6 +14,11 @@ use std::str::FromStr; use tower::ServiceBuilder; use tower_http::cors::CorsLayer; use tower_http::trace::TraceLayer; +use booksman_orm::{ + sea_orm::{Database, DatabaseConnection}, + Mutation as MutationCore, Query as QueryCore, +}; +use ::entity::entities::{book,book_author,book_person,book_place,book_subject,book_time,book_isbn}; #[derive(Deserialize, Serialize, Debug)] struct Docs { @@ -29,21 +35,7 @@ struct Docs { subject: Option>, time: Option>, } -/* -impl Docs { - fn set_cover_url(&mut self) { - match self.cover_i { - Some(cover_i) => { - self.cover_url = Some(format!( - "https://covers.openlibrary.org/b/id/{}-L.jpg", - (cover_i.unwrap()) - )) - } - None => (), - } - } -} -*/ + #[derive(Deserialize, Serialize, Debug)] struct Books { num_found: u32, @@ -99,7 +91,10 @@ pub async fn main() { tracing_subscriber::fmt::init(); let app = Router::new() - .route("/api/hello", get(hello)) + .route("/api/search_openlibrary", get(search_openlibrary)) + .route("/api/delete/:id", post(delete_book)) + .route("/api/list", post(list_book)) + .route("/api/create", post(create_book)) .merge(SpaRouter::new("/assets", opt.static_dir)) .layer(ServiceBuilder::new().layer(TraceLayer::new_for_http())) .layer( @@ -111,7 +106,7 @@ pub async fn main() { // or see this issue https://github.com/tokio-rs/axum/issues/849 CorsLayer::new() .allow_origin("http://localhost:8080".parse::().unwrap()) - .allow_methods([Method::GET]), + .allow_methods([Method::GET, Method::POST]), ); let sock_addr = SocketAddr::from(( @@ -127,7 +122,7 @@ pub async fn main() { .expect("Unable to start server"); } -async fn hello( +async fn search_openlibrary( axum::extract::Query(params): axum::extract::Query>, ) -> impl IntoResponse { print!("Get items with query params: {:?}\n", params); @@ -140,3 +135,49 @@ async fn hello( print!("Search token {:?}\n", search); return Json(resjson); } + + +async fn delete_book( + Extension(ref conn): Extension, + Path(id): Path, +) -> impl IntoResponse { + MutationCore::delete_book(conn, id) + .await + .expect("could not delete book"); + "success" +} + +async fn list_book( + Extension(ref conn): Extension, +) -> impl IntoResponse { + let books = QueryCore::find_books_in_page(conn,1,5) + .await + .expect("could not list books"); + "success" +} + + +async fn create_book( + Extension(ref conn): Extension, + doc_sent : Form, +) -> impl IntoResponse { + let book: book::Model = book::Model{ + open_library_key: "NONE".to_string(), + title: (doc_sent.title.to_owned()), + edition_count: 1, + first_publish_year: 1,//(doc_sent.first_publish_year.unwrap().to_owned()), + median_page_count: 1, //(doc_sent.number_of_pages_median.unwrap().to_owned()), + goodread_id: "NONE".to_string(), + description: "NONE".to_string(), + comments: "NONE".to_string(), + cover: "NONE".to_string(), + rating: 1, + time_added: "NONE".to_string(), + id: 1, + location: "NONE".to_string(), + }; + let created_book = MutationCore::create_book(conn, book) + .await + .expect("could not create book"); + "success" +} diff --git a/backend/orm/src/mutation.rs b/backend/orm/src/mutation.rs index d7bdf19..7c1653e 100644 --- a/backend/orm/src/mutation.rs +++ b/backend/orm/src/mutation.rs @@ -1,24 +1,114 @@ //use ::entity::entities::prelude::Book; //use ::entity::entities::{prelude::*, *}; +use ::entity::entities::{book::Entity as Book}; +//, book_author::Entity as Author, book_person::Entity as Person, book_place::Entity as Place, book_subject::Entity as Subject, book_time::Entity as Time, book_isbn::Entity as ISBN}; +use ::entity::entities::{book,book_author,book_person,book_place,book_subject,book_time,book_isbn}; use sea_orm::*; //use ::entity::entities::prelude::Book; pub struct Mutation; impl Mutation { -/* pub async fn create_post( + pub async fn create_book( db: &DbConn, - form_data: post::Model, - ) -> Result { - post::ActiveModel { + form_data: book::Model, + ) -> Result { + book::ActiveModel { + open_library_key: Set(form_data.open_library_key.to_owned()), title: Set(form_data.title.to_owned()), - text: Set(form_data.text.to_owned()), + edition_count: Set(form_data.edition_count.to_owned()), + first_publish_year: Set(form_data.first_publish_year.to_owned()), + median_page_count: Set(form_data.median_page_count.to_owned()), + goodread_id: Set(form_data.goodread_id.to_owned()), + description: Set(form_data.description.to_owned()), + cover: Set(form_data.cover.to_owned()), + location: Set(form_data.location.to_owned()), + time_added: Set(form_data.time_added.to_owned()), + rating: Set(form_data.rating.to_owned()), + comments: Set(form_data.comments.to_owned()), + ..Default::default() + } + .save(db) + .await + } + + pub async fn create_book_author( + db: &DbConn, + form_data: book_author::Model, + ) -> Result { + book_author::ActiveModel { + book_id: Set(form_data.book_id.to_owned()), + author_name: Set(form_data.author_name.to_owned()), ..Default::default() } .save(db) .await } + pub async fn create_book_person( + db: &DbConn, + form_data: book_person::Model, + ) -> Result { + book_person::ActiveModel { + book_id: Set(form_data.book_id.to_owned()), + person: Set(form_data.person.to_owned()), + ..Default::default() + } + .save(db) + .await + } + + pub async fn create_book_place( + db: &DbConn, + form_data: book_place::Model, + ) -> Result { + book_place::ActiveModel { + book_id: Set(form_data.book_id.to_owned()), + place: Set(form_data.place.to_owned()), + ..Default::default() + } + .save(db) + .await + } + pub async fn create_book_subject( + db: &DbConn, + form_data: book_subject::Model, + ) -> Result { + book_subject::ActiveModel { + book_id: Set(form_data.book_id.to_owned()), + subject: Set(form_data.subject.to_owned()), + ..Default::default() + } + .save(db) + .await + } + + pub async fn create_book_time( + db: &DbConn, + form_data: book_time::Model, + ) -> Result { + book_time::ActiveModel { + book_id: Set(form_data.book_id.to_owned()), + time: Set(form_data.time.to_owned()), + ..Default::default() + } + .save(db) + .await + } + + pub async fn create_book_isbn( + db: &DbConn, + form_data: book_isbn::Model, + ) -> Result { + book_isbn::ActiveModel { + book_id: Set(form_data.book_id.to_owned()), + isbn: Set(form_data.isbn.to_owned()), + ..Default::default() + } + .save(db) + .await + } + /* pub async fn update_post_by_id( db: &DbConn, id: i32, @@ -38,18 +128,18 @@ impl Mutation { .update(db) .await } - - pub async fn delete_post(db: &DbConn, id: i32) -> Result { - let post: post::ActiveModel = Post::find_by_id(id) +*/ + pub async fn delete_book(db: &DbConn, id: i32) -> Result { + let book: book::ActiveModel = Book::find_by_id(id) .one(db) .await? - .ok_or(DbErr::Custom("Cannot find post.".to_owned())) + .ok_or(DbErr::Custom("Cannot find book.".to_owned())) .map(Into::into)?; - post.delete(db).await + book.delete(db).await } - */ - /* pub async fn delete_all_books(db: &DbConn) -> Result { + + pub async fn delete_all_books(db: &DbConn) -> Result { Book::delete_many().exec(db).await - } */ + } } diff --git a/backend/orm/src/query.rs b/backend/orm/src/query.rs index 120d156..a647bcc 100644 --- a/backend/orm/src/query.rs +++ b/backend/orm/src/query.rs @@ -1,7 +1,32 @@ -use ::entity::entities::book::Entity as Book; -use ::entity::entities::book; +use ::entity::entities::{book::Entity as Book}; +//, book_author::Entity as Author, book_person::Entity as Person, book_place::Entity as Place, book_subject::Entity as Subject, book_time::Entity as Time, book_isbn::Entity as ISBN}; +use ::entity::entities::{book,book_author,book_person,book_place,book_subject,book_time,book_isbn}; //use ::entity::entities::{prelude::*, *}; use sea_orm::*; +//use sea_query::Expr; + +#[derive(FromQueryResult)] +pub struct BookAndMeta { + pub id: i32, + pub open_library_key: String, + pub title: String, + pub edition_count: i32, + pub first_publish_year: i32, + pub median_page_count: i32, + pub goodread_id: String, + pub description: String, + pub cover: String, + pub location: String, + pub time_added: String, + pub rating: i32, + pub comments: String, + pub author_name: String, + pub person: String, + pub place: String, + pub subject: String, + pub time: String, + pub isbn: String, +} //use ::entity::entities::prelude::Book; pub struct Query; @@ -11,8 +36,16 @@ impl Query { Book::find_by_id(id).one(db).await } +/* pub async fn find_bookplusmeta_by_id(db: &DbConn, id: i32) -> Result, Vec)>, DbErr> { + let book = Book::find_by_id(id).one(db).await?.unwrap(); + let authors = book.find_related(Author).all(db).await?; + let persons = book.find_related(Person).all(db).await?; + Ok(Some((book, authors, persons))) + } + */ + /// If ok, returns (post models, num pages). - pub async fn find_posts_in_page( + pub async fn find_books_in_page( db: &DbConn, page: usize, posts_per_page: usize, @@ -26,5 +59,34 @@ impl Query { // Fetch paginated posts paginator.fetch_page(page - 1).await.map(|p| (p, num_pages)) } + +pub async fn find_books_plus_meta_in_page( + db: &DbConn, + page: usize, + posts_per_page: usize, + ) -> Result<(Vec, usize), DbErr> { + // Setup paginator + let books = Self::find_books_in_page(db,page,posts_per_page).await?; + let book_ids: Vec = books.0.clone().into_iter().map(|b| b.id).collect(); + /* let paginator1 = Book::find().filter( + Condition::any() + .add(book::Column::Id.is_in(book_ids)) + ).find_with_related(Author).all(db).await?;*/ + let paginator2 = Book::find() + .filter(Condition::any() + .add(book::Column::Id.is_in(book_ids))) + .join(JoinType::LeftJoin, book_author::Relation::Book.def()) + .join(JoinType::LeftJoin, book_person::Relation::Book.def()) + .join(JoinType::LeftJoin, book_place::Relation::Book.def()) + .join(JoinType::LeftJoin, book_subject::Relation::Book.def()) + .join(JoinType::LeftJoin, book_time::Relation::Book.def()) + .join(JoinType::LeftJoin, book_isbn::Relation::Book.def()) + .column_as(book::Column::Id, "id") + .into_model::() + .all(db).await?; + + Ok((paginator2,books.1)) + } + }