Logic for add item
This commit is contained in:
parent
5d757e84d5
commit
39d65f5e12
15
Cargo.lock
generated
15
Cargo.lock
generated
@ -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"
|
||||||
|
@ -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" }
|
||||||
|
82
api/src/actors/cart_manager.rs
Normal file
82
api/src/actors/cart_manager.rs
Normal 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),
|
||||||
|
}
|
||||||
|
}
|
@ -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 {
|
||||||
|
@ -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
|
||||||
|
@ -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#"
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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#"
|
||||||
|
@ -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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -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(
|
||||||
|
@ -1 +1,2 @@
|
|||||||
|
pub mod cart_manager;
|
||||||
pub mod database;
|
pub mod database;
|
||||||
|
@ -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>;
|
||||||
|
@ -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
27
api/src/utils.rs
Normal 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user