diff --git a/.sqlx/query-c348d55dd91acb0d4697c433f61866b288fec93971bf9ab41faec21680e50f71.json b/.sqlx/query-c348d55dd91acb0d4697c433f61866b288fec93971bf9ab41faec21680e50f71.json deleted file mode 100644 index f2ce603..0000000 --- a/.sqlx/query-c348d55dd91acb0d4697c433f61866b288fec93971bf9ab41faec21680e50f71.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE\n cqrs_ordering_line_item_query\n SET\n version = $1,\n product_name = $2,\n product_id = $3,\n line_item_id = $4,\n quantity_minor_unit = $5,\n quantity_minor_number = $6,\n quantity_major_unit = $7,\n quantity_major_number = $8,\n created_time = $9,\n kot_id = $10,\n deleted = $11;", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int8", - "Text", - "Uuid", - "Uuid", - "Text", - "Int4", - "Text", - "Int4", - "Timestamptz", - "Uuid", - "Bool" - ] - }, - "nullable": [] - }, - "hash": "c348d55dd91acb0d4697c433f61866b288fec93971bf9ab41faec21680e50f71" -} diff --git a/.sqlx/query-e61db066fe7e66879f8ce8ccd3da94d89c30f8f5d633004ba16a6e971e041a14.json b/.sqlx/query-e61db066fe7e66879f8ce8ccd3da94d89c30f8f5d633004ba16a6e971e041a14.json new file mode 100644 index 0000000..e48e3e8 --- /dev/null +++ b/.sqlx/query-e61db066fe7e66879f8ce8ccd3da94d89c30f8f5d633004ba16a6e971e041a14.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE\n cqrs_ordering_line_item_query\n SET\n version = $1,\n product_name = $2,\n product_id = $3,\n quantity_minor_unit = $4,\n quantity_minor_number = $5,\n quantity_major_unit = $6,\n quantity_major_number = $7,\n created_time = $8,\n kot_id = $9,\n deleted = $10;", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8", + "Text", + "Uuid", + "Text", + "Int4", + "Text", + "Int4", + "Timestamptz", + "Uuid", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "e61db066fe7e66879f8ce8ccd3da94d89c30f8f5d633004ba16a6e971e041a14" +} diff --git a/src/ordering/adapters/output/db/customization_view.rs b/src/ordering/adapters/output/db/customization_view.rs index 87a5c36..c31a26b 100644 --- a/src/ordering/adapters/output/db/customization_view.rs +++ b/src/ordering/adapters/output/db/customization_view.rs @@ -219,7 +219,6 @@ impl Query for OrderingDBPostgresAdapter { } } - #[cfg(test)] mod tests { use super::*; diff --git a/src/ordering/adapters/output/db/kot_id_exists.rs b/src/ordering/adapters/output/db/kot_id_exists.rs index 5d3486f..48d2879 100644 --- a/src/ordering/adapters/output/db/kot_id_exists.rs +++ b/src/ordering/adapters/output/db/kot_id_exists.rs @@ -36,7 +36,7 @@ pub mod tests { // use crate::ordering::domain::add_product_command::tests::get_customizations; use crate::ordering::domain::kot_aggregate::*; - async fn create_dummy_kot(kot: &Kot, db: &OrderingDBPostgresAdapter) { + pub async fn create_dummy_kot(kot: &Kot, db: &OrderingDBPostgresAdapter) { sqlx::query!( "INSERT INTO cqrs_ordering_kot_query ( version, diff --git a/src/ordering/adapters/output/db/line_item_view.rs b/src/ordering/adapters/output/db/line_item_view.rs index 341cc7f..5fe5c2a 100644 --- a/src/ordering/adapters/output/db/line_item_view.rs +++ b/src/ordering/adapters/output/db/line_item_view.rs @@ -271,18 +271,16 @@ impl ViewRepository for OrderingDBPostgresAdapter { version = $1, product_name = $2, product_id = $3, - line_item_id = $4, - quantity_minor_unit = $5, - quantity_minor_number = $6, - quantity_major_unit = $7, - quantity_major_number = $8, - created_time = $9, - kot_id = $10, - deleted = $11;", + quantity_minor_unit = $4, + quantity_minor_number = $5, + quantity_major_unit = $6, + quantity_major_number = $7, + created_time = $8, + kot_id = $9, + deleted = $10;", version, view.product_name, view.product_id, - view.line_item_id, view.quantity_minor_unit, view.quantity_minor_number, view.quantity_major_unit, @@ -320,3 +318,216 @@ impl Query for OrderingDBPostgresAdapter { self.update_view(view, view_context).await.unwrap(); } } + + +#[cfg(test)] +mod tests { + use super::*; + + use postgres_es::PostgresCqrs; + + use crate::{ + ordering::{ + application::services::{ + add_line_item_service::AddLineItemServiceBuilder, delete_line_item_service::*, + update_line_item_service::*, MockOrderingServicesInterface, + }, + domain::{ + add_line_item_command::*, kot_aggregate::Kot, commands::OrderingCommand, + delete_line_item_command::DeleteLineItemCommandBuilder, + update_line_item_command::*, + }, + }, + db::migrate::*, + tests::bdd::*, + types::quantity::*, + utils::{ + random_string::GenerateRandomStringInterface, + uuid::{tests::UUID, *}, + }, + }; + use std::sync::Arc; + + #[actix_rt::test] + async fn pg_query_ordering_line_item_view() { + let settings = crate::settings::tests::get_settings().await; + //let settings = crate::settings::Settings::new().unwrap(); + settings.create_db().await; + + let db = crate::db::sqlx_postgres::Postgres::init(&settings.database.url).await; + db.migrate().await; + let db = OrderingDBPostgresAdapter::new(db.pool.clone()); + + let queries: Vec>> = vec![Box::new(db.clone())]; + + let mut mock_services = MockOrderingServicesInterface::new(); + + let kot = Kot::default(); + crate::ordering::adapters::output::db::kot_id_exists::tests::create_dummy_kot( + &kot, &db, + ) + .await; + + let db2 = db.clone(); + mock_services + .expect_add_line_item() + .times(IS_CALLED_ONLY_ONCE.unwrap()) + .returning(move || { + Arc::new( + AddLineItemServiceBuilder::default() + .db_line_item_id_exists(Arc::new(db2.clone())) + .db_kot_id_exists(Arc::new(db2.clone())) + .build() + .unwrap(), + ) + }); + + let db2 = db.clone(); + mock_services + .expect_update_line_item() + .times(IS_CALLED_ONLY_ONCE.unwrap()) + .returning(move || { + Arc::new( + UpdateLineItemServiceBuilder::default() + .db_line_item_id_exists(Arc::new(db2.clone())) + .db_kot_id_exists(Arc::new(db2.clone())) + .build() + .unwrap(), + ) + }); + + let db2 = db.clone(); + mock_services + .expect_delete_line_item() + .times(IS_CALLED_ONLY_ONCE.unwrap()) + .returning(move || { + Arc::new( + DeleteLineItemServiceBuilder::default() + .db_line_item_id_exists(Arc::new(db2.clone())) + .build() + .unwrap(), + ) + }); + + let (cqrs, line_item_query): ( + Arc>, + Arc>, + ) = ( + Arc::new(postgres_es::postgres_cqrs( + db.pool.clone(), + queries, + Arc::new(mock_services), + )), + Arc::new(db.clone()), + ); + + let rand = crate::utils::random_string::GenerateRandomString {}; + let uuid = GenerateUUID {}; + let line_item_id = uuid.get_uuid(); + + let cmd = AddLineItemCommandBuilder::default() + .product_name(rand.get_random(10)) + .adding_by(UUID) + + .quantity(Quantity::get_quantity()) + .product_id(UUID) + .kot_id(*kot.kot_id()) + .line_item_id(line_item_id) + .build() + .unwrap(); + + cqrs.execute( + &cmd.line_item_id().to_string(), + OrderingCommand::AddLineItem(cmd.clone()), + ) + .await + .unwrap(); + + let line_item = line_item_query + .load(&(*cmd.line_item_id()).to_string()) + .await + .unwrap() + .unwrap(); + let line_item: LineItem = line_item.into(); + assert_eq!(line_item.line_item_id(), cmd.line_item_id()); + assert_eq!(line_item.product_name(), cmd.product_name()); + assert_eq!(line_item.product_id(), cmd.product_id()); + assert_eq!(line_item.quantity(), cmd.quantity()); + assert!(!line_item.deleted()); + + let update_line_item_cmd = UnvalidatedUpdateLineItemCommandBuilder::default() + .product_name(rand.get_random(10)) + .adding_by(UUID) + .quantity(Quantity::get_quantity()) + .product_id(UUID) + .kot_id(*kot.kot_id()) + .old_line_item(line_item.clone()) + + .build() + .unwrap() + .validate() + .unwrap(); + + cqrs.execute( + &cmd.line_item_id().to_string(), + OrderingCommand::UpdateLineItem(update_line_item_cmd.clone()), + ) + .await + .unwrap(); + let line_item = line_item_query + .load(&(*cmd.line_item_id()).to_string()) + .await + .unwrap() + .unwrap(); + let line_item: LineItem = line_item.into(); + assert_eq!( + line_item.line_item_id(), + update_line_item_cmd.old_line_item().line_item_id() + ); + assert_eq!( + line_item.product_name(), + update_line_item_cmd.product_name() + ); + assert_eq!(line_item.product_id(), update_line_item_cmd.product_id()); + assert_eq!(line_item.quantity(), update_line_item_cmd.quantity()); + assert!(!line_item.deleted()); + + // delete + let delete_line_item_cmd = DeleteLineItemCommandBuilder::default() + .line_item(line_item.clone()) + .adding_by(UUID) + .build() + .unwrap(); + cqrs.execute( + &cmd.line_item_id().to_string(), + OrderingCommand::DeleteLineItem(delete_line_item_cmd.clone()), + ) + .await + .unwrap(); + let deleted_line_item = line_item_query + .load(&(*cmd.line_item_id()).to_string()) + .await + .unwrap() + .unwrap(); + let deleted_line_item: LineItem = deleted_line_item.into(); + assert_eq!( + deleted_line_item.line_item_id(), + delete_line_item_cmd.line_item().line_item_id() + ); + assert_eq!( + deleted_line_item.product_name(), + delete_line_item_cmd.line_item().product_name() + ); + assert_eq!( + deleted_line_item.product_id(), + delete_line_item_cmd.line_item().product_id() + ); + assert_eq!( + deleted_line_item.quantity(), + delete_line_item_cmd.line_item().quantity() + ); + assert!(deleted_line_item.deleted()); + + settings.drop_db().await; + } +} diff --git a/src/ordering/adapters/output/db/store_view.rs b/src/ordering/adapters/output/db/store_view.rs index 8680b1b..7c3a29c 100644 --- a/src/ordering/adapters/output/db/store_view.rs +++ b/src/ordering/adapters/output/db/store_view.rs @@ -216,7 +216,6 @@ impl Query for OrderingDBPostgresAdapter { } } - #[cfg(test)] mod tests { use super::*; @@ -224,6 +223,7 @@ mod tests { use postgres_es::PostgresCqrs; use crate::{ + db::migrate::*, ordering::{ application::services::{ add_store_service::AddStoreServiceBuilder, update_store_service::*, @@ -233,7 +233,6 @@ mod tests { domain::commands::OrderingCommand, domain::update_store_command::*, }, - db::migrate::*, tests::bdd::*, utils::{random_string::GenerateRandomStringInterface, uuid::tests::UUID}, }; diff --git a/src/ordering/application/services/add_customization_service.rs b/src/ordering/application/services/add_customization_service.rs index 0b52d68..f47656f 100644 --- a/src/ordering/application/services/add_customization_service.rs +++ b/src/ordering/application/services/add_customization_service.rs @@ -11,14 +11,10 @@ use mockall::*; use super::errors::*; use crate::ordering::{ application::port::output::db::{ - customization_id_exists::*, - customization_name_exists_for_product::*, - product_id_exists::*, + customization_id_exists::*, customization_name_exists_for_product::*, product_id_exists::*, }, domain::{ - add_customization_command::*, - customization_added_event::*, - customization_aggregate::*, + add_customization_command::*, customization_added_event::*, customization_aggregate::*, }, }; @@ -54,13 +50,13 @@ impl AddCustomizationUseCase for AddCustomizationService { return Err(OrderingError::ProductIDNotFound); } - if self - .db_customization_id_exists - .customization_id_exists(cmd.customization_id()) - .await? - { - return Err(OrderingError::DuplicateCustomizationID); - } + if self + .db_customization_id_exists + .customization_id_exists(cmd.customization_id()) + .await? + { + return Err(OrderingError::DuplicateCustomizationID); + } let customization = CustomizationBuilder::default() .name(cmd.name().into()) @@ -90,8 +86,7 @@ pub mod tests { use super::*; use crate::ordering::domain::{ - add_customization_command::tests::*, - customization_added_event::tests::*, + add_customization_command::tests::*, customization_added_event::tests::*, }; use crate::tests::bdd::*; @@ -131,7 +126,10 @@ pub mod tests { let res = s.add_customization(cmd.clone()).await.unwrap(); assert_eq!(res.customization().name(), cmd.name()); - assert_eq!(res.customization().customization_id(), cmd.customization_id()); + assert_eq!( + res.customization().customization_id(), + cmd.customization_id() + ); } #[actix_rt::test] diff --git a/src/ordering/application/services/add_line_item_service.rs b/src/ordering/application/services/add_line_item_service.rs index 4214c3b..1e5d573 100644 --- a/src/ordering/application/services/add_line_item_service.rs +++ b/src/ordering/application/services/add_line_item_service.rs @@ -15,7 +15,6 @@ use crate::ordering::{ application::port::output::db::line_item_id_exists::*, domain::{add_line_item_command::*, line_item_added_event::*, line_item_aggregate::*}, }; -use crate::utils::uuid::*; #[automock] #[async_trait::async_trait] @@ -29,7 +28,6 @@ pub type AddLineItemServiceObj = Arc; pub struct AddLineItemService { db_line_item_id_exists: LineItemIDExistsDBPortObj, db_kot_id_exists: KotIDExistsDBPortObj, - get_uuid: GetUUIDInterfaceObj, } #[async_trait::async_trait] @@ -39,27 +37,20 @@ impl AddLineItemUseCase for AddLineItemService { return Err(OrderingError::KotIDNotFound); } - let mut line_item_id = self.get_uuid.get_uuid(); - - loop { if self .db_line_item_id_exists - .line_item_id_exists(&line_item_id) + .line_item_id_exists(cmd.line_item_id()) .await? { - line_item_id = self.get_uuid.get_uuid(); - continue; - } else { - break; + return Err(OrderingError::DuplicateLineItemID); } - } let line_item = LineItemBuilder::default() .created_time(cmd.created_time().clone()) .product_name(cmd.product_name().into()) .product_id(*cmd.product_id()) .kot_id(*cmd.kot_id()) - .line_item_id(line_item_id) + .line_item_id(*cmd.line_item_id()) .quantity(cmd.quantity().clone()) .deleted(false) .build() @@ -79,7 +70,7 @@ pub mod tests { use crate::ordering::domain::line_item_added_event::tests::get_added_line_item_event_from_command; use crate::utils::uuid::tests::UUID; - use crate::{tests::bdd::*, utils::uuid::tests::mock_get_uuid}; + use crate::tests::bdd::*; pub fn mock_add_line_item_service( times: Option, @@ -106,7 +97,6 @@ pub mod tests { let s = AddLineItemServiceBuilder::default() .db_line_item_id_exists(mock_line_item_id_exists_db_port_false(IS_CALLED_ONLY_ONCE)) .db_kot_id_exists(mock_kot_id_exists_db_port_true(IS_CALLED_ONLY_ONCE)) - .get_uuid(mock_get_uuid(IS_CALLED_ONLY_ONCE)) .build() .unwrap(); @@ -117,6 +107,7 @@ pub mod tests { assert_eq!(res.line_item().quantity(), cmd.quantity()); assert_eq!(res.line_item().quantity(), cmd.quantity()); assert_eq!(res.line_item().created_time(), cmd.created_time()); + assert_eq!(res.line_item().line_item_id(), cmd.line_item_id()); assert!(!res.line_item().deleted()); assert_eq!(res.added_by_user(), cmd.adding_by()); } @@ -128,7 +119,6 @@ pub mod tests { let s = AddLineItemServiceBuilder::default() .db_line_item_id_exists(mock_line_item_id_exists_db_port_false(IS_NEVER_CALLED)) .db_kot_id_exists(mock_kot_id_exists_db_port_false(IS_CALLED_ONLY_ONCE)) - .get_uuid(mock_get_uuid(IS_NEVER_CALLED)) .build() .unwrap(); diff --git a/src/ordering/application/services/add_store_service.rs b/src/ordering/application/services/add_store_service.rs index aa88411..9b268a6 100644 --- a/src/ordering/application/services/add_store_service.rs +++ b/src/ordering/application/services/add_store_service.rs @@ -35,10 +35,13 @@ pub struct AddStoreService { #[async_trait::async_trait] impl AddStoreUseCase for AddStoreService { async fn add_store(&self, cmd: AddStoreCommand) -> OrderingResult { - - if self.db_store_id_exists.store_id_exists(cmd.store_id()).await? { - return Err(OrderingError::DuplicateStoreID); - } + if self + .db_store_id_exists + .store_id_exists(cmd.store_id()) + .await? + { + return Err(OrderingError::DuplicateStoreID); + } let store = StoreBuilder::default() .name(cmd.name().into()) diff --git a/src/ordering/application/services/errors.rs b/src/ordering/application/services/errors.rs index 1cacb54..83303f6 100644 --- a/src/ordering/application/services/errors.rs +++ b/src/ordering/application/services/errors.rs @@ -46,7 +46,7 @@ impl From for OrderingError { OrderingDBError::DuplicateStoreID => Self::DuplicateStoreID, OrderingDBError::StoreIDNotFound => OrderingError::StoreIDNotFound, OrderingDBError::OrderIDNotFound => OrderingError::OrderIDNotFound, - OrderingDBError::DuplicateKotID => Self::DuplicateKotID, + OrderingDBError::DuplicateKotID => Self::DuplicateKotID, OrderingDBError::KotIDNotFound => OrderingError::KotIDNotFound, OrderingDBError::InternalError => Self::InternalError, OrderingDBError::DuplicateCategoryName => Self::DuplicateCategoryName, diff --git a/src/ordering/domain/add_customization_command.rs b/src/ordering/domain/add_customization_command.rs index a912070..507412c 100644 --- a/src/ordering/domain/add_customization_command.rs +++ b/src/ordering/domain/add_customization_command.rs @@ -82,4 +82,3 @@ pub mod tests { .is_err(),); } } - diff --git a/src/ordering/domain/add_line_item_command.rs b/src/ordering/domain/add_line_item_command.rs index d1f68cb..4a70415 100644 --- a/src/ordering/domain/add_line_item_command.rs +++ b/src/ordering/domain/add_line_item_command.rs @@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize}; use time::OffsetDateTime; use uuid::Uuid; -use crate::types::quantity::*; +use crate::types::{currency::*, quantity::*}; use crate::utils::string::empty_string_err; #[derive(Debug, Error, Display, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] @@ -19,52 +19,41 @@ pub enum AddLineItemCommandError { } #[derive( - Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Getters, Builder, + Clone, Debug, Builder, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Getters, )] -pub struct UnvalidatedAddLineItemCommand { - adding_by: Uuid, - +#[builder(build_fn(validate = "Self::validate"))] +pub struct AddLineItemCommand { #[builder(default = "OffsetDateTime::now_utc()")] created_time: OffsetDateTime, + #[builder(setter(custom))] product_name: String, product_id: Uuid, kot_id: Uuid, - quantity: Quantity, -} - -impl UnvalidatedAddLineItemCommand { - pub fn validate(self) -> Result { - let product_name = empty_string_err( - self.product_name, - AddLineItemCommandError::ProductNameIsEmpty, - )?; - - if self.quantity.is_empty() { - return Err(AddLineItemCommandError::QuantityIsEmpty); - } - - Ok(AddLineItemCommand { - created_time: self.created_time, - product_name, - product_id: self.product_id, - kot_id: self.kot_id, - quantity: self.quantity, - adding_by: self.adding_by, - }) - } -} - -#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Getters)] -pub struct AddLineItemCommand { - created_time: OffsetDateTime, - product_name: String, - product_id: Uuid, - kot_id: Uuid, + line_item_id: Uuid, quantity: Quantity, adding_by: Uuid, } +impl AddLineItemCommandBuilder { + pub fn product_name(&mut self, product_name: String) -> &mut Self { + self.product_name = Some(product_name.trim().to_owned()); + self + } + + fn validate(&self) -> Result<(), String> { + let product_name = self.product_name.as_ref().unwrap().trim().to_owned(); + if product_name.is_empty() { + return Err(AddLineItemCommandError::ProductNameIsEmpty.to_string()); + } + + if self.quantity.as_ref().unwrap().is_empty() { + return Err(AddLineItemCommandError::QuantityIsEmpty.to_string()); + } + Ok(()) + } +} + #[cfg(test)] mod tests { use time::macros::datetime; @@ -81,17 +70,16 @@ mod tests { let adding_by = UUID; let quantity = Quantity::get_quantity(); - UnvalidatedAddLineItemCommandBuilder::default() + AddLineItemCommandBuilder::default() .product_name(product_name.into()) .adding_by(adding_by) .created_time(datetime!(1970-01-01 0:00 UTC)) .quantity(quantity.clone()) .product_id(product_id) .kot_id(kot_id) + .line_item_id(UUID) .build() .unwrap() - .validate() - .unwrap() } } @@ -103,15 +91,14 @@ mod tests { let adding_by = UUID; let quantity = Quantity::get_quantity(); - let cmd = UnvalidatedAddLineItemCommandBuilder::default() + let cmd = AddLineItemCommandBuilder::default() .product_name(product_name.into()) .adding_by(adding_by) .quantity(quantity.clone()) .product_id(product_id) .kot_id(kot_id) + .line_item_id(UUID) .build() - .unwrap() - .validate() .unwrap(); assert_eq!(cmd.quantity(), &quantity); @@ -128,18 +115,15 @@ mod tests { let adding_by = UUID; let quantity = Quantity::get_quantity(); - assert_eq!( - UnvalidatedAddLineItemCommandBuilder::default() - .product_name(product_name.into()) - .adding_by(adding_by) - .quantity(quantity.clone()) - .product_id(product_id) - .kot_id(kot_id) - .build() - .unwrap() - .validate(), - Err(AddLineItemCommandError::ProductNameIsEmpty) - ); + assert!(AddLineItemCommandBuilder::default() + .product_name(product_name.into()) + .adding_by(adding_by) + .quantity(quantity.clone()) + .product_id(product_id) + .line_item_id(UUID) + .kot_id(kot_id) + .build() + .is_err()); } #[test] @@ -151,17 +135,13 @@ mod tests { // minor = 0; major = 0; let quantity = Quantity::default(); - assert_eq!( - UnvalidatedAddLineItemCommandBuilder::default() - .product_name(product_name.into()) - .adding_by(adding_by) - .quantity(quantity.clone()) - .product_id(product_id) - .kot_id(kot_id) - .build() - .unwrap() - .validate(), - Err(AddLineItemCommandError::QuantityIsEmpty) - ); + assert!(AddLineItemCommandBuilder::default() + .product_name(product_name.into()) + .adding_by(adding_by) + .quantity(quantity.clone()) + .product_id(product_id) + .kot_id(kot_id) + .build() + .is_err()); } }