Load single product
This commit is contained in:
parent
c7e9b25b97
commit
7bec8534e4
@ -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>;
|
||||||
|
@ -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:?}")]
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user