diff options
-rw-r--r-- | src/bin/server.rs | 11 | ||||
-rw-r--r-- | src/handlers/product.rs | 23 | ||||
-rw-r--r-- | src/handlers/rating.rs | 90 | ||||
-rw-r--r-- | src/models.rs | 25 | ||||
-rw-r--r-- | tests/requests.txt | 10 |
5 files changed, 149 insertions, 10 deletions
diff --git a/src/bin/server.rs b/src/bin/server.rs index 601d514..5af3135 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs | |||
@@ -5,7 +5,7 @@ use actix_web::{web, App, HttpServer}; | |||
5 | use diesel::r2d2::{ConnectionManager, Pool}; | 5 | use diesel::r2d2::{ConnectionManager, Pool}; |
6 | use diesel::MysqlConnection; | 6 | use diesel::MysqlConnection; |
7 | use furby::handlers::smoke::manual_hello; | 7 | use furby::handlers::smoke::manual_hello; |
8 | use furby::handlers::{cart_items, product, users}; | 8 | use furby::handlers::{cart_items, product, rating, users}; |
9 | use rand::Rng; | 9 | use rand::Rng; |
10 | 10 | ||
11 | #[actix_web::main] | 11 | #[actix_web::main] |
@@ -46,6 +46,10 @@ async fn main() -> std::io::Result<()> { | |||
46 | .route("/new", web::post().to(product::new_product)) | 46 | .route("/new", web::post().to(product::new_product)) |
47 | .route("/{id}", web::get().to(product::product_details)) | 47 | .route("/{id}", web::get().to(product::product_details)) |
48 | .route( | 48 | .route( |
49 | "/reviews/{id}", | ||
50 | web::get().to(product::get_product_reviews), | ||
51 | ) | ||
52 | .route( | ||
49 | "/update_product/{id}", | 53 | "/update_product/{id}", |
50 | web::post().to(product::update_product), | 54 | web::post().to(product::update_product), |
51 | ), | 55 | ), |
@@ -62,6 +66,11 @@ async fn main() -> std::io::Result<()> { | |||
62 | web::post().to(cart_items::remove_from_cart), | 66 | web::post().to(cart_items::remove_from_cart), |
63 | ), | 67 | ), |
64 | ) | 68 | ) |
69 | .service( | ||
70 | web::scope("/rating") | ||
71 | .route("/add", web::post().to(rating::add_rating)) | ||
72 | .route("/remove", web::post().to(rating::remove_rating)), | ||
73 | ) | ||
65 | .route("/hey", web::get().to(manual_hello)) | 74 | .route("/hey", web::get().to(manual_hello)) |
66 | }) | 75 | }) |
67 | .bind("127.0.0.1:7878")? | 76 | .bind("127.0.0.1:7878")? |
diff --git a/src/handlers/product.rs b/src/handlers/product.rs index 788efb3..21bdbc9 100644 --- a/src/handlers/product.rs +++ b/src/handlers/product.rs | |||
@@ -1,11 +1,11 @@ | |||
1 | use crate::models::{NewProduct, Product, UpdateProduct}; | 1 | use crate::models::{NewProduct, Product, Rating, UpdateProduct}; |
2 | use crate::schema::product::dsl::*; | 2 | use crate::schema::product::dsl::*; |
3 | use crate::schema::rating::dsl as rating; | ||
3 | use crate::TPool; | 4 | use crate::TPool; |
4 | 5 | ||
5 | use actix_web::{web, HttpResponse, Responder}; | 6 | use actix_web::{web, HttpResponse, Responder}; |
6 | use diesel::prelude::*; | 7 | use diesel::prelude::*; |
7 | use log::{error, info}; | 8 | use log::{error, info}; |
8 | use serde::Deserialize; | ||
9 | 9 | ||
10 | pub async fn new_product( | 10 | pub async fn new_product( |
11 | pool: web::Data<TPool>, | 11 | pool: web::Data<TPool>, |
@@ -75,13 +75,24 @@ pub async fn get_all_products(pool: web::Data<TPool>) -> impl Responder { | |||
75 | let conn = pool.get().unwrap(); | 75 | let conn = pool.get().unwrap(); |
76 | info!("Generating and returning catalog ..."); | 76 | info!("Generating and returning catalog ..."); |
77 | match product.load::<Product>(&conn) { | 77 | match product.load::<Product>(&conn) { |
78 | Ok(products) => { | 78 | Ok(products) => return HttpResponse::Ok().json(&products), |
79 | return HttpResponse::Ok() | ||
80 | .body(serde_json::to_string(&products).unwrap()) | ||
81 | } | ||
82 | Err(_) => { | 79 | Err(_) => { |
83 | return HttpResponse::InternalServerError() | 80 | return HttpResponse::InternalServerError() |
84 | .body("Unable to fetch product catalog") | 81 | .body("Unable to fetch product catalog") |
85 | } | 82 | } |
86 | } | 83 | } |
87 | } | 84 | } |
85 | |||
86 | pub async fn get_product_reviews( | ||
87 | pool: web::Data<TPool>, | ||
88 | product_id: web::Path<i32>, | ||
89 | ) -> impl Responder { | ||
90 | let conn = pool.get().unwrap(); | ||
91 | info!("Fetching product reviews for {}", product_id); | ||
92 | let pid = product_id.into_inner(); | ||
93 | let rating_entries = rating::rating | ||
94 | .filter(rating::product_id.eq(pid)) | ||
95 | .load::<Rating>(&conn) | ||
96 | .expect("Couldn't connect to DB"); | ||
97 | return HttpResponse::Ok().json(&rating_entries); | ||
98 | } | ||
diff --git a/src/handlers/rating.rs b/src/handlers/rating.rs new file mode 100644 index 0000000..309c2c6 --- /dev/null +++ b/src/handlers/rating.rs | |||
@@ -0,0 +1,90 @@ | |||
1 | use crate::models::{AddRating, Customer, Rating}; | ||
2 | use crate::schema::rating::dsl as rating; | ||
3 | use crate::schema::{customer::dsl::*, product::dsl::*}; | ||
4 | use crate::TPool; | ||
5 | |||
6 | use actix_identity::Identity; | ||
7 | use actix_web::{web, HttpResponse, Responder}; | ||
8 | use diesel::prelude::*; | ||
9 | use log::{error, info}; | ||
10 | use serde::Deserialize; | ||
11 | |||
12 | #[derive(Deserialize, Debug)] | ||
13 | pub struct AddRatingJson { | ||
14 | pub comment_text: Option<String>, | ||
15 | pub stars: Option<i32>, | ||
16 | pub product_id: i32, | ||
17 | } | ||
18 | |||
19 | pub async fn add_rating( | ||
20 | cookie: Identity, | ||
21 | rating_details: web::Json<AddRatingJson>, | ||
22 | pool: web::Data<TPool>, | ||
23 | ) -> impl Responder { | ||
24 | info!("Add rating hit: {:?}", rating_details.product_id); | ||
25 | let conn = pool.get().unwrap(); | ||
26 | if let Some(uname) = cookie.identity() { | ||
27 | let selected_user = customer | ||
28 | .filter(username.eq(&uname)) | ||
29 | .limit(1) | ||
30 | .first::<Customer>(&conn) | ||
31 | .expect("Couldn't connect to DB"); | ||
32 | let rating_details = rating_details.into_inner(); | ||
33 | let new_rating = AddRating { | ||
34 | comment_text: rating_details.comment_text, | ||
35 | stars: rating_details.stars, | ||
36 | product_id: rating_details.product_id, | ||
37 | customer_id: selected_user.id, | ||
38 | }; | ||
39 | diesel::insert_into(rating::rating) | ||
40 | .values(new_rating) | ||
41 | .execute(&conn) | ||
42 | .expect("Coundn't connect to DB"); | ||
43 | HttpResponse::Ok().body("Inserted rating successfully!") | ||
44 | } else { | ||
45 | error!("Unauthorized add rating action!"); | ||
46 | return HttpResponse::Unauthorized() | ||
47 | .body("Need to be logged in to add rating!"); | ||
48 | } | ||
49 | } | ||
50 | |||
51 | #[derive(Deserialize, Debug)] | ||
52 | pub struct RemoveRating { | ||
53 | rating_id: i32, | ||
54 | } | ||
55 | |||
56 | pub async fn remove_rating( | ||
57 | cookie: Identity, | ||
58 | rating_details: web::Json<RemoveRating>, | ||
59 | pool: web::Data<TPool>, | ||
60 | ) -> impl Responder { | ||
61 | info!("Remove rating hit: {:?}", rating_details.rating_id); | ||
62 | let conn = pool.get().unwrap(); | ||
63 | if let Some(uname) = cookie.identity() { | ||
64 | let selected_user = customer | ||
65 | .filter(username.eq(&uname)) | ||
66 | .limit(1) | ||
67 | .first::<Customer>(&conn) | ||
68 | .expect("Couldn't connect to DB"); | ||
69 | |||
70 | diesel::delete( | ||
71 | rating::rating | ||
72 | .filter(rating::customer_id.eq(selected_user.id)) | ||
73 | .filter(rating::id.eq(rating_details.rating_id)), | ||
74 | ) | ||
75 | .execute(&conn) | ||
76 | .expect("Coundn't connect to DB"); | ||
77 | HttpResponse::Ok().body("Removed successfully!") | ||
78 | } else { | ||
79 | error!("Unauthorized add to cart action!"); | ||
80 | return HttpResponse::Unauthorized() | ||
81 | .body("Need to be logged in to add to cart!"); | ||
82 | } | ||
83 | } | ||
84 | |||
85 | // pub async fn get_product_reviews( | ||
86 | // product: web::Json<GetProductReviews>, | ||
87 | // pool: web::Data<TPool>, | ||
88 | // ) -> impl Responder { | ||
89 | // unimplemented!() | ||
90 | // } | ||
diff --git a/src/models.rs b/src/models.rs index acd67a6..a104209 100644 --- a/src/models.rs +++ b/src/models.rs | |||
@@ -1,5 +1,6 @@ | |||
1 | use super::schema::{cart_items, customer, product, rating, transaction}; | 1 | use super::schema::{cart_items, customer, product, rating, transaction}; |
2 | 2 | ||
3 | use chrono::naive::{NaiveDate, NaiveDateTime}; | ||
3 | use diesel::{Insertable, Queryable}; | 4 | use diesel::{Insertable, Queryable}; |
4 | use serde::{Deserialize, Serialize}; | 5 | use serde::{Deserialize, Serialize}; |
5 | 6 | ||
@@ -70,3 +71,27 @@ pub struct AddCartItem { | |||
70 | pub cart_id: i32, | 71 | pub cart_id: i32, |
71 | pub product_id: i32, | 72 | pub product_id: i32, |
72 | } | 73 | } |
74 | |||
75 | /* Rating */ | ||
76 | #[derive(Queryable, Serialize)] | ||
77 | pub struct Rating { | ||
78 | pub id: i32, | ||
79 | pub comment_text: Option<String>, | ||
80 | pub comment_date: Option<NaiveDate>, | ||
81 | pub product_id: Option<i32>, | ||
82 | pub customer_id: Option<i32>, | ||
83 | pub stars: Option<i32>, | ||
84 | } | ||
85 | |||
86 | #[derive(Insertable, Deserialize)] | ||
87 | #[table_name = "rating"] | ||
88 | pub struct AddRating { | ||
89 | #[serde(skip_serializing_if = "Option::is_none")] | ||
90 | pub comment_text: Option<String>, | ||
91 | |||
92 | #[serde(skip_serializing_if = "Option::is_none")] | ||
93 | pub stars: Option<i32>, | ||
94 | |||
95 | pub product_id: i32, | ||
96 | pub customer_id: i32, | ||
97 | } | ||
diff --git a/tests/requests.txt b/tests/requests.txt index 7e38905..7e2ca5c 100644 --- a/tests/requests.txt +++ b/tests/requests.txt | |||
@@ -2,7 +2,7 @@ http POST :7878/user/login username=akshay password=password | |||
2 | 2 | ||
3 | http POST :7878/user/login username=akshay password=nigga | 3 | http POST :7878/user/login username=akshay password=nigga |
4 | 4 | ||
5 | http POST :7878/user/change_password Cookie:user-login=mKT3PRPSKJp/AdsfPCXg3GICmQW2wViUwUKPpsXKp+70ug== old_password=nigga new_password=nigga | 5 | http POST :7878/user/change_password Cookie: old_password=nigga new_password=nigga |
6 | 6 | ||
7 | http :7878/user/change_password username=akshay password=password | 7 | http :7878/user/change_password username=akshay password=password |
8 | 8 | ||
@@ -10,8 +10,12 @@ http :7878/product/catalog | |||
10 | 10 | ||
11 | http :7878/product/1 | 11 | http :7878/product/1 |
12 | 12 | ||
13 | http POST :7878/cart/add Cookie:user-login=mKT3PRPSKJp/AdsfPCXg3GICmQW2wViUwUKPpsXKp+70ug== product_id:=1 | 13 | http POST :7878/cart/add Cookie: product_id:=1 |
14 | 14 | ||
15 | http :7878/cart/items Cookie:user-login=mKT3PRPSKJp/AdsfPCXg3GICmQW2wViUwUKPpsXKp+70ug== | 15 | http :7878/cart/items Cookie: |
16 | 16 | ||
17 | http POST :7878/cart/remove Cookie: product_id:=1 | 17 | http POST :7878/cart/remove Cookie: product_id:=1 |
18 | |||
19 | http POST :7878/rating/add Cookie: product_id:=1 stars:=3 comment_text=Very good functional chair | ||
20 | |||
21 | http :7878/product/reviews/1 | ||