aboutsummaryrefslogtreecommitdiff
path: root/backend/src
diff options
context:
space:
mode:
authorAkshay <[email protected]>2020-12-26 05:21:46 +0000
committerAkshay <[email protected]>2020-12-26 05:21:46 +0000
commit8014def1a8da3397d78d1162f9e1b8c3f22d0322 (patch)
tree346e1de0ac6aa4ca973c1b3e5897c2c44948e5a8 /backend/src
parent7c6006e1abc6094b5922ab69ccfa5449b8dbbc99 (diff)
add transactions and quantities
- backend exposes endpoints to perform transactions - frontend introduces a new page to "checkout" cart
Diffstat (limited to 'backend/src')
-rw-r--r--backend/src/bin/server.rs18
-rw-r--r--backend/src/handlers/cart_items.rs131
-rw-r--r--backend/src/handlers/mod.rs1
-rw-r--r--backend/src/handlers/transaction.rs74
-rw-r--r--backend/src/handlers/users.rs4
-rw-r--r--backend/src/models.rs28
-rw-r--r--backend/src/schema.rs2
7 files changed, 238 insertions, 20 deletions
diff --git a/backend/src/bin/server.rs b/backend/src/bin/server.rs
index 7c67e4f..310914e 100644
--- a/backend/src/bin/server.rs
+++ b/backend/src/bin/server.rs
@@ -5,7 +5,7 @@ use actix_web::{web, App, HttpServer};
5use diesel::r2d2::{ConnectionManager, Pool}; 5use diesel::r2d2::{ConnectionManager, Pool};
6use diesel::MysqlConnection; 6use diesel::MysqlConnection;
7use furby::handlers::smoke::manual_hello; 7use furby::handlers::smoke::manual_hello;
8use furby::handlers::{cart_items, product, rating, users}; 8use furby::handlers::{cart_items, product, rating, transaction, users};
9use rand::Rng; 9use rand::Rng;
10 10
11#[actix_web::main] 11#[actix_web::main]
@@ -48,6 +48,7 @@ async fn main() -> std::io::Result<()> {
48 web::scope("/user") 48 web::scope("/user")
49 .route("/existing", web::post().to(users::name_exists)) 49 .route("/existing", web::post().to(users::name_exists))
50 .route("/login", web::post().to(users::login)) 50 .route("/login", web::post().to(users::login))
51 .route("/logout", web::post().to(users::logout))
51 .route("/{uname}", web::get().to(users::user_details)) 52 .route("/{uname}", web::get().to(users::user_details))
52 .route("/new", web::post().to(users::new_user)) 53 .route("/new", web::post().to(users::new_user))
53 .route( 54 .route(
@@ -75,6 +76,10 @@ async fn main() -> std::io::Result<()> {
75 "/items", 76 "/items",
76 web::get().to(cart_items::get_user_cart_items), 77 web::get().to(cart_items::get_user_cart_items),
77 ) 78 )
79 .route(
80 "/total",
81 web::get().to(cart_items::get_user_cart_total),
82 )
78 .route("/add", web::post().to(cart_items::add_to_cart)) 83 .route("/add", web::post().to(cart_items::add_to_cart))
79 .route( 84 .route(
80 "/remove", 85 "/remove",
@@ -86,6 +91,17 @@ async fn main() -> std::io::Result<()> {
86 .route("/add", web::post().to(rating::add_rating)) 91 .route("/add", web::post().to(rating::add_rating))
87 .route("/remove", web::post().to(rating::remove_rating)), 92 .route("/remove", web::post().to(rating::remove_rating)),
88 ) 93 )
94 .service(
95 web::scope("/transaction")
96 .route(
97 "/checkout",
98 web::post().to(transaction::checkout_cart),
99 )
100 .route(
101 "/list",
102 web::get().to(transaction::list_transactions),
103 ),
104 )
89 .route("/hey", web::get().to(manual_hello)) 105 .route("/hey", web::get().to(manual_hello))
90 }) 106 })
91 .bind("127.0.0.1:7878")? 107 .bind("127.0.0.1:7878")?
diff --git a/backend/src/handlers/cart_items.rs b/backend/src/handlers/cart_items.rs
index 25baaeb..e17f4c4 100644
--- a/backend/src/handlers/cart_items.rs
+++ b/backend/src/handlers/cart_items.rs
@@ -7,7 +7,7 @@ use actix_identity::Identity;
7use actix_web::{web, HttpResponse, Responder}; 7use actix_web::{web, HttpResponse, Responder};
8use diesel::prelude::*; 8use diesel::prelude::*;
9use log::{error, info}; 9use log::{error, info};
10use serde::Deserialize; 10use serde::Serialize;
11 11
12pub async fn add_to_cart( 12pub async fn add_to_cart(
13 cookie: Identity, 13 cookie: Identity,
@@ -27,16 +27,41 @@ pub async fn add_to_cart(
27 let new_cart_item = AddCartItem { 27 let new_cart_item = AddCartItem {
28 cart_id: selected_user.id, 28 cart_id: selected_user.id,
29 product_id: item_details, 29 product_id: item_details,
30 quantity: Some(1),
30 }; 31 };
31 info!( 32 info!(
32 "cart id: {:?}, product id {:?}", 33 "cart id: {:?}, product id {:?}",
33 selected_user.id, item_details 34 selected_user.id, item_details
34 ); 35 );
35 diesel::insert_into(cart_items) 36 let current_entry = cart_items
36 .values((cart_id.eq(selected_user.id), product_id.eq(item_details))) 37 .filter(cart_id.eq(selected_user.id))
37 .execute(&conn) 38 .filter(product_id.eq(item_details))
38 .expect("Coundn't connect to DB"); 39 .limit(1)
39 HttpResponse::Ok().body("Inserted successfully!") 40 .first::<CartItem>(&conn);
41 match current_entry {
42 Ok(v) => {
43 info!("Item already present in cart, increasing quantity.");
44 let old_quantity = v.quantity.unwrap_or(1);
45 diesel::update(
46 cart_items
47 .filter(cart_id.eq(selected_user.id))
48 .filter(product_id.eq(item_details)),
49 )
50 .set(quantity.eq(old_quantity + 1))
51 .execute(&conn)
52 .unwrap();
53 return HttpResponse::Ok()
54 .body("Updated quantity successfully!");
55 }
56 Err(_) => {
57 info!("Item not present, adding to cart.");
58 diesel::insert_into(cart_items)
59 .values(new_cart_item)
60 .execute(&conn)
61 .expect("Couldn't connect to DB");
62 HttpResponse::Ok().body("Inserted successfully!")
63 }
64 }
40 } else { 65 } else {
41 error!("Unauthorized add to cart action!"); 66 error!("Unauthorized add to cart action!");
42 return HttpResponse::Unauthorized() 67 return HttpResponse::Unauthorized()
@@ -58,15 +83,44 @@ pub async fn remove_from_cart(
58 .limit(1) 83 .limit(1)
59 .first::<Customer>(&conn) 84 .first::<Customer>(&conn)
60 .expect("Couldn't connect to DB"); 85 .expect("Couldn't connect to DB");
61 86 let current_entry = cart_items
62 diesel::delete( 87 .filter(cart_id.eq(selected_user.id))
63 cart_items 88 .filter(product_id.eq(item_details))
64 .filter(cart_id.eq(selected_user.id)) 89 .limit(1)
65 .filter(product_id.eq(item_details)), 90 .first::<CartItem>(&conn);
66 ) 91 match current_entry {
67 .execute(&conn) 92 Ok(v) => {
68 .expect("Coundn't connect to DB"); 93 info!("Item already present in cart, increasing quantity.");
69 HttpResponse::Ok().body("Removed successfully!") 94 let old_quantity = v.quantity.unwrap_or(1);
95 if old_quantity == 1 {
96 diesel::delete(
97 cart_items
98 .filter(cart_id.eq(selected_user.id))
99 .filter(product_id.eq(item_details)),
100 )
101 .execute(&conn)
102 .expect("Coundn't connect to DB");
103 } else {
104 diesel::update(
105 cart_items
106 .filter(cart_id.eq(selected_user.id))
107 .filter(product_id.eq(item_details)),
108 )
109 .set(quantity.eq(old_quantity - 1))
110 .execute(&conn)
111 .unwrap();
112 return HttpResponse::Ok()
113 .body("Updated quantity successfully!");
114 }
115 return HttpResponse::Ok()
116 .body("Updated quantity successfully!");
117 }
118 Err(_) => {
119 info!("Item not present.");
120 return HttpResponse::InternalServerError()
121 .body("Item not found!");
122 }
123 }
70 } else { 124 } else {
71 error!("Unauthorized add to cart action!"); 125 error!("Unauthorized add to cart action!");
72 return HttpResponse::Unauthorized() 126 return HttpResponse::Unauthorized()
@@ -74,6 +128,12 @@ pub async fn remove_from_cart(
74 } 128 }
75} 129}
76 130
131#[derive(Serialize)]
132struct UserCartItem {
133 product_item: Product,
134 quantity: i32,
135}
136
77pub async fn get_user_cart_items( 137pub async fn get_user_cart_items(
78 cookie: Identity, 138 cookie: Identity,
79 pool: web::Data<TPool>, 139 pool: web::Data<TPool>,
@@ -92,11 +152,15 @@ pub async fn get_user_cart_items(
92 let cart_products = user_cart_items 152 let cart_products = user_cart_items
93 .into_iter() 153 .into_iter()
94 .map(|item| { 154 .map(|item| {
95 prod::product 155 let p = prod::product
96 .filter(prod::id.eq(item.product_id)) 156 .filter(prod::id.eq(item.product_id))
97 .limit(1) 157 .limit(1)
98 .first::<Product>(&conn) 158 .first::<Product>(&conn)
99 .expect("Couldn't connect to db") 159 .expect("Couldn't connect to db");
160 UserCartItem {
161 product_item: p,
162 quantity: item.quantity.unwrap_or(1),
163 }
100 }) 164 })
101 .collect::<Vec<_>>(); 165 .collect::<Vec<_>>();
102 return HttpResponse::Ok().json(&cart_products); 166 return HttpResponse::Ok().json(&cart_products);
@@ -105,3 +169,36 @@ pub async fn get_user_cart_items(
105 .body("Need to be logged in to add to cart!"); 169 .body("Need to be logged in to add to cart!");
106 } 170 }
107} 171}
172
173pub async fn get_user_cart_total(
174 cookie: Identity,
175 pool: web::Data<TPool>,
176) -> impl Responder {
177 let conn = pool.get().unwrap();
178 if let Some(uname) = cookie.identity() {
179 let selected_user = customer
180 .filter(username.eq(&uname))
181 .limit(1)
182 .first::<Customer>(&conn)
183 .expect("Couldn't connect to DB");
184 let user_cart_items = cart_items
185 .filter(cart_id.eq(selected_user.id))
186 .load::<CartItem>(&conn)
187 .expect("Couldn't connect to DB");
188 let cart_total: f32 = user_cart_items
189 .into_iter()
190 .map(|item| {
191 let p = prod::product
192 .filter(prod::id.eq(item.product_id))
193 .limit(1)
194 .first::<Product>(&conn)
195 .expect("Couldn't connect to db");
196 return p.price * item.quantity.unwrap_or(1) as f32;
197 })
198 .sum();
199 return HttpResponse::Ok().json(&cart_total);
200 } else {
201 return HttpResponse::Unauthorized()
202 .body("Need to be logged in to add to cart!");
203 }
204}
diff --git a/backend/src/handlers/mod.rs b/backend/src/handlers/mod.rs
index 9416857..e4ecb3a 100644
--- a/backend/src/handlers/mod.rs
+++ b/backend/src/handlers/mod.rs
@@ -2,4 +2,5 @@ pub mod cart_items;
2pub mod product; 2pub mod product;
3pub mod rating; 3pub mod rating;
4pub mod smoke; 4pub mod smoke;
5pub mod transaction;
5pub mod users; 6pub mod users;
diff --git a/backend/src/handlers/transaction.rs b/backend/src/handlers/transaction.rs
new file mode 100644
index 0000000..1e87312
--- /dev/null
+++ b/backend/src/handlers/transaction.rs
@@ -0,0 +1,74 @@
1use crate::models::{AddTransaction, CartItem, Customer, Product, Transaction};
2use crate::schema::cart_items::dsl::*;
3use crate::schema::customer::dsl::*;
4use crate::schema::product::dsl as prod;
5use crate::schema::transaction::dsl::*;
6use crate::TPool;
7
8use actix_identity::Identity;
9use actix_web::{web, HttpResponse, Responder};
10use diesel::prelude::*;
11use log::{error, info};
12
13pub async fn checkout_cart(
14 pool: web::Data<TPool>,
15 pmt_kind: String,
16 cookie: Identity,
17) -> impl Responder {
18 let conn = pool.get().unwrap();
19 info!("Checkout cart for user: {:?}", cookie.identity());
20 if let Some(uname) = cookie.identity() {
21 let selected_user = customer
22 .filter(username.eq(&uname))
23 .limit(1)
24 .first::<Customer>(&conn)
25 .expect("Couldn't connect to DB");
26 let user_cart_items = cart_items
27 .filter(cart_id.eq(selected_user.id))
28 .load::<CartItem>(&conn)
29 .expect("Couldn't connect to DB");
30 let cart_total = user_cart_items.into_iter().fold(0., |acc, item| {
31 let item_price = prod::product
32 .filter(prod::id.eq(item.product_id))
33 .limit(1)
34 .first::<Product>(&conn)
35 .unwrap()
36 .price;
37 acc + item.quantity.unwrap_or(1) as f32 * item_price
38 });
39 let transaction_entry = AddTransaction {
40 customer_id: Some(selected_user.id),
41 amount: cart_total,
42 payment_type: pmt_kind,
43 };
44 diesel::insert_into(transaction)
45 .values(transaction_entry)
46 .execute(&conn)
47 .expect("Coundn't connect to DB");
48 return HttpResponse::Ok().body("Transaction performed successfully");
49 } else {
50 return HttpResponse::Unauthorized().body("Login first");
51 }
52}
53
54pub async fn list_transactions(
55 pool: web::Data<TPool>,
56 cookie: Identity,
57) -> impl Responder {
58 let conn = pool.get().unwrap();
59 if let Some(uname) = cookie.identity() {
60 let selected_user = customer
61 .filter(username.eq(&uname))
62 .limit(1)
63 .first::<Customer>(&conn)
64 .expect("Couldn't connect to DB");
65 let user_transactions = transaction
66 .filter(customer_id.eq(selected_user.id))
67 .load::<Transaction>(&conn)
68 .expect("Couldn't connect to DB");
69 return HttpResponse::Ok().json(&user_transactions);
70 } else {
71 return HttpResponse::Unauthorized()
72 .body("Need to be logged in to add to cart!");
73 }
74}
diff --git a/backend/src/handlers/users.rs b/backend/src/handlers/users.rs
index 24fb591..a043c1f 100644
--- a/backend/src/handlers/users.rs
+++ b/backend/src/handlers/users.rs
@@ -58,7 +58,7 @@ pub async fn login(
58 login_details: web::Json<Login>, 58 login_details: web::Json<Login>,
59) -> impl Responder { 59) -> impl Responder {
60 info!("Login hit"); 60 info!("Login hit");
61 if let Some(uname) = cookie.identity() { 61 if cookie.identity().is_some() {
62 info!("Found existing cookie: {:?}", cookie.identity()); 62 info!("Found existing cookie: {:?}", cookie.identity());
63 return HttpResponse::Ok().finish(); 63 return HttpResponse::Ok().finish();
64 } 64 }
@@ -84,7 +84,7 @@ pub async fn login(
84 84
85pub async fn logout(cookie: Identity) -> impl Responder { 85pub async fn logout(cookie: Identity) -> impl Responder {
86 cookie.forget(); 86 cookie.forget();
87 HttpResponse::Found().header("location", "/").finish() 87 HttpResponse::Ok().body("Successful logout.")
88} 88}
89 89
90pub async fn user_details( 90pub async fn user_details(
diff --git a/backend/src/models.rs b/backend/src/models.rs
index a104209..bf531ad 100644
--- a/backend/src/models.rs
+++ b/backend/src/models.rs
@@ -35,6 +35,8 @@ pub struct Product {
35 pub kind: Option<String>, 35 pub kind: Option<String>,
36 pub price: f32, 36 pub price: f32,
37 pub description: Option<String>, 37 pub description: Option<String>,
38 pub src: Option<String>,
39 pub ios_src: Option<String>,
38} 40}
39 41
40#[derive(Insertable, Deserialize)] 42#[derive(Insertable, Deserialize)]
@@ -48,6 +50,12 @@ pub struct NewProduct {
48 50
49 #[serde(skip_serializing_if = "Option::is_none")] 51 #[serde(skip_serializing_if = "Option::is_none")]
50 pub description: Option<String>, 52 pub description: Option<String>,
53
54 #[serde(skip_serializing_if = "Option::is_none")]
55 pub src: Option<String>,
56
57 #[serde(skip_serializing_if = "Option::is_none")]
58 pub ios_src: Option<String>,
51} 59}
52 60
53#[derive(Deserialize)] 61#[derive(Deserialize)]
@@ -63,6 +71,7 @@ pub struct UpdateProduct {
63pub struct CartItem { 71pub struct CartItem {
64 pub cart_id: i32, 72 pub cart_id: i32,
65 pub product_id: i32, 73 pub product_id: i32,
74 pub quantity: Option<i32>,
66} 75}
67 76
68#[derive(Insertable, Deserialize)] 77#[derive(Insertable, Deserialize)]
@@ -70,6 +79,7 @@ pub struct CartItem {
70pub struct AddCartItem { 79pub struct AddCartItem {
71 pub cart_id: i32, 80 pub cart_id: i32,
72 pub product_id: i32, 81 pub product_id: i32,
82 pub quantity: Option<i32>,
73} 83}
74 84
75/* Rating */ 85/* Rating */
@@ -95,3 +105,21 @@ pub struct AddRating {
95 pub product_id: i32, 105 pub product_id: i32,
96 pub customer_id: i32, 106 pub customer_id: i32,
97} 107}
108
109/* Transaction */
110#[derive(Queryable, Serialize)]
111pub struct Transaction {
112 pub id: i32,
113 pub payment_type: String,
114 pub amount: f32,
115 pub customer_id: Option<i32>,
116 pub order_date: NaiveDate,
117}
118
119#[derive(Insertable, Deserialize)]
120#[table_name = "transaction"]
121pub struct AddTransaction {
122 pub payment_type: String,
123 pub amount: f32,
124 pub customer_id: Option<i32>,
125}
diff --git a/backend/src/schema.rs b/backend/src/schema.rs
index f08221a..1419bc0 100644
--- a/backend/src/schema.rs
+++ b/backend/src/schema.rs
@@ -2,6 +2,7 @@ table! {
2 cart_items (cart_id, product_id) { 2 cart_items (cart_id, product_id) {
3 cart_id -> Integer, 3 cart_id -> Integer,
4 product_id -> Integer, 4 product_id -> Integer,
5 quantity -> Nullable<Integer>,
5 } 6 }
6} 7}
7 8
@@ -43,6 +44,7 @@ table! {
43 payment_type -> Varchar, 44 payment_type -> Varchar,
44 amount -> Float, 45 amount -> Float,
45 customer_id -> Nullable<Integer>, 46 customer_id -> Nullable<Integer>,
47 order_date -> Date,
46 } 48 }
47} 49}
48 50