From 5d757e84d5320da47d0a749f20920c9c4c70cf96 Mon Sep 17 00:00:00 2001 From: eraden Date: Sat, 16 Apr 2022 12:48:38 +0200 Subject: [PATCH] Update cart and items, add cart state --- api/src/actors/database.rs | 4 + .../actors/database/shopping_cart_items.rs | 201 ++++++++++++++++++ api/src/actors/database/shopping_carts.rs | 46 +++- api/src/main.rs | 2 +- api/src/model.rs | 14 +- db/migrate/202204161233_add_cart_state.sql | 10 + 6 files changed, 266 insertions(+), 11 deletions(-) create mode 100644 api/src/actors/database/shopping_cart_items.rs create mode 100644 db/migrate/202204161233_add_cart_state.sql diff --git a/api/src/actors/database.rs b/api/src/actors/database.rs index 26a78c5..db994d2 100644 --- a/api/src/actors/database.rs +++ b/api/src/actors/database.rs @@ -5,6 +5,7 @@ pub use account_orders::*; pub use accounts::*; pub use order_items::*; pub use products::*; +pub use shopping_cart_items::*; pub use shopping_carts::*; pub use stocks::*; @@ -12,6 +13,7 @@ mod account_orders; mod accounts; mod order_items; mod products; +mod shopping_cart_items; mod shopping_carts; mod stocks; @@ -45,6 +47,8 @@ pub enum Error { OrderItem(order_items::Error), #[error("{0}")] ShoppingCart(shopping_carts::Error), + #[error("{0}")] + ShoppingCartItem(shopping_cart_items::Error), } pub type Result = std::result::Result; diff --git a/api/src/actors/database/shopping_cart_items.rs b/api/src/actors/database/shopping_cart_items.rs new file mode 100644 index 0000000..874cc8d --- /dev/null +++ b/api/src/actors/database/shopping_cart_items.rs @@ -0,0 +1,201 @@ +use crate::async_handler; +use actix::{Handler, ResponseActFuture, WrapFuture}; +use sqlx::PgPool; + +use crate::database::Database; +use crate::model::*; + +use super::Result; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("Can't create shopping cart item")] + CantCreate, + #[error("Can't update shopping cart item")] + CantUpdate(ShoppingCartItemId), + #[error("Shopping cart does not exists")] + NotExists, + #[error("Failed to load all shopping cart items")] + All, + #[error("Failed to load account shopping cart items")] + AccountCarts, + #[error("Failed to load items for shopping cart {0}")] + CartItems(ShoppingCartId), +} + +#[derive(actix::Message)] +#[rtype(result = "Result>")] +pub struct AllShoppingCartItems; + +async_handler!(AllShoppingCartItems, all_shopping_cart_items, Vec); + +pub async fn all_shopping_cart_items( + _msg: AllShoppingCartItems, + pool: PgPool, +) -> Result> { + sqlx::query_as( + r#" +SELECT id, product_id, shopping_cart_id, quantity, quantity_unit +FROM shopping_cart_items + "#, + ) + .fetch_all(&pool) + .await + .map_err(|e| { + log::error!("{e:?}"); + super::Error::ShoppingCartItem(Error::All) + }) +} + +#[derive(actix::Message)] +#[rtype(result = "Result>")] +pub struct AccountShoppingCartItems { + pub account_id: AccountId, +} + +async_handler!(AccountShoppingCartItems, account_shopping_cart_items, Vec); + +pub async fn account_shopping_cart_items( + msg: AccountShoppingCartItems, + pool: PgPool, +) -> Result> { + sqlx::query_as( + r#" +SELECT id, product_id, shopping_cart_id, quantity, quantity_unit +FROM shopping_cart_items +WHERE buyer_id = $1 + "#, + ) + .bind(msg.account_id) + .fetch_all(&pool) + .await + .map_err(|e| { + log::error!("{e:?}"); + super::Error::ShoppingCartItem(Error::AccountCarts) + }) +} + +#[derive(actix::Message)] +#[rtype(result = "Result")] +pub struct CreateShoppingCartItem { + pub product_id: ProductId, + pub shopping_cart_id: ShoppingCartId, + pub quantity: Quantity, + pub quantity_unit: QuantityUnit, +} + +async_handler!(CreateShoppingCartItem, create_shopping_cart_item, ShoppingCartItem); + +async fn create_shopping_cart_item( + msg: CreateShoppingCartItem, + db: PgPool, +) -> Result { + sqlx::query_as( + r#" +INSERT INTO shopping_cart_items (product_id, shopping_cart_id, quantity, quantity_unit) +VALUES ($1, $2, $3, $4) +RETURNING id, product_id, shopping_cart_id, quantity, quantity_unit + "#, + ) + .bind(msg.product_id) + .bind(msg.shopping_cart_id) + .bind(msg.quantity) + .bind(msg.quantity_unit) + .fetch_one(&db) + .await + .map_err(|e| { + log::error!("{e:?}"); + super::Error::ShoppingCartItem(Error::CantCreate) + }) +} + +#[derive(actix::Message)] +#[rtype(result = "Result")] +pub struct UpdateShoppingCartItem { + pub id: ShoppingCartItemId, + pub product_id: ProductId, + pub shopping_cart_id: ShoppingCartId, + pub quantity: Quantity, + pub quantity_unit: QuantityUnit, +} + +async_handler!(UpdateShoppingCartItem, update_shopping_cart_item, ShoppingCartItem); + +async fn update_shopping_cart_item( + msg: UpdateShoppingCartItem, + db: PgPool, +) -> Result { + sqlx::query_as( + r#" +UPDATE shopping_cart_items +SET product_id = $2 AND shopping_cart_id = $3 AND quantity = $4 AND quantity_unit = $5 +WHERE id = $1 +RETURNING id, product_id, shopping_cart_id, quantity, quantity_unit + "#, + ) + .bind(msg.id) + .bind(msg.product_id) + .bind(msg.shopping_cart_id) + .bind(msg.quantity) + .bind(msg.quantity_unit) + .fetch_one(&db) + .await + .map_err(|e| { + log::error!("{e:?}"); + super::Error::ShoppingCartItem(Error::CantUpdate(msg.id)) + }) +} + +#[derive(actix::Message)] +#[rtype(result = "Result")] +pub struct FindShoppingCartItem { + pub id: ShoppingCartItemId, +} + +async_handler!(FindShoppingCartItem, find_shopping_cart_item, ShoppingCartItem); + +async fn find_shopping_cart_item( + msg: FindShoppingCartItem, + db: PgPool, +) -> Result { + sqlx::query_as( + r#" +SELECT id, product_id, shopping_cart_id, quantity, quantity_unit +FROM shopping_cart_items +WHERE id = $1 + "#, + ) + .bind(msg.id) + .fetch_one(&db) + .await + .map_err(|e| { + log::error!("{e:?}"); + super::Error::ShoppingCartItem(Error::NotExists) + }) +} + +#[derive(actix::Message)] +#[rtype(result = "Result>")] +pub struct CartItems { + pub shopping_cart_id: ShoppingCartId, +} + +async_handler!(CartItems, cart_items, Vec); + +async fn cart_items(msg: CartItems, pool: PgPool) -> Result> { + let shopping_cart_id = msg.shopping_cart_id; + sqlx::query_as( + r#" +SELECT id, product_id, shopping_cart_id, quantity, quantity_unit +FROM shopping_cart_items +WHERE shopping_cart_id = $1 + "#, + ) + .bind(msg.shopping_cart_id) + .fetch_all(&pool) + .await + .map_err(|e| { + log::error!("{e:?}"); + super::Error::ShoppingCartItem(Error::CartItems(shopping_cart_id)) + }) +} diff --git a/api/src/actors/database/shopping_carts.rs b/api/src/actors/database/shopping_carts.rs index d28dfa2..af11388 100644 --- a/api/src/actors/database/shopping_carts.rs +++ b/api/src/actors/database/shopping_carts.rs @@ -11,8 +11,8 @@ use super::Result; pub enum Error { #[error("Can't create shopping cart")] CantCreate, - #[error("Can't find shopping cart does to lack of identity")] - NoIdentity, + #[error("Can't update shopping cart {0}")] + CantUpdate(ShoppingCartId), #[error("Shopping cart does not exists")] NotExists, #[error("Failed to load all shopping carts")] @@ -30,7 +30,7 @@ async_handler!(AllShoppingCarts, all_shopping_carts, Vec); pub async fn all_shopping_carts(_msg: AllShoppingCarts, pool: PgPool) -> Result> { sqlx::query_as( r#" -SELECT id, buyer_id, payment_method +SELECT id, buyer_id, payment_method, state FROM shopping_carts "#, ) @@ -56,12 +56,12 @@ pub async fn account_shopping_carts( ) -> Result> { sqlx::query_as( r#" -SELECT id, buyer_id, payment_method +SELECT id, buyer_id, payment_method, state FROM shopping_carts WHERE buyer_id = $1 "#, ) - .bind(msg.acocunt_id) + .bind(msg.account_id) .fetch_all(&pool) .await .map_err(|e| { @@ -84,7 +84,7 @@ async fn create_shopping_cart(msg: CreateShoppingCart, db: PgPool) -> Result Result { + sqlx::query_as( + r#" +UPDATE shopping_carts +SET buyer_id = $2 AND payment_method = $2 AND state = $4 +WHERE id = $1 +RETURNING id, buyer_id, payment_method, state + "#, + ) + .bind(msg.id) + .bind(msg.buyer_id) + .bind(msg.payment_method) + .bind(msg.state) + .fetch_one(&db) + .await + .map_err(|e| { + log::error!("{e:?}"); + super::Error::ShoppingCart(Error::CantUpdate(msg.id)) + }) +} + #[derive(actix::Message)] #[rtype(result = "Result")] pub struct FindShoppingCart { @@ -108,7 +140,7 @@ async_handler!(FindShoppingCart, find_shopping_cart, ShoppingCart); async fn find_shopping_cart(msg: FindShoppingCart, db: PgPool) -> Result { sqlx::query_as( r#" -SELECT id, buyer_id, payment_method +SELECT id, buyer_id, payment_method, state FROM shopping_carts WHERE id = $1 "#, diff --git a/api/src/main.rs b/api/src/main.rs index ce639a3..52f3250 100644 --- a/api/src/main.rs +++ b/api/src/main.rs @@ -161,7 +161,7 @@ async fn server(opts: ServerOpts) -> Result<()> { App::new() .wrap(Logger::default()) .wrap(actix_web::middleware::Compress::default()) - .wrap(actix_web::middleware::NormalizePath::default()) + .wrap(actix_web::middleware::NormalizePath::trim()) .wrap(SessionMiddleware::new( RedisActorSessionStore::new(redis_connection_string), secret_key.clone(), diff --git a/api/src/model.rs b/api/src/model.rs index a102a2b..71bebd8 100644 --- a/api/src/model.rs +++ b/api/src/model.rs @@ -54,6 +54,13 @@ pub enum PaymentMethod { PaymentOnTheSpot, } +#[derive(sqlx::Type, Copy, Clone, Debug, Display, Deserialize, Serialize)] +#[sqlx(rename_all = "lowercase")] +pub enum ShoppingCartState { + Active, + Closed, +} + #[derive(sqlx::Type, Serialize, Deserialize, Deref)] #[sqlx(transparent)] #[serde(transparent)] @@ -280,7 +287,7 @@ pub struct AccountOrder { pub status: OrderStatus, } -#[derive(sqlx::Type, Serialize, Deserialize, Deref)] +#[derive(sqlx::Type, Serialize, Deserialize, Copy, Clone, Deref)] #[sqlx(transparent)] #[serde(transparent)] pub struct OrderItemId(pub RecordId); @@ -294,7 +301,7 @@ pub struct OrderItem { pub quantity_unit: QuantityUnit, } -#[derive(sqlx::Type, Serialize, Deserialize, Deref)] +#[derive(sqlx::Type, Serialize, Deserialize, Copy, Clone, Deref, Display, Debug)] #[sqlx(transparent)] #[serde(transparent)] pub struct ShoppingCartId(pub RecordId); @@ -304,9 +311,10 @@ pub struct ShoppingCart { pub id: ShoppingCartId, pub buyer_id: AccountId, pub payment_method: PaymentMethod, + pub state: ShoppingCartState, } -#[derive(sqlx::Type, Serialize, Deserialize, Deref)] +#[derive(sqlx::Type, Serialize, Deserialize, Copy, Clone, Deref, Display, Debug)] #[sqlx(transparent)] #[serde(transparent)] pub struct ShoppingCartItemId(pub RecordId); diff --git a/db/migrate/202204161233_add_cart_state.sql b/db/migrate/202204161233_add_cart_state.sql new file mode 100644 index 0000000..ba9016a --- /dev/null +++ b/db/migrate/202204161233_add_cart_state.sql @@ -0,0 +1,10 @@ +CREATE TYPE "ShoppingCartState" AS ENUM ( + 'active', + 'closed' +); + +ALTER TABLE shopping_carts +ADD COLUMN "state" "ShoppingCartState" NOT NULL DEFAULT 'active'; + +ALTER TABLE shopping_carts +ADD CONSTRAINT single_active_cart UNIQUE (buyer_id, "state");