From 88446b94c4589943422c3cba9230d758e1f51259 Mon Sep 17 00:00:00 2001 From: Aravinth Manivannan Date: Sat, 21 Sep 2024 16:21:19 +0530 Subject: [PATCH] feat: inventory: product ID is provided by caller --- .../services/add_product_service.rs | 28 ++---- src/inventory/application/services/errors.rs | 6 +- src/inventory/domain/add_product_command.rs | 88 +++++++++---------- .../domain/update_product_command.rs | 59 +++++++++++-- 4 files changed, 104 insertions(+), 77 deletions(-) diff --git a/src/inventory/application/services/add_product_service.rs b/src/inventory/application/services/add_product_service.rs index e446886..c3f1501 100644 --- a/src/inventory/application/services/add_product_service.rs +++ b/src/inventory/application/services/add_product_service.rs @@ -23,7 +23,6 @@ use crate::inventory::{ product_aggregate::*, }, }; -use crate::utils::uuid::*; #[automock] #[async_trait::async_trait] @@ -40,7 +39,6 @@ pub struct AddProductService { db_product_id_exists: ProductIDExistsDBPortObj, db_get_category: GetCategoryDBPortObj, fts_add_product: AddProductToStoreFTSPortObj, - get_uuid: GetUUIDInterfaceObj, } #[async_trait::async_trait] @@ -54,19 +52,12 @@ impl AddProductUseCase for AddProductService { return Err(InventoryError::CategoryIDNotFound); } - let mut product_id = self.get_uuid.get_uuid(); - - loop { - if self - .db_product_id_exists - .product_id_exists(&product_id) - .await? - { - product_id = self.get_uuid.get_uuid(); - continue; - } else { - break; - } + if self + .db_product_id_exists + .product_id_exists(cmd.product_id()) + .await? + { + return Err(InventoryError::DuplicateProductID); } let product = ProductBuilder::default() @@ -77,7 +68,7 @@ impl AddProductUseCase for AddProductService { .price(cmd.price().clone()) .category_id(*cmd.category_id()) .quantity(cmd.quantity().clone()) - .product_id(product_id) + .product_id(*cmd.product_id()) .build() .unwrap(); @@ -119,8 +110,8 @@ pub mod tests { use super::*; use crate::inventory::domain::add_product_command::tests::get_command; + use crate::tests::bdd::*; use crate::utils::uuid::tests::UUID; - use crate::{tests::bdd::*, utils::uuid::tests::mock_get_uuid}; pub fn mock_add_product_service( times: Option, @@ -165,7 +156,6 @@ pub mod tests { .db_get_category(mock_get_category_db_port(IS_CALLED_ONLY_ONCE)) .db_category_id_exists(mock_category_id_exists_db_port_true(IS_CALLED_ONLY_ONCE)) .fts_add_product(mock_add_product_to_store_fts_port(IS_CALLED_ONLY_ONCE)) - .get_uuid(mock_get_uuid(IS_CALLED_ONLY_ONCE)) .build() .unwrap(); @@ -190,7 +180,6 @@ pub mod tests { mock_product_name_exists_for_category_db_port_true(IS_CALLED_ONLY_ONCE), ) .db_category_id_exists(mock_category_id_exists_db_port_true(IS_CALLED_ONLY_ONCE)) - .get_uuid(mock_get_uuid(IS_CALLED_ONLY_ONCE)) .db_product_id_exists(mock_product_id_exists_db_port_false(IS_CALLED_ONLY_ONCE)) .db_get_category(mock_get_category_db_port(IS_NEVER_CALLED)) .fts_add_product(mock_add_product_to_store_fts_port(IS_NEVER_CALLED)) @@ -215,7 +204,6 @@ pub mod tests { .db_category_id_exists(mock_category_id_exists_db_port_false(IS_CALLED_ONLY_ONCE)) .db_get_category(mock_get_category_db_port(IS_NEVER_CALLED)) .fts_add_product(mock_add_product_to_store_fts_port(IS_NEVER_CALLED)) - .get_uuid(mock_get_uuid(IS_NEVER_CALLED)) .build() .unwrap(); diff --git a/src/inventory/application/services/errors.rs b/src/inventory/application/services/errors.rs index 5373fed..5c1782c 100644 --- a/src/inventory/application/services/errors.rs +++ b/src/inventory/application/services/errors.rs @@ -21,6 +21,7 @@ pub enum InventoryError { DuplicateCustomizationID, DuplicateStoreID, DuplicateCategoryID, + DuplicateProductID, ProductIDNotFound, CategoryIDNotFound, CustomizationIDNotFound, @@ -36,10 +37,7 @@ impl From for InventoryError { InventoryDBError::DuplicateProductName => Self::DuplicateProductName, InventoryDBError::DuplicateCustomizationName => Self::DuplicateCustomizationName, InventoryDBError::DuplicateStoreID => Self::DuplicateStoreID, - InventoryDBError::DuplicateProductID => { - error!("DuplicateProductID"); - Self::InternalError - } + InventoryDBError::DuplicateProductID => Self::DuplicateProductID, InventoryDBError::DuplicateCategoryID => Self::DuplicateCategoryID, InventoryDBError::DuplicateCustomizationID => Self::DuplicateCustomizationID, InventoryDBError::InternalError => Self::InternalError, diff --git a/src/inventory/domain/add_product_command.rs b/src/inventory/domain/add_product_command.rs index f347838..5484ebd 100644 --- a/src/inventory/domain/add_product_command.rs +++ b/src/inventory/domain/add_product_command.rs @@ -17,24 +17,17 @@ pub enum AddProductCommandError { } #[derive( - Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Getters, Builder, + Clone, Builder, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Getters, )] -pub struct UnvalidatedAddProductCommand { - name: String, - description: Option, - image: Option, - category_id: Uuid, - sku_able: bool, - quantity: Quantity, - price: Price, - adding_by: Uuid, -} - -#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Getters)] +#[builder(build_fn(validate = "Self::validate"))] pub struct AddProductCommand { + #[builder(setter(custom))] name: String, + #[builder(setter(custom))] description: Option, + #[builder(setter(custom))] image: Option, + product_id: Uuid, category_id: Uuid, sku_able: bool, price: Price, @@ -42,9 +35,9 @@ pub struct AddProductCommand { adding_by: Uuid, } -impl UnvalidatedAddProductCommand { - pub fn validate(self) -> Result { - let description: Option = if let Some(description) = self.description { +impl AddProductCommandBuilder { + pub fn description(&mut self, description: Option) -> &mut Self { + let description: Option = if let Some(description) = description { let description = description.trim(); if description.is_empty() { None @@ -54,8 +47,12 @@ impl UnvalidatedAddProductCommand { } else { None }; + self.description = Some(description); + self + } - let image: Option = if let Some(image) = self.image { + pub fn image(&mut self, image: Option) -> &mut Self { + let image: Option = if let Some(image) = image { let image = image.trim(); if image.is_empty() { None @@ -65,22 +62,22 @@ impl UnvalidatedAddProductCommand { } else { None }; + self.image = Some(image); + self + } - let name = self.name.trim().to_owned(); - if name.is_empty() { - return Err(AddProductCommandError::NameIsEmpty); + pub fn name(&mut self, name: String) -> &mut Self { + let name = name.trim().to_owned(); + self.name = Some(name); + self + } + + pub fn validate(&self) -> Result<(), String> { + if self.name.as_ref().unwrap().is_empty() { + return Err(AddProductCommandError::NameIsEmpty.to_string()); } - Ok(AddProductCommand { - name, - description, - image, - category_id: self.category_id, - sku_able: self.sku_able, - price: self.price, - quantity: self.quantity, - adding_by: self.adding_by, - }) + Ok(()) } } @@ -93,7 +90,7 @@ pub mod tests { pub fn get_command() -> AddProductCommand { let name = "foo"; let adding_by = UUID; - let category_id = Uuid::new_v4(); + let category_id = UUID; let sku_able = false; let image = Some("image".to_string()); let description = Some("description".to_string()); @@ -123,7 +120,7 @@ pub mod tests { .build() .unwrap(); - let cmd = UnvalidatedAddProductCommandBuilder::default() + AddProductCommandBuilder::default() .name(name.into()) .description(description.clone()) .image(image.clone()) @@ -131,11 +128,10 @@ pub mod tests { .adding_by(adding_by) .quantity(quantity) .sku_able(sku_able) + .product_id(UUID) .price(price.clone()) .build() - .unwrap(); - - cmd.validate().unwrap() + .unwrap() } #[test] @@ -155,7 +151,7 @@ pub mod tests { let quantity = Quantity::default(); // description = None - let cmd = UnvalidatedAddProductCommandBuilder::default() + let cmd = AddProductCommandBuilder::default() .name(name.into()) .description(None) .image(None) @@ -163,12 +159,11 @@ pub mod tests { .adding_by(adding_by) .quantity(quantity.clone()) .sku_able(sku_able) + .product_id(UUID) .price(price.clone()) .build() .unwrap(); - let cmd = cmd.validate().unwrap(); - assert_eq!(cmd.name(), name); assert_eq!(cmd.description(), &None); assert_eq!(cmd.adding_by(), &adding_by); @@ -196,21 +191,19 @@ pub mod tests { let quantity = Quantity::default(); - let cmd = UnvalidatedAddProductCommandBuilder::default() + let cmd = AddProductCommandBuilder::default() .name(name.into()) .description(description.clone()) .image(image.clone()) .category_id(category_id) - .quantity(quantity.clone()) .adding_by(adding_by) + .quantity(quantity.clone()) .sku_able(sku_able) + .product_id(UUID) .price(price.clone()) - // .customizations(customizations.clone()) .build() .unwrap(); - let cmd = cmd.validate().unwrap(); - assert_eq!(cmd.name(), name); assert_eq!(cmd.description(), &description); assert_eq!(cmd.adding_by(), &adding_by); @@ -238,7 +231,8 @@ pub mod tests { let quantity = Quantity::default(); - let cmd = UnvalidatedAddProductCommandBuilder::default() + // AddProductCommandError::NameIsEmpty + assert!(AddProductCommandBuilder::default() .name("".into()) .description(description.clone()) .image(image.clone()) @@ -246,11 +240,9 @@ pub mod tests { .adding_by(adding_by) .quantity(quantity) .sku_able(sku_able) + .product_id(UUID) .price(price.clone()) .build() - .unwrap(); - - // AddProductCommandError::NameIsEmpty - assert_eq!(cmd.validate(), Err(AddProductCommandError::NameIsEmpty)) + .is_err()); } } diff --git a/src/inventory/domain/update_product_command.rs b/src/inventory/domain/update_product_command.rs index f158f07..b99a897 100644 --- a/src/inventory/domain/update_product_command.rs +++ b/src/inventory/domain/update_product_command.rs @@ -95,13 +95,62 @@ pub mod tests { use crate::types::quantity::*; use crate::utils::uuid::tests::UUID; - pub fn get_command() -> UpdateProductCommand { - let name = "foo"; + pub fn get_command_with_product(product: Product) -> UpdateProductCommand { + let name = "foobaaar"; let adding_by = UUID; - let category_id = Uuid::new_v4(); + let category_id = UUID; let sku_able = false; - let image = Some("image".to_string()); - let description = Some("description".to_string()); + let image = Some("imageeee".to_string()); + let description = Some("descriptionnnn".to_string()); + + let price = PriceBuilder::default() + .minor(0) + .major(100) + .currency(Currency::INR) + .build() + .unwrap(); + + let quantity = QuantityBuilder::default() + .minor( + QuantityPartBuilder::default() + .number(0) + .unit(QuantityUnit::DiscreteNumber) + .build() + .unwrap(), + ) + .major( + QuantityPartBuilder::default() + .number(1) + .unit(QuantityUnit::DiscreteNumber) + .build() + .unwrap(), + ) + .build() + .unwrap(); + + let cmd = UnvalidatedUpdateProductCommandBuilder::default() + .name(name.into()) + .description(description.clone()) + .image(image.clone()) + .category_id(category_id.clone()) + .adding_by(adding_by.clone()) + .quantity(quantity) + .sku_able(sku_able) + .price(price.clone()) + .old_product(product) + .build() + .unwrap(); + + cmd.validate().unwrap() + } + + pub fn get_command() -> UpdateProductCommand { + let name = "foobaaar"; + let adding_by = UUID; + let category_id = UUID; + let sku_able = false; + let image = Some("imageeee".to_string()); + let description = Some("descriptionnnn".to_string()); let price = PriceBuilder::default() .minor(0)