Load single product

This commit is contained in:
Adrian Woźniak 2022-11-12 21:58:08 +01:00
parent c7e9b25b97
commit 7bec8534e4
No known key found for this signature in database
GPG Key ID: 0012845A89C7352B
4 changed files with 160 additions and 104 deletions

View File

@ -10,9 +10,7 @@ pub mod detailed_product {
#[derive(Debug, serde::Serialize, serde::Deserialize)] #[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct Output2 { pub struct Output2 {
pub product: model::Product, pub product: DetailedProduct,
pub stocks: Vec<Stock>,
pub photos: Vec<Photo>,
} }
pub type Output = Result<Output2, Error>; pub type Output = Result<Output2, Error>;

View File

@ -18,6 +18,8 @@ pub static CLIENT_NAME: &str = "stocks";
pub enum Error { pub enum Error {
#[error("Something went wrong")] #[error("Something went wrong")]
InternalServerError, InternalServerError,
#[error("Failed to load product {0:?}")]
ProductNotFound(ProductId),
#[error("Failed to load products")] #[error("Failed to load products")]
Products, Products,
#[error("Failed to load products for products {0:?}")] #[error("Failed to load products for products {0:?}")]

View File

@ -1,21 +1,67 @@
use std::collections::HashMap; use channels::stocks::{detailed_product, detailed_products, Error};
use channels::stocks::{detailed_product, detailed_products};
use channels::AsyncClient; use channels::AsyncClient;
use config::SharedAppConfig; use config::SharedAppConfig;
use db_utils::PgT; use db_utils::PgT;
use model::v2::{DetailedProduct, DetailedProductVariant, Product, ProductVariant};
use model::Limit; use model::Limit;
use crate::db::{Database, PhotosForProductVariants, ProductVariantsStock, ProductsVariants}; use crate::db::{Database, PhotosForProductVariants, ProductVariantsStock, ProductsVariants};
use crate::{begin_t, dbm_run};
pub async fn detailed_product( pub async fn detailed_product(
_input: detailed_product::Input, input: detailed_product::Input,
_db: Database, db: Database,
_mqtt: AsyncClient, _mqtt: AsyncClient,
_config: SharedAppConfig, _config: SharedAppConfig,
) -> detailed_product::Output { ) -> 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<AsyncClient>,
_config: Option<SharedAppConfig>,
) -> 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( pub async fn detailed_products(
@ -24,13 +70,7 @@ pub async fn detailed_products(
_mqtt: AsyncClient, _mqtt: AsyncClient,
_config: SharedAppConfig, _config: SharedAppConfig,
) -> detailed_products::Output { ) -> detailed_products::Output {
let mut t = match db.pool().begin().await { let mut t = begin_t!(db, Error::InternalServerError);
Err(e) => {
tracing::error!("{}", e);
return Err(detailed_products::Error::InternalServerError);
}
Ok(t) => t,
};
let res = inner_detailed_products(input, &mut t, Some(_mqtt), Some(_config)).await; 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, limit: input.limit,
offset: input.offset, offset: input.offset,
}; };
let products = match dbm.run(&mut *t).await { let products = dbm_run!(dbm, &mut *t, Error::Products);
Ok(products) => products,
Err(e) => {
tracing::error!("{}", e);
return Err(detailed_products::Error::Products);
}
};
let dbm = ProductsVariants { let dbm = ProductsVariants {
product_ids: products.iter().map(|p| p.id).collect(), product_ids: products.iter().map(|p| p.id).collect(),
limit: Some(Limit::from_u32(products.len() as u32 * 10)), limit: Some(Limit::from_u32(products.len() as u32 * 10)),
offset: Some(0.into()), offset: Some(0.into()),
}; };
let variants = match dbm.run(&mut *t).await { let variants = dbm_run!(
Ok(variants) => variants, dbm,
Err(e) => { &mut *t,
tracing::error!("{}", e); Error::ProductVariants(products.into_iter().map(|p| p.id).collect(),)
return Err(detailed_products::Error::ProductVariants( );
products.into_iter().map(|p| p.id).collect(),
));
}
};
let dbm = ProductVariantsStock { let dbm = ProductVariantsStock {
product_variant_ids: variants.iter().map(|p| p.id).collect(), product_variant_ids: variants.iter().map(|p| p.id).collect(),
}; };
@ -77,7 +107,7 @@ async fn inner_detailed_products(
Ok(stocks) => stocks, Ok(stocks) => stocks,
Err(e) => { Err(e) => {
tracing::error!("{}", e); tracing::error!("{}", e);
return Err(detailed_products::Error::VariantStocks( return Err(Error::VariantStocks(
variants.into_iter().map(|p| p.id).collect(), variants.into_iter().map(|p| p.id).collect(),
)); ));
} }
@ -89,89 +119,88 @@ async fn inner_detailed_products(
Ok(photos) => photos, Ok(photos) => photos,
Err(e) => { Err(e) => {
tracing::error!("{}", e); tracing::error!("{}", e);
return Err(detailed_products::Error::VariantPhotos( return Err(Error::VariantPhotos(
variants.into_iter().map(|p| p.id).collect(), variants.into_iter().map(|p| p.id).collect(),
)); ));
} }
}; };
let mut variants = { let mut variants = utils::vec_to_hash_vec(variants, 10, |p| p.product_id);
let len = variants.len(); let mut stocks = utils::vec_to_hash_vec(stocks, 10, |s| s.product_variant_id);
variants let mut photos = utils::vec_to_hash_vec(photos, 10, |p| p.product_variant_id);
.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 products = products let products = products
.into_iter() .into_iter()
.map( .map(|product| utils::map_product(product, &mut variants, &mut stocks, &mut photos))
|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(),
},
)
.collect(); .collect();
Ok(detailed_products::Details { products }) 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: Hash + Eq, R, F: Fn(&R) -> Id>(
v: Vec<R>,
cap: usize,
f: F,
) -> HashMap<Id, Vec<R>> {
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<ProductId, Vec<ProductVariant>>,
stocks: &mut HashMap<ProductVariantId, Vec<Stock>>,
photos: &mut HashMap<ProductVariantId, Vec<ProductLinkedPhoto>>,
) -> 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)] #[cfg(test)]
mod tests { mod tests {
use channels::stocks::detailed_products; use channels::stocks::detailed_products;
@ -313,6 +342,7 @@ mod tests {
assert_eq!(res.products.len(), 3); assert_eq!(res.products.len(), 3);
let product = res.products.remove(0); let product = res.products.remove(0);
assert_eq!(product.variants.len(), 3); assert_eq!(product.variants.len(), 3);
for variant in product.variants { for variant in product.variants {
assert_eq!(variant.photos.len(), 3); assert_eq!(variant.photos.len(), 3);

View File

@ -9,3 +9,29 @@ pub use product::*;
pub use product_photo::*; pub use product_photo::*;
pub use product_stock::*; pub use product_stock::*;
pub use product_variant::*; 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);
}
}
};
}