Logic for add item

This commit is contained in:
eraden 2022-04-16 18:57:37 +02:00
parent 5d757e84d5
commit 39d65f5e12
15 changed files with 260 additions and 86 deletions

15
Cargo.lock generated
View File

@ -963,6 +963,8 @@ dependencies = [
"chrono", "chrono",
"derive_more", "derive_more",
"dotenv", "dotenv",
"futures",
"futures-util",
"gumdrop", "gumdrop",
"log", "log",
"parking_lot 0.12.0", "parking_lot 0.12.0",
@ -974,6 +976,7 @@ dependencies = [
"sqlx", "sqlx",
"tera", "tera",
"thiserror", "thiserror",
"tokio 1.17.0",
"toml", "toml",
"tracing", "tracing",
"uuid", "uuid",
@ -3636,9 +3639,21 @@ dependencies = [
"pin-project-lite 0.2.8", "pin-project-lite 0.2.8",
"signal-hook-registry", "signal-hook-registry",
"socket2 0.4.4", "socket2 0.4.4",
"tokio-macros",
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "tokio-macros"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "tokio-rustls" name = "tokio-rustls"
version = "0.22.0" version = "0.22.0"

View File

@ -47,3 +47,7 @@ parking_lot = { version = "0.12.0" }
password-hash = { version = "0.4.0", features = ["alloc"] } password-hash = { version = "0.4.0", features = ["alloc"] }
argon2 = { version = "0.4.0", features = ["parallel", "password-hash"] } argon2 = { version = "0.4.0", features = ["parallel", "password-hash"] }
rand_core = { version = "0.6", features = ["std"] } rand_core = { version = "0.6", features = ["std"] }
tokio = { version = "1.17.0", features = ["full"] }
futures = { version = "0.3.21" }
futures-util = { version = "0.3.21" }

View File

@ -0,0 +1,82 @@
use crate::database::Database;
use crate::model::{
AccountId, ProductId, Quantity, QuantityUnit, ShoppingCartItem, ShoppingCartState,
};
use crate::{cart_async_handler, database};
use actix::{Actor, Addr, Context, Handler, Message, ResponseActFuture, WrapFuture};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("System can't ensure shopping cart existence")]
ShoppingCartFailed,
#[error("Shopping cart is not available for unknown reason")]
CartNotAvailable,
#[error("Failed to add item to cart")]
CantAddItem,
#[error("{0}")]
Db(#[from] database::Error),
}
pub type Result<T> = std::result::Result<T, Error>;
pub struct CartManager {
db: Addr<Database>,
}
impl Actor for CartManager {
type Context = Context<Self>;
}
impl CartManager {
pub fn new(db: Addr<Database>) -> Self {
Self { db }
}
}
#[derive(Message)]
#[rtype(result = "Result<ShoppingCartItem>")]
pub struct AddItem {
pub buyer_id: AccountId,
pub product_id: ProductId,
pub quantity: Quantity,
pub quantity_unit: QuantityUnit,
}
cart_async_handler!(AddItem, add_item, ShoppingCartItem);
async fn add_item(msg: AddItem, db: Addr<Database>) -> Result<ShoppingCartItem> {
match db.send(database::EnsureActiveShoppingCart { buyer_id: msg.buyer_id }).await {
Ok(Ok(_)) => {}
_ => return Err(Error::ShoppingCartFailed),
};
let cart = match db
.send(database::AccountShoppingCarts {
account_id: msg.buyer_id,
state: Some(ShoppingCartState::Active),
})
.await
.map(|res| match res {
Ok(mut v) if !v.is_empty() => Ok(v.remove(0)),
Err(e) => return Err(Error::Db(e)),
_ => Err(Error::CartNotAvailable),
}) {
Ok(Ok(cart)) => cart,
Ok(Err(e)) => {
log::error!("{e:?}");
return Err(e);
}
_ => return Err(Error::CartNotAvailable),
};
match db
.send(database::CreateShoppingCartItem {
product_id: msg.product_id,
shopping_cart_id: cart.id,
quantity: msg.quantity,
quantity_unit: msg.quantity_unit,
})
.await
{
Ok(res) => res.map_err(Into::into),
_ => Err(Error::CantAddItem),
}
}

View File

@ -9,27 +9,13 @@ pub use shopping_cart_items::*;
pub use shopping_carts::*; pub use shopping_carts::*;
pub use stocks::*; pub use stocks::*;
mod account_orders; pub mod account_orders;
mod accounts; pub mod accounts;
mod order_items; pub mod order_items;
mod products; pub mod products;
mod shopping_cart_items; pub mod shopping_cart_items;
mod shopping_carts; pub mod shopping_carts;
mod stocks; pub mod stocks;
#[macro_export]
macro_rules! async_handler {
($msg: ty, $async: ident, $res: ty) => {
impl Handler<$msg> for Database {
type Result = ResponseActFuture<Self, Result<$res>>;
fn handle(&mut self, msg: $msg, _ctx: &mut Self::Context) -> Self::Result {
let pool = self.pool.clone();
Box::pin(async { $async(msg, pool).await }.into_actor(self))
}
}
};
}
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum Error { pub enum Error {

View File

@ -1,4 +1,4 @@
use crate::async_handler; use crate::db_async_handler;
use actix::{Handler, ResponseActFuture, WrapFuture}; use actix::{Handler, ResponseActFuture, WrapFuture};
use sqlx::PgPool; use sqlx::PgPool;
@ -23,9 +23,12 @@ pub enum Error {
#[rtype(result = "Result<Vec<AccountOrder>>")] #[rtype(result = "Result<Vec<AccountOrder>>")]
pub struct AllAccountOrders; pub struct AllAccountOrders;
async_handler!(AllAccountOrders, all_account_orders, Vec<AccountOrder>); db_async_handler!(AllAccountOrders, all_account_orders, Vec<AccountOrder>);
pub async fn all_account_orders(_msg: AllAccountOrders, pool: PgPool) -> Result<Vec<AccountOrder>> { pub(crate) async fn all_account_orders(
_msg: AllAccountOrders,
pool: PgPool,
) -> Result<Vec<AccountOrder>> {
sqlx::query_as( sqlx::query_as(
r#" r#"
SELECT id, buyer_id, status SELECT id, buyer_id, status
@ -47,9 +50,12 @@ pub struct CreateAccountOrder {
pub status: OrderStatus, pub status: OrderStatus,
} }
async_handler!(CreateAccountOrder, create_account_order, AccountOrder); db_async_handler!(CreateAccountOrder, create_account_order, AccountOrder);
async fn create_account_order(msg: CreateAccountOrder, db: PgPool) -> Result<AccountOrder> { pub(crate) async fn create_account_order(
msg: CreateAccountOrder,
db: PgPool,
) -> Result<AccountOrder> {
sqlx::query_as( sqlx::query_as(
r#" r#"
INSERT INTO account_orders (buyer_id, status) INSERT INTO account_orders (buyer_id, status)
@ -73,9 +79,9 @@ pub struct FindAccountOrder {
pub id: AccountOrderId, pub id: AccountOrderId,
} }
async_handler!(FindAccountOrder, find_account_order, AccountOrder); db_async_handler!(FindAccountOrder, find_account_order, AccountOrder);
async fn find_account_order(msg: FindAccountOrder, db: PgPool) -> Result<AccountOrder> { pub(crate) async fn find_account_order(msg: FindAccountOrder, db: PgPool) -> Result<AccountOrder> {
sqlx::query_as( sqlx::query_as(
r#" r#"
SELECT id, buyer_id, status SELECT id, buyer_id, status

View File

@ -1,4 +1,4 @@
use crate::async_handler; use crate::db_async_handler;
use actix::{Handler, ResponseActFuture, WrapFuture}; use actix::{Handler, ResponseActFuture, WrapFuture};
use sqlx::PgPool; use sqlx::PgPool;
@ -23,9 +23,9 @@ pub enum Error {
#[rtype(result = "Result<Vec<FullAccount>>")] #[rtype(result = "Result<Vec<FullAccount>>")]
pub struct AllAccounts; pub struct AllAccounts;
async_handler!(AllAccounts, all_accounts, Vec<FullAccount>); db_async_handler!(AllAccounts, all_accounts, Vec<FullAccount>);
pub async fn all_accounts(_msg: AllAccounts, pool: PgPool) -> Result<Vec<FullAccount>> { pub(crate) async fn all_accounts(_msg: AllAccounts, pool: PgPool) -> Result<Vec<FullAccount>> {
sqlx::query_as( sqlx::query_as(
r#" r#"
SELECT id, email, login, pass_hash, role SELECT id, email, login, pass_hash, role
@ -49,9 +49,9 @@ pub struct CreateAccount {
pub role: Role, pub role: Role,
} }
async_handler!(CreateAccount, create_account, FullAccount); db_async_handler!(CreateAccount, create_account, FullAccount);
async fn create_account(msg: CreateAccount, db: PgPool) -> Result<FullAccount> { pub(crate) async fn create_account(msg: CreateAccount, db: PgPool) -> Result<FullAccount> {
sqlx::query_as( sqlx::query_as(
r#" r#"
INSERT INTO accounts (login, email, role, pass_hash) INSERT INTO accounts (login, email, role, pass_hash)
@ -77,9 +77,9 @@ pub struct FindAccount {
pub account_id: AccountId, pub account_id: AccountId,
} }
async_handler!(FindAccount, find_account, FullAccount); db_async_handler!(FindAccount, find_account, FullAccount);
async fn find_account(msg: FindAccount, db: PgPool) -> Result<FullAccount> { pub(crate) async fn find_account(msg: FindAccount, db: PgPool) -> Result<FullAccount> {
sqlx::query_as( sqlx::query_as(
r#" r#"
SELECT id, email, login, pass_hash, role SELECT id, email, login, pass_hash, role
@ -103,9 +103,9 @@ pub struct AccountByIdentity {
pub email: Option<Email>, pub email: Option<Email>,
} }
async_handler!(AccountByIdentity, account_by_identity, FullAccount); db_async_handler!(AccountByIdentity, account_by_identity, FullAccount);
async fn account_by_identity(msg: AccountByIdentity, db: PgPool) -> Result<FullAccount> { pub(crate) async fn account_by_identity(msg: AccountByIdentity, db: PgPool) -> Result<FullAccount> {
match (msg.login, msg.email) { match (msg.login, msg.email) {
(Some(login), None) => sqlx::query_as( (Some(login), None) => sqlx::query_as(
r#" r#"

View File

@ -1,4 +1,4 @@
use crate::async_handler; use crate::db_async_handler;
use actix::{Handler, ResponseActFuture, WrapFuture}; use actix::{Handler, ResponseActFuture, WrapFuture};
use sqlx::PgPool; use sqlx::PgPool;
@ -23,9 +23,9 @@ pub enum Error {
#[rtype(result = "Result<Vec<OrderItem>>")] #[rtype(result = "Result<Vec<OrderItem>>")]
pub struct AllOrderItems; pub struct AllOrderItems;
async_handler!(AllOrderItems, all_order_items, Vec<OrderItem>); db_async_handler!(AllOrderItems, all_order_items, Vec<OrderItem>);
pub async fn all_order_items(_msg: AllOrderItems, pool: PgPool) -> Result<Vec<OrderItem>> { pub(crate) async fn all_order_items(_msg: AllOrderItems, pool: PgPool) -> Result<Vec<OrderItem>> {
sqlx::query_as( sqlx::query_as(
r#" r#"
SELECT id, buyer_id, status SELECT id, buyer_id, status
@ -47,9 +47,9 @@ pub struct CreateOrderItem {
pub status: OrderStatus, pub status: OrderStatus,
} }
async_handler!(CreateOrderItem, create_order_item, OrderItem); db_async_handler!(CreateOrderItem, create_order_item, OrderItem);
async fn create_order_item(msg: CreateOrderItem, db: PgPool) -> Result<OrderItem> { pub(crate) async fn create_order_item(msg: CreateOrderItem, db: PgPool) -> Result<OrderItem> {
sqlx::query_as( sqlx::query_as(
r#" r#"
INSERT INTO order_items (buyer_id, status) INSERT INTO order_items (buyer_id, status)
@ -73,9 +73,9 @@ pub struct FindOrderItem {
pub id: OrderItemId, pub id: OrderItemId,
} }
async_handler!(FindOrderItem, find_order_item, OrderItem); db_async_handler!(FindOrderItem, find_order_item, OrderItem);
async fn find_order_item(msg: FindOrderItem, db: PgPool) -> Result<OrderItem> { pub(crate) async fn find_order_item(msg: FindOrderItem, db: PgPool) -> Result<OrderItem> {
sqlx::query_as( sqlx::query_as(
r#" r#"
SELECT id, buyer_id, status SELECT id, buyer_id, status

View File

@ -25,9 +25,9 @@ pub enum Error {
#[rtype(result = "Result<Vec<model::Product>>")] #[rtype(result = "Result<Vec<model::Product>>")]
pub struct AllProducts; pub struct AllProducts;
crate::async_handler!(AllProducts, all, Vec<Product>); crate::db_async_handler!(AllProducts, all, Vec<Product>);
async fn all(_msg: AllProducts, pool: PgPool) -> Result<Vec<model::Product>> { pub(crate) async fn all(_msg: AllProducts, pool: PgPool) -> Result<Vec<model::Product>> {
sqlx::query_as( sqlx::query_as(
r#" r#"
SELECT id, SELECT id,
@ -59,9 +59,9 @@ pub struct CreateProduct {
pub price_minor: PriceMinor, pub price_minor: PriceMinor,
} }
crate::async_handler!(CreateProduct, create_product, Product); crate::db_async_handler!(CreateProduct, create_product, Product);
async fn create_product(msg: CreateProduct, pool: PgPool) -> Result<model::Product> { pub(crate) async fn create_product(msg: CreateProduct, pool: PgPool) -> Result<model::Product> {
sqlx::query_as( sqlx::query_as(
r#" r#"
INSERT INTO products (name, short_description, long_description, category, price_major, price_minor) INSERT INTO products (name, short_description, long_description, category, price_major, price_minor)
@ -101,9 +101,9 @@ pub struct UpdateProduct {
pub price_minor: PriceMinor, pub price_minor: PriceMinor,
} }
crate::async_handler!(UpdateProduct, update_product, Product); crate::db_async_handler!(UpdateProduct, update_product, Product);
async fn update_product(msg: UpdateProduct, pool: PgPool) -> Result<model::Product> { pub(crate) async fn update_product(msg: UpdateProduct, pool: PgPool) -> Result<model::Product> {
sqlx::query_as( sqlx::query_as(
r#" r#"
UPDATE products UPDATE products
@ -144,9 +144,9 @@ pub struct DeleteProduct {
pub product_id: ProductId, pub product_id: ProductId,
} }
crate::async_handler!(DeleteProduct, delete_product, Option<model::Product>); crate::db_async_handler!(DeleteProduct, delete_product, Option<model::Product>);
async fn delete_product(msg: DeleteProduct, pool: PgPool) -> Result<Option<Product>> { pub(crate) async fn delete_product(msg: DeleteProduct, pool: PgPool) -> Result<Option<Product>> {
sqlx::query_as( sqlx::query_as(
r#" r#"
DELETE FROM products DELETE FROM products

View File

@ -1,4 +1,4 @@
use crate::async_handler; use crate::db_async_handler;
use actix::{Handler, ResponseActFuture, WrapFuture}; use actix::{Handler, ResponseActFuture, WrapFuture};
use sqlx::PgPool; use sqlx::PgPool;
@ -27,9 +27,9 @@ pub enum Error {
#[rtype(result = "Result<Vec<ShoppingCartItem>>")] #[rtype(result = "Result<Vec<ShoppingCartItem>>")]
pub struct AllShoppingCartItems; pub struct AllShoppingCartItems;
async_handler!(AllShoppingCartItems, all_shopping_cart_items, Vec<ShoppingCartItem>); db_async_handler!(AllShoppingCartItems, all_shopping_cart_items, Vec<ShoppingCartItem>);
pub async fn all_shopping_cart_items( pub(crate) async fn all_shopping_cart_items(
_msg: AllShoppingCartItems, _msg: AllShoppingCartItems,
pool: PgPool, pool: PgPool,
) -> Result<Vec<ShoppingCartItem>> { ) -> Result<Vec<ShoppingCartItem>> {
@ -53,9 +53,9 @@ pub struct AccountShoppingCartItems {
pub account_id: AccountId, pub account_id: AccountId,
} }
async_handler!(AccountShoppingCartItems, account_shopping_cart_items, Vec<ShoppingCartItem>); db_async_handler!(AccountShoppingCartItems, account_shopping_cart_items, Vec<ShoppingCartItem>);
pub async fn account_shopping_cart_items( pub(crate) async fn account_shopping_cart_items(
msg: AccountShoppingCartItems, msg: AccountShoppingCartItems,
pool: PgPool, pool: PgPool,
) -> Result<Vec<ShoppingCartItem>> { ) -> Result<Vec<ShoppingCartItem>> {
@ -84,9 +84,9 @@ pub struct CreateShoppingCartItem {
pub quantity_unit: QuantityUnit, pub quantity_unit: QuantityUnit,
} }
async_handler!(CreateShoppingCartItem, create_shopping_cart_item, ShoppingCartItem); db_async_handler!(CreateShoppingCartItem, create_shopping_cart_item, ShoppingCartItem);
async fn create_shopping_cart_item( pub(crate) async fn create_shopping_cart_item(
msg: CreateShoppingCartItem, msg: CreateShoppingCartItem,
db: PgPool, db: PgPool,
) -> Result<ShoppingCartItem> { ) -> Result<ShoppingCartItem> {
@ -119,9 +119,9 @@ pub struct UpdateShoppingCartItem {
pub quantity_unit: QuantityUnit, pub quantity_unit: QuantityUnit,
} }
async_handler!(UpdateShoppingCartItem, update_shopping_cart_item, ShoppingCartItem); db_async_handler!(UpdateShoppingCartItem, update_shopping_cart_item, ShoppingCartItem);
async fn update_shopping_cart_item( pub(crate) async fn update_shopping_cart_item(
msg: UpdateShoppingCartItem, msg: UpdateShoppingCartItem,
db: PgPool, db: PgPool,
) -> Result<ShoppingCartItem> { ) -> Result<ShoppingCartItem> {
@ -152,9 +152,9 @@ pub struct FindShoppingCartItem {
pub id: ShoppingCartItemId, pub id: ShoppingCartItemId,
} }
async_handler!(FindShoppingCartItem, find_shopping_cart_item, ShoppingCartItem); db_async_handler!(FindShoppingCartItem, find_shopping_cart_item, ShoppingCartItem);
async fn find_shopping_cart_item( pub(crate) async fn find_shopping_cart_item(
msg: FindShoppingCartItem, msg: FindShoppingCartItem,
db: PgPool, db: PgPool,
) -> Result<ShoppingCartItem> { ) -> Result<ShoppingCartItem> {
@ -180,9 +180,9 @@ pub struct CartItems {
pub shopping_cart_id: ShoppingCartId, pub shopping_cart_id: ShoppingCartId,
} }
async_handler!(CartItems, cart_items, Vec<ShoppingCartItem>); db_async_handler!(CartItems, cart_items, Vec<ShoppingCartItem>);
async fn cart_items(msg: CartItems, pool: PgPool) -> Result<Vec<ShoppingCartItem>> { pub(crate) async fn cart_items(msg: CartItems, pool: PgPool) -> Result<Vec<ShoppingCartItem>> {
let shopping_cart_id = msg.shopping_cart_id; let shopping_cart_id = msg.shopping_cart_id;
sqlx::query_as( sqlx::query_as(
r#" r#"

View File

@ -1,4 +1,4 @@
use crate::async_handler; use crate::db_async_handler;
use actix::{Handler, ResponseActFuture, WrapFuture}; use actix::{Handler, ResponseActFuture, WrapFuture};
use sqlx::PgPool; use sqlx::PgPool;
@ -25,9 +25,12 @@ pub enum Error {
#[rtype(result = "Result<Vec<ShoppingCart>>")] #[rtype(result = "Result<Vec<ShoppingCart>>")]
pub struct AllShoppingCarts; pub struct AllShoppingCarts;
async_handler!(AllShoppingCarts, all_shopping_carts, Vec<ShoppingCart>); db_async_handler!(AllShoppingCarts, all_shopping_carts, Vec<ShoppingCart>);
pub async fn all_shopping_carts(_msg: AllShoppingCarts, pool: PgPool) -> Result<Vec<ShoppingCart>> { pub(crate) async fn all_shopping_carts(
_msg: AllShoppingCarts,
pool: PgPool,
) -> Result<Vec<ShoppingCart>> {
sqlx::query_as( sqlx::query_as(
r#" r#"
SELECT id, buyer_id, payment_method, state SELECT id, buyer_id, payment_method, state
@ -46,22 +49,35 @@ FROM shopping_carts
#[rtype(result = "Result<Vec<ShoppingCart>>")] #[rtype(result = "Result<Vec<ShoppingCart>>")]
pub struct AccountShoppingCarts { pub struct AccountShoppingCarts {
pub account_id: AccountId, pub account_id: AccountId,
pub state: Option<ShoppingCartState>,
} }
async_handler!(AccountShoppingCarts, account_shopping_carts, Vec<ShoppingCart>); db_async_handler!(AccountShoppingCarts, account_shopping_carts, Vec<ShoppingCart>);
pub async fn account_shopping_carts( pub(crate) async fn account_shopping_carts(
msg: AccountShoppingCarts, msg: AccountShoppingCarts,
pool: PgPool, pool: PgPool,
) -> Result<Vec<ShoppingCart>> { ) -> Result<Vec<ShoppingCart>> {
sqlx::query_as( if let Some(state) = msg.state {
r#" sqlx::query_as(
r#"
SELECT id, buyer_id, payment_method, state
FROM shopping_carts
WHERE buyer_id = $1 AND state = $2
"#,
)
.bind(msg.account_id)
.bind(state)
} else {
sqlx::query_as(
r#"
SELECT id, buyer_id, payment_method, state SELECT id, buyer_id, payment_method, state
FROM shopping_carts FROM shopping_carts
WHERE buyer_id = $1 WHERE buyer_id = $1
"#, "#,
) )
.bind(msg.account_id) .bind(msg.account_id)
}
.fetch_all(&pool) .fetch_all(&pool)
.await .await
.map_err(|e| { .map_err(|e| {
@ -77,9 +93,12 @@ pub struct CreateShoppingCart {
pub payment_method: PaymentMethod, pub payment_method: PaymentMethod,
} }
async_handler!(CreateShoppingCart, create_shopping_cart, ShoppingCart); db_async_handler!(CreateShoppingCart, create_shopping_cart, ShoppingCart);
async fn create_shopping_cart(msg: CreateShoppingCart, db: PgPool) -> Result<ShoppingCart> { pub(crate) async fn create_shopping_cart(
msg: CreateShoppingCart,
db: PgPool,
) -> Result<ShoppingCart> {
sqlx::query_as( sqlx::query_as(
r#" r#"
INSERT INTO shopping_carts (buyer_id, payment_method) INSERT INTO shopping_carts (buyer_id, payment_method)
@ -106,9 +125,12 @@ pub struct UpdateShoppingCart {
pub state: ShoppingCartState, pub state: ShoppingCartState,
} }
async_handler!(UpdateShoppingCart, update_shopping_cart, ShoppingCart); db_async_handler!(UpdateShoppingCart, update_shopping_cart, ShoppingCart);
async fn update_shopping_cart(msg: UpdateShoppingCart, db: PgPool) -> Result<ShoppingCart> { pub(crate) async fn update_shopping_cart(
msg: UpdateShoppingCart,
db: PgPool,
) -> Result<ShoppingCart> {
sqlx::query_as( sqlx::query_as(
r#" r#"
UPDATE shopping_carts UPDATE shopping_carts
@ -135,9 +157,9 @@ pub struct FindShoppingCart {
pub id: ShoppingCartId, pub id: ShoppingCartId,
} }
async_handler!(FindShoppingCart, find_shopping_cart, ShoppingCart); db_async_handler!(FindShoppingCart, find_shopping_cart, ShoppingCart);
async fn find_shopping_cart(msg: FindShoppingCart, db: PgPool) -> Result<ShoppingCart> { pub(crate) async fn find_shopping_cart(msg: FindShoppingCart, db: PgPool) -> Result<ShoppingCart> {
sqlx::query_as( sqlx::query_as(
r#" r#"
SELECT id, buyer_id, payment_method, state SELECT id, buyer_id, payment_method, state
@ -153,3 +175,33 @@ WHERE id = $1
super::Error::ShoppingCart(Error::NotExists) super::Error::ShoppingCart(Error::NotExists)
}) })
} }
#[derive(actix::Message)]
#[rtype(result = "Result<Option<ShoppingCart>>")]
pub struct EnsureActiveShoppingCart {
pub buyer_id: AccountId,
}
db_async_handler!(EnsureActiveShoppingCart, ensure_active_shopping_cart, Option<ShoppingCart>);
pub(crate) async fn ensure_active_shopping_cart(
msg: EnsureActiveShoppingCart,
pool: PgPool,
) -> Result<Option<ShoppingCart>> {
sqlx::query_as(
r#"
INSERT INTO shopping_carts (buyer_id, state)
VALUES ($1, 'active')
ON CONFLICT
DO NOTHING
RETURNING id, buyer_id, payment_method, state;
"#,
)
.bind(msg.buyer_id)
.fetch_optional(&pool)
.await
.map_err(|e| {
log::error!("{e:?}");
super::Error::ShoppingCart(Error::NotExists)
})
}

View File

@ -22,7 +22,7 @@ pub enum Error {
#[rtype(result = "Result<Vec<model::Stock>>")] #[rtype(result = "Result<Vec<model::Stock>>")]
pub struct AllStocks; pub struct AllStocks;
crate::async_handler!(AllStocks, all_stocks, Vec<Stock>); crate::db_async_handler!(AllStocks, all_stocks, Vec<Stock>);
async fn all_stocks(_msg: AllStocks, pool: PgPool) -> Result<Vec<model::Stock>> { async fn all_stocks(_msg: AllStocks, pool: PgPool) -> Result<Vec<model::Stock>> {
sqlx::query_as( sqlx::query_as(
@ -47,7 +47,7 @@ pub struct CreateStock {
pub quantity_unit: QuantityUnit, pub quantity_unit: QuantityUnit,
} }
crate::async_handler!(CreateStock, create_stock, Stock); crate::db_async_handler!(CreateStock, create_stock, Stock);
async fn create_stock(msg: CreateStock, pool: PgPool) -> Result<model::Stock> { async fn create_stock(msg: CreateStock, pool: PgPool) -> Result<model::Stock> {
sqlx::query_as( sqlx::query_as(
@ -77,7 +77,7 @@ pub struct UpdateStock {
pub quantity_unit: QuantityUnit, pub quantity_unit: QuantityUnit,
} }
crate::async_handler!(UpdateStock, update_stock, Stock); crate::db_async_handler!(UpdateStock, update_stock, Stock);
async fn update_stock(msg: UpdateStock, pool: PgPool) -> Result<model::Stock> { async fn update_stock(msg: UpdateStock, pool: PgPool) -> Result<model::Stock> {
sqlx::query_as( sqlx::query_as(
@ -108,7 +108,7 @@ pub struct DeleteStock {
pub stock_id: StockId, pub stock_id: StockId,
} }
crate::async_handler!(DeleteStock, delete_stock, Option<model::Stock>); crate::db_async_handler!(DeleteStock, delete_stock, Option<model::Stock>);
async fn delete_stock(msg: DeleteStock, pool: PgPool) -> Result<Option<Stock>> { async fn delete_stock(msg: DeleteStock, pool: PgPool) -> Result<Option<Stock>> {
sqlx::query_as( sqlx::query_as(

View File

@ -1 +1,2 @@
pub mod cart_manager;
pub mod database; pub mod database;

View File

@ -18,6 +18,7 @@ pub mod actors;
pub mod logic; pub mod logic;
pub mod model; pub mod model;
pub mod routes; pub mod routes;
mod utils;
trait ResolveDbUrl { trait ResolveDbUrl {
fn own_db_url(&self) -> Option<String>; fn own_db_url(&self) -> Option<String>;

View File

@ -198,10 +198,10 @@ impl PartialEq<PasswordConfirmation> for Password {
} }
} }
#[derive(sqlx::Type, Serialize, Deserialize, Deref)] #[derive(sqlx::Type, Serialize, Deserialize, Copy, Clone, Deref, Display)]
#[sqlx(transparent)] #[sqlx(transparent)]
#[serde(transparent)] #[serde(transparent)]
pub struct AccountId(pub RecordId); pub struct AccountId(RecordId);
#[derive(sqlx::FromRow, Serialize, Deserialize)] #[derive(sqlx::FromRow, Serialize, Deserialize)]
pub struct FullAccount { pub struct FullAccount {

27
api/src/utils.rs Normal file
View File

@ -0,0 +1,27 @@
#[macro_export]
macro_rules! db_async_handler {
($msg: ty, $async: ident, $res: ty) => {
impl Handler<$msg> for Database {
type Result = ResponseActFuture<Self, Result<$res>>;
fn handle(&mut self, msg: $msg, _ctx: &mut Self::Context) -> Self::Result {
let pool = self.pool.clone();
Box::pin(async { $async(msg, pool).await }.into_actor(self))
}
}
};
}
#[macro_export]
macro_rules! cart_async_handler {
($msg: ty, $async: ident, $res: ty) => {
impl Handler<$msg> for CartManager {
type Result = ResponseActFuture<Self, Result<$res>>;
fn handle(&mut self, msg: $msg, _ctx: &mut Self::Context) -> Self::Result {
let db = self.db.clone();
Box::pin(async { $async(msg, db).await }.into_actor(self))
}
}
};
}