From 7bec8534e48c45a326b640454923d97e9a125f5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Wo=C5=BAniak?= Date: Sat, 12 Nov 2022 21:58:08 +0100 Subject: [PATCH] Load single product --- crates/channels/src/stocks/load.rs | 4 +- crates/channels/src/stocks/mod.rs | 2 + crates/stock_manager/src/actions/load.rs | 232 +++++++++++++---------- crates/stock_manager/src/actions/mod.rs | 26 +++ 4 files changed, 160 insertions(+), 104 deletions(-) diff --git a/crates/channels/src/stocks/load.rs b/crates/channels/src/stocks/load.rs index 270141c..8ee351d 100644 --- a/crates/channels/src/stocks/load.rs +++ b/crates/channels/src/stocks/load.rs @@ -10,9 +10,7 @@ pub mod detailed_product { #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct Output2 { - pub product: model::Product, - pub stocks: Vec, - pub photos: Vec, + pub product: DetailedProduct, } pub type Output = Result; diff --git a/crates/channels/src/stocks/mod.rs b/crates/channels/src/stocks/mod.rs index d8b6ef5..f8139f5 100644 --- a/crates/channels/src/stocks/mod.rs +++ b/crates/channels/src/stocks/mod.rs @@ -18,6 +18,8 @@ pub static CLIENT_NAME: &str = "stocks"; pub enum Error { #[error("Something went wrong")] InternalServerError, + #[error("Failed to load product {0:?}")] + ProductNotFound(ProductId), #[error("Failed to load products")] Products, #[error("Failed to load products for products {0:?}")] diff --git a/crates/stock_manager/src/actions/load.rs b/crates/stock_manager/src/actions/load.rs index bab701c..c725495 100644 --- a/crates/stock_manager/src/actions/load.rs +++ b/crates/stock_manager/src/actions/load.rs @@ -1,21 +1,67 @@ -use std::collections::HashMap; - -use channels::stocks::{detailed_product, detailed_products}; +use channels::stocks::{detailed_product, detailed_products, Error}; use channels::AsyncClient; use config::SharedAppConfig; use db_utils::PgT; -use model::v2::{DetailedProduct, DetailedProductVariant, Product, ProductVariant}; use model::Limit; use crate::db::{Database, PhotosForProductVariants, ProductVariantsStock, ProductsVariants}; +use crate::{begin_t, dbm_run}; pub async fn detailed_product( - _input: detailed_product::Input, - _db: Database, + input: detailed_product::Input, + db: Database, _mqtt: AsyncClient, _config: SharedAppConfig, ) -> detailed_product::Output { - todo!() + let mut t = begin_t!(db, Error::InternalServerError); + + let res = inner_detailed_product(input, &mut t, Some(_mqtt), Some(_config)).await; + + t.commit().await.ok(); + + res +} + +async fn inner_detailed_product( + input: detailed_product::Input, + t: &mut PgT<'_>, + _mqtt: Option, + _config: Option, +) -> detailed_product::Output { + let dbm = crate::db::FindProduct { + product_id: input.product_id, + }; + let product = dbm_run!(dbm, &mut *t, Error::ProductNotFound(input.product_id)); + let dbm = ProductsVariants { + product_ids: vec![input.product_id], + limit: Some(10.into()), + offset: Some(0.into()), + }; + let variants = dbm_run!(dbm, &mut *t, Error::ProductVariants(vec![input.product_id])); + let dbm = ProductVariantsStock { + product_variant_ids: variants.iter().map(|p| p.id).collect(), + }; + let stocks = dbm_run!( + dbm, + t, + Error::VariantStocks(variants.into_iter().map(|p| p.id).collect()) + ); + let dbm = PhotosForProductVariants { + product_variant_ids: variants.iter().map(|p| p.id).collect(), + }; + let photos = dbm_run!( + dbm, + t, + Error::VariantPhotos(variants.into_iter().map(|p| p.id).collect()) + ); + + let mut variants = utils::vec_to_hash_vec(variants, 10, |p| p.product_id); + let mut stocks = utils::vec_to_hash_vec(stocks, 10, |s| s.product_variant_id); + let mut photos = utils::vec_to_hash_vec(photos, 10, |p| p.product_variant_id); + + let product = utils::map_product(product, &mut variants, &mut stocks, &mut photos); + + Ok(detailed_product::Output2 { product }) } pub async fn detailed_products( @@ -24,13 +70,7 @@ pub async fn detailed_products( _mqtt: AsyncClient, _config: SharedAppConfig, ) -> detailed_products::Output { - let mut t = match db.pool().begin().await { - Err(e) => { - tracing::error!("{}", e); - return Err(detailed_products::Error::InternalServerError); - } - Ok(t) => t, - }; + let mut t = begin_t!(db, Error::InternalServerError); let res = inner_detailed_products(input, &mut t, Some(_mqtt), Some(_config)).await; @@ -49,27 +89,17 @@ async fn inner_detailed_products( limit: input.limit, offset: input.offset, }; - let products = match dbm.run(&mut *t).await { - Ok(products) => products, - Err(e) => { - tracing::error!("{}", e); - return Err(detailed_products::Error::Products); - } - }; + let products = dbm_run!(dbm, &mut *t, Error::Products); let dbm = ProductsVariants { product_ids: products.iter().map(|p| p.id).collect(), limit: Some(Limit::from_u32(products.len() as u32 * 10)), offset: Some(0.into()), }; - let variants = match dbm.run(&mut *t).await { - Ok(variants) => variants, - Err(e) => { - tracing::error!("{}", e); - return Err(detailed_products::Error::ProductVariants( - products.into_iter().map(|p| p.id).collect(), - )); - } - }; + let variants = dbm_run!( + dbm, + &mut *t, + Error::ProductVariants(products.into_iter().map(|p| p.id).collect(),) + ); let dbm = ProductVariantsStock { product_variant_ids: variants.iter().map(|p| p.id).collect(), }; @@ -77,7 +107,7 @@ async fn inner_detailed_products( Ok(stocks) => stocks, Err(e) => { tracing::error!("{}", e); - return Err(detailed_products::Error::VariantStocks( + return Err(Error::VariantStocks( variants.into_iter().map(|p| p.id).collect(), )); } @@ -89,89 +119,88 @@ async fn inner_detailed_products( Ok(photos) => photos, Err(e) => { tracing::error!("{}", e); - return Err(detailed_products::Error::VariantPhotos( + return Err(Error::VariantPhotos( variants.into_iter().map(|p| p.id).collect(), )); } }; - let mut variants = { - let len = variants.len(); - variants - .into_iter() - .fold(HashMap::with_capacity(len), |mut h, variant| { - h.entry(variant.product_id) - .or_insert_with(|| Vec::with_capacity(10)) - .push(variant); - h - }) - }; - - let mut stocks = { - let len = stocks.len(); - stocks - .into_iter() - .fold(HashMap::with_capacity(len), |mut h, stock| { - h.entry(stock.product_variant_id) - .or_insert_with(|| Vec::with_capacity(10)) - .push(stock); - h - }) - }; - - let mut photos = - photos - .into_iter() - .fold(HashMap::with_capacity(stocks.len()), |mut h, photo| { - h.entry(photo.product_variant_id) - .or_insert_with(|| Vec::with_capacity(10)) - .push(photo); - h - }); + let mut variants = utils::vec_to_hash_vec(variants, 10, |p| p.product_id); + let mut stocks = utils::vec_to_hash_vec(stocks, 10, |s| s.product_variant_id); + let mut photos = utils::vec_to_hash_vec(photos, 10, |p| p.product_variant_id); let products = products .into_iter() - .map( - |Product { - id, - name, - category, - deliver_days_flag, - }| DetailedProduct { - id, - name, - category, - deliver_days_flag, - variants: variants - .remove(&id) - .unwrap_or(vec![]) - .into_iter() - .map( - |ProductVariant { - id, - product_id: _, - name, - short_description, - long_description, - price, - }| DetailedProductVariant { - id, - name, - short_description, - long_description, - price, - stocks: stocks.remove(&id).unwrap_or_default(), - photos: photos.remove(&id).unwrap_or_default(), - }, - ) - .collect(), - }, - ) + .map(|product| utils::map_product(product, &mut variants, &mut stocks, &mut photos)) .collect(); Ok(detailed_products::Details { products }) } +mod utils { + use std::collections::HashMap; + use std::hash::Hash; + + use model::v2::*; + + pub fn vec_to_hash_vec Id>( + v: Vec, + cap: usize, + f: F, + ) -> HashMap> { + let len = v.len(); + v.into_iter().fold(HashMap::with_capacity(len), |mut h, r| { + h.entry(f(&r)) + .or_insert_with(|| Vec::with_capacity(cap)) + .push(r); + h + }) + } + + pub fn map_product( + product: Product, + variants: &mut HashMap>, + stocks: &mut HashMap>, + photos: &mut HashMap>, + ) -> DetailedProduct { + let Product { + id, + name, + category, + deliver_days_flag, + } = product; + DetailedProduct { + id, + name, + category, + deliver_days_flag, + variants: variants + .remove(&id) + .unwrap_or(vec![]) + .into_iter() + .map( + |ProductVariant { + id, + product_id: _, + name, + short_description, + long_description, + price, + }| DetailedProductVariant { + id, + name, + short_description, + long_description, + price, + stocks: stocks.remove(&id).unwrap_or_default(), + photos: photos.remove(&id).unwrap_or_default(), + }, + ) + .collect(), + } + } +} + #[cfg(test)] mod tests { use channels::stocks::detailed_products; @@ -313,6 +342,7 @@ mod tests { assert_eq!(res.products.len(), 3); let product = res.products.remove(0); + assert_eq!(product.variants.len(), 3); for variant in product.variants { assert_eq!(variant.photos.len(), 3); diff --git a/crates/stock_manager/src/actions/mod.rs b/crates/stock_manager/src/actions/mod.rs index 9a680e1..a7440bd 100644 --- a/crates/stock_manager/src/actions/mod.rs +++ b/crates/stock_manager/src/actions/mod.rs @@ -9,3 +9,29 @@ pub use product::*; pub use product_photo::*; pub use product_stock::*; pub use product_variant::*; + +#[macro_export] +macro_rules! begin_t { + ($db: ident, $err: expr) => { + match $db.pool().begin().await { + Err(e) => { + tracing::error!("{}", e); + return Err(detailed_products::Error::InternalServerError); + } + Ok(t) => t, + } + }; +} + +#[macro_export] +macro_rules! dbm_run { + ($dbm: ident, $t: expr, $err: expr) => { + match $dbm.run($t).await { + Ok(res) => res, + Err(e) => { + tracing::error!("{}", e); + return Err($err); + } + } + }; +}