bazzar/actors/database_manager/src/order_items.rs

330 lines
8.4 KiB
Rust

#[cfg(feature = "dummy")]
use fake::Fake;
use model::*;
use super::Result;
use crate::db_async_handler;
#[derive(Debug, Copy, Clone, PartialEq, serde::Serialize, thiserror::Error)]
pub enum Error {
#[error("Can't create order item")]
CantCreate,
#[error("Can't find order item doe to lack of identity")]
NoIdentity,
#[error("Order item does not exists")]
NotExists,
#[error("Failed to load all order items")]
All,
#[error("Failed to load order items")]
OrderItems,
}
#[derive(actix::Message)]
#[rtype(result = "Result<Vec<OrderItem>>")]
pub struct AllOrderItems;
db_async_handler!(
AllOrderItems,
all_order_items,
Vec<OrderItem>,
inner_all_order_items
);
pub(crate) async fn all_order_items(
_msg: AllOrderItems,
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
) -> Result<Vec<OrderItem>> {
sqlx::query_as(
r#"
SELECT id, product_id, order_id, quantity, quantity_unit
FROM order_items
ORDER BY id DESC
"#,
)
.fetch_all(t)
.await
.map_err(|e| {
tracing::error!("{e:?}");
Error::All.into()
})
}
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
#[derive(actix::Message)]
#[rtype(result = "Result<OrderItem>")]
pub struct CreateOrderItem {
pub product_id: ProductId,
pub order_id: OrderId,
pub quantity: Quantity,
pub quantity_unit: QuantityUnit,
}
db_async_handler!(
CreateOrderItem,
create_order_item,
OrderItem,
inner_create_order_item
);
pub(crate) async fn create_order_item(
msg: CreateOrderItem,
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
) -> Result<OrderItem> {
sqlx::query_as(
r#"
INSERT INTO order_items (product_id, order_id, quantity, quantity_unit)
VALUES ($1, $2, $3, $4)
RETURNING id, product_id, order_id, quantity, quantity_unit
"#,
)
.bind(msg.product_id)
.bind(msg.order_id)
.bind(msg.quantity)
.bind(msg.quantity_unit)
.fetch_one(t)
.await
.map_err(|e| {
tracing::error!("{e:?}");
dbg!(e);
super::Error::OrderItem(Error::CantCreate)
})
}
#[derive(actix::Message)]
#[rtype(result = "Result<OrderItem>")]
pub struct FindOrderItem {
pub id: OrderItemId,
}
db_async_handler!(FindOrderItem, find_order_item, OrderItem, inner_find_order);
pub(crate) async fn find_order_item(
msg: FindOrderItem,
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
) -> Result<OrderItem> {
sqlx::query_as(
r#"
SELECT id, product_id, order_id, quantity, quantity_unit
FROM order_items
WHERE id = $1
"#,
)
.bind(msg.id)
.fetch_one(t)
.await
.map_err(|e| {
tracing::error!("{e:?}");
Error::NotExists.into()
})
}
#[derive(actix::Message)]
#[rtype(result = "Result<Vec<OrderItem>>")]
pub struct OrderItems {
pub order_id: OrderId,
}
db_async_handler!(OrderItems, order_items, Vec<OrderItem>, inner_order_items);
pub(crate) async fn order_items(
msg: OrderItems,
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
) -> Result<Vec<OrderItem>> {
sqlx::query_as(
r#"
SELECT id, product_id, order_id, quantity, quantity_unit
FROM order_items
WHERE order_id = $1
ORDER BY id DESC
"#,
)
.bind(msg.order_id)
.fetch_all(t)
.await
.map_err(|e| {
tracing::error!("{e:?}");
Error::OrderItems.into()
})
}
#[cfg(test)]
mod tests {
use config::UpdateConfig;
use fake::Fake;
use model::*;
use uuid::Uuid;
pub struct NoOpts;
impl UpdateConfig for NoOpts {}
use crate::*;
async fn test_order_address(
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
) -> model::OrderAddress {
use fake::faker::address::en;
let zip: String = en::ZipCode().fake();
create_order_address(
CreateOrderAddress {
name: Default::default(),
email: Default::default(),
street: Default::default(),
city: Default::default(),
country: Default::default(),
zip: Zip::new(zip),
phone: Default::default(),
},
t,
)
.await
.unwrap()
}
async fn test_account(t: &mut sqlx::Transaction<'_, sqlx::Postgres>) -> FullAccount {
use fake::faker::internet::en;
let login: String = en::Username().fake();
let email: String = en::FreeEmail().fake();
let hash: String = en::Password(11..20).fake();
create_account(
CreateAccount {
email: Email::new(email),
login: Login::new(login),
pass_hash: PassHash::new(hash),
role: Role::Admin,
},
t,
)
.await
.unwrap()
}
async fn test_shopping_cart(
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
buyer_id: AccountId,
) -> ShoppingCart {
let cart = create_shopping_cart(
CreateShoppingCart {
buyer_id,
payment_method: PaymentMethod::PaymentOnTheSpot,
},
&mut *t,
)
.await
.unwrap();
update_shopping_cart(
UpdateShoppingCart {
id: cart.id,
buyer_id: cart.buyer_id,
payment_method: cart.payment_method,
state: ShoppingCartState::Active,
checkout_notes: None,
},
&mut *t,
)
.await
.unwrap()
}
async fn test_order(t: &mut sqlx::Transaction<'_, sqlx::Postgres>) -> Order {
let buyer_id = test_account(t).await.id;
crate::create_order(
CreateOrder {
buyer_id,
items: vec![],
shopping_cart_id: Some(test_shopping_cart(t, buyer_id).await.id),
checkout_notes: None,
delivery_address_id: test_order_address(t).await.id,
},
t,
)
.await
.unwrap()
}
async fn test_product(t: &mut sqlx::Transaction<'_, sqlx::Postgres>) -> Product {
crate::create_product(
CreateProduct {
name: ProductName::new(format!("{}", Uuid::new_v4())),
short_description: ProductShortDesc::new(format!("{}", Uuid::new_v4())),
long_description: ProductLongDesc::new(format!("{}", Uuid::new_v4())),
category: None,
price: Price::from_u32(4688),
deliver_days_flag: Day::Friday | Day::Saturday | Day::Sunday,
},
t,
)
.await
.unwrap()
}
async fn test_order_item(
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
order_id: Option<OrderId>,
) -> OrderItem {
let order_id = match order_id {
Some(id) => id,
_ => test_order(&mut *t).await.id,
};
let product_id = test_product(&mut *t).await.id;
super::create_order_item(
CreateOrderItem {
product_id,
order_id,
quantity: Default::default(),
quantity_unit: QuantityUnit::Gram,
},
t,
)
.await
.unwrap()
}
#[actix::test]
async fn create() {
testx::db_t!(t);
test_order_item(&mut t, None).await;
testx::db_rollback!(t);
}
#[actix::test]
async fn order_items() {
testx::db_t!(t);
let order1 = test_order(&mut t).await;
test_order_item(&mut t, Some(order1.id)).await;
test_order_item(&mut t, Some(order1.id)).await;
test_order_item(&mut t, Some(order1.id)).await;
let mut expected = vec![];
let order2 = test_order(&mut t).await;
let item1 = test_order_item(&mut t, Some(order2.id)).await;
let item2 = test_order_item(&mut t, Some(order2.id)).await;
let item3 = test_order_item(&mut t, Some(order2.id)).await;
expected.push(item3);
expected.push(item2);
expected.push(item1);
let order3 = test_order(&mut t).await;
test_order_item(&mut t, Some(order3.id)).await;
test_order_item(&mut t, Some(order3.id)).await;
test_order_item(&mut t, Some(order3.id)).await;
let loaded = super::order_items(
OrderItems {
order_id: order2.id,
},
&mut t,
)
.await;
testx::db_rollback!(t);
assert_eq!(loaded, Ok(expected));
}
}