diff --git a/src/forms/mod.rs b/src/forms/mod.rs new file mode 100644 index 0000000..7a10a3e --- /dev/null +++ b/src/forms/mod.rs @@ -0,0 +1,3 @@ +mod rating; + +pub use rating::*; diff --git a/src/forms/rating.rs b/src/forms/rating.rs new file mode 100644 index 0000000..76efca4 --- /dev/null +++ b/src/forms/rating.rs @@ -0,0 +1,14 @@ +use crate::models; +use serde::{Deserialize, Serialize}; +use serde_valid::Validate; + +#[derive(Serialize, Deserialize, Debug, Validate)] +pub struct Rating { + pub obj_id: i32, // product external id + pub category: models::RateCategory, // rating of product | rating of service etc + #[validate(max_length = 1000)] + pub comment: Option, // always linked to a product + #[validate(minimum = 0)] + #[validate(maximum = 10)] + pub rate: i32, // +} diff --git a/src/lib.rs b/src/lib.rs index 911ec72..9d3cc9b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,8 @@ -pub mod routes; -pub mod startup; pub mod configuration; -pub mod telemetry; +pub mod forms; mod middleware; -mod models; +pub mod models; +pub mod routes; pub mod services; +pub mod startup; +pub mod telemetry; diff --git a/src/models/mod.rs b/src/models/mod.rs index 7595e12..c3cbfed 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,3 +1,5 @@ pub mod rating; +pub mod stack; pub mod user; -pub mod stack; \ No newline at end of file + +pub use rating::*; diff --git a/src/models/rating.rs b/src/models/rating.rs index d9a7a31..b1242dd 100644 --- a/src/models/rating.rs +++ b/src/models/rating.rs @@ -1,6 +1,6 @@ -use uuid::Uuid; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; +use uuid::Uuid; pub struct Product { // Product - is an external object that we want to store in the database, @@ -10,40 +10,37 @@ pub struct Product { // rating - is a rating of the product // product type stack & app, // id is generated based on the product type and external obj_id - pub id: i32, //primary key, for better data management - pub obj_id: u32, // external product ID db, no autoincrement, example: 100 - pub obj_type: String, // stack | app, unique index - pub rating: Rating, // 0-10 - // pub rules: Rules, + pub id: i32, //primary key, for better data management + pub obj_id: i32, // external product ID db, no autoincrement, example: 100 + pub obj_type: String, // stack | app, unique index pub created_at: DateTime, pub updated_at: DateTime, } pub struct Rating { pub id: i32, - pub user_id: Uuid, // external user_id, 100, taken using token (middleware?) - pub category: String, // rating of product | rating of service etc - pub comment: String, // always linked to a product - pub hidden: bool, // rating can be hidden for non-adequate user behaviour + pub user_id: Uuid, // external user_id, 100, taken using token (middleware?) + pub product_id: i32, //primary key, for better data management + pub category: String, // rating of product | rating of service etc + pub comment: String, // always linked to a product + pub hidden: bool, // rating can be hidden for non-adequate user behaviour pub rate: u32, pub created_at: DateTime, pub updated_at: DateTime, } - -#[derive(sqlx::Type)] -#[sqlx(rename_all = "lowercase", type_name = "category")] -#[derive(Serialize, Deserialize, Debug, Clone, Copy)] +#[derive(sqlx::Type, Serialize, Deserialize, Debug, Clone, Copy)] +#[sqlx(rename_all = "lowercase", type_name = "varchar")] pub enum RateCategory { - Application, // app, feature, extension - Cloud, // is user satisfied working with this cloud - Stack, // app stack + Application, // app, feature, extension + Cloud, // is user satisfied working with this cloud + Stack, // app stack DeploymentSpeed, Documentation, Design, TechSupport, Price, - MemoryUsage + MemoryUsage, } impl Into for RateCategory { @@ -57,4 +54,3 @@ pub struct Rules { // example: allow to add only a single comment comments_per_user: i32, // default = 1 } - diff --git a/src/routes/rating.rs b/src/routes/rating.rs index 1b1ed13..33a65a5 100644 --- a/src/routes/rating.rs +++ b/src/routes/rating.rs @@ -1,41 +1,51 @@ +use crate::forms; +use crate::models; +use crate::startup::AppState; use actix_web::{web, HttpResponse}; -use serde::{Deserialize, Serialize}; -use crate::models::rating::RateCategory; -use serde_valid::Validate; use sqlx::PgPool; use tracing::instrument; -use uuid::Uuid; -use crate::startup::AppState; use tracing::Instrument; +use uuid::Uuid; // workflow // add, update, list, get(user_id), ACL, // ACL - access to func for a user // ACL - access to objects for a user -#[derive(Serialize, Deserialize, Debug, Validate)] -pub struct RatingForm { - pub obj_id: i32, // product external id - pub category: RateCategory, // rating of product | rating of service etc - #[validate(max_length = 1000)] - pub comment: Option, // always linked to a product - #[validate(minimum = 0)] - #[validate(maximum = 10)] - pub rate: i32, // -} - -pub async fn rating(app_state: web::Data, form: web::Json, pool: -web::Data) -> HttpResponse { +pub async fn rating( + app_state: web::Data, + form: web::Json, + pool: web::Data, +) -> HttpResponse { + //TODO. check if there already exists a rating for this product committed by this user let request_id = Uuid::new_v4(); - let user_id = app_state.user_id; // uuid Let's assume we have a user id already taken from auth - + match sqlx::query_as!( + models::Product, + r"SELECT * FROM product WHERE obj_id = $1", + form.obj_id + ) + .fetch_one(pool.get_ref()) + .await + { + Ok(product) => { + tracing::info!("req_id: {} Found product: {:?}", request_id, product.obj_id); + } + Err(e) => { + tracing::error!( + "req_id: {} Failed to fetch product: {:?}, error: {:?}", + request_id, + form.obj_id, + e + ); + return HttpResponse::InternalServerError().finish(); + } + }; - let query_span = tracing::info_span!( - "Saving new rating details in the database" - ); + let user_id = app_state.user_id; // uuid Let's assume we have a user id already taken from auth + let query_span = tracing::info_span!("Saving new rating details in the database"); // Get product by id // Insert rating - let category = Into::::into(form.category.clone()); + //let category = Into::::into(form.category.clone()); match sqlx::query!( r#" INSERT INTO rating (user_id, product_id, category, comment, hidden,rate, @@ -45,7 +55,7 @@ web::Data) -> HttpResponse { "#, user_id, form.obj_id, - category.as_str(), + form.category as models::RateCategory, form.comment, false, form.rate @@ -55,6 +65,7 @@ web::Data) -> HttpResponse { .await { Ok(_) => { + //TODO return json containing the id of the new rating tracing::info!( "req_id: {} New subscriber details have been saved to database", request_id @@ -65,7 +76,5 @@ web::Data) -> HttpResponse { tracing::error!("req_id: {} Failed to execute query: {:?}", request_id, e); HttpResponse::InternalServerError().finish() } - }; - println!("{:?}", form); - HttpResponse::Ok().finish() + } }