From 93e4c18df55fb67c81f5ff7884d886110888b94a Mon Sep 17 00:00:00 2001 From: Aravinth Manivannan Date: Tue, 23 Jul 2024 19:53:06 +0530 Subject: [PATCH] feat: delete order service --- .../adapters/output/db/line_item_view.rs | 3 - .../services/delete_order_service.rs | 115 ++++++++++++++++++ src/ordering/application/services/mod.rs | 7 ++ src/ordering/domain/commands.rs | 3 +- src/ordering/domain/delete_order_command.rs | 37 ++++++ src/ordering/domain/events.rs | 4 +- src/ordering/domain/mod.rs | 2 + src/ordering/domain/order_aggregate.rs | 51 ++++---- src/ordering/domain/order_deleted_event.rs | 48 ++++++++ 9 files changed, 240 insertions(+), 30 deletions(-) create mode 100644 src/ordering/application/services/delete_order_service.rs create mode 100644 src/ordering/domain/delete_order_command.rs create mode 100644 src/ordering/domain/order_deleted_event.rs diff --git a/src/ordering/adapters/output/db/line_item_view.rs b/src/ordering/adapters/output/db/line_item_view.rs index 673fc0f..ae36ab1 100644 --- a/src/ordering/adapters/output/db/line_item_view.rs +++ b/src/ordering/adapters/output/db/line_item_view.rs @@ -38,7 +38,6 @@ pub struct LineItemView { deleted: bool, } - impl Default for LineItemView { fn default() -> Self { Self { @@ -57,8 +56,6 @@ impl Default for LineItemView { } } - - impl From for LineItem { fn from(v: LineItemView) -> Self { let quantity = QuantityBuilder::default() diff --git a/src/ordering/application/services/delete_order_service.rs b/src/ordering/application/services/delete_order_service.rs new file mode 100644 index 0000000..8536f93 --- /dev/null +++ b/src/ordering/application/services/delete_order_service.rs @@ -0,0 +1,115 @@ +// SPDX-FileCopyrightText: 2024 Aravinth Manivannan +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::sync::Arc; + +use derive_builder::Builder; +use mockall::predicate::*; +use mockall::*; +use time::OffsetDateTime; + +use super::errors::*; +use crate::ordering::{ + application::port::output::db::order_id_exists::*, + domain::{delete_order_command::*, order_aggregate::*, order_deleted_event::*}, +}; +use crate::utils::uuid::*; + +#[automock] +#[async_trait::async_trait] +pub trait DeleteOrderUseCase: Send + Sync { + async fn delete_order(&self, cmd: DeleteOrderCommand) -> OrderingResult; +} + +pub type DeleteOrderServiceObj = Arc; + +#[derive(Clone, Builder)] +pub struct DeleteOrderService { + db_order_id_exists: OrderIDExistsDBPortObj, +} + +#[async_trait::async_trait] +impl DeleteOrderUseCase for DeleteOrderService { + async fn delete_order(&self, cmd: DeleteOrderCommand) -> OrderingResult { + if !self + .db_order_id_exists + .order_id_exists(cmd.order().order_id()) + .await? + { + return Err(OrderingError::OrderIDNotFound); + } + + let deleted_order = OrderBuilder::default() + .created_time(cmd.order().created_time().clone()) + .customer_name(cmd.order().customer_name().into()) + .order_id(*cmd.order().order_id()) + .deleted(true) + .build() + .unwrap(); + + Ok(OrderDeletedEventBuilder::default() + .added_by_user(*cmd.adding_by()) + .order(deleted_order) + .build() + .unwrap()) + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + + use crate::ordering::domain::order_deleted_event::tests::get_deleted_order_event_from_command; + use crate::tests::bdd::*; + + pub fn mock_delete_order_service( + times: Option, + cmd: DeleteOrderCommand, + ) -> DeleteOrderServiceObj { + let mut m = MockDeleteOrderUseCase::new(); + + let res = get_deleted_order_event_from_command(&cmd); + if let Some(times) = times { + m.expect_delete_order() + .times(times) + .returning(move |_| Ok(res.clone())); + } else { + m.expect_delete_order().returning(move |_| Ok(res.clone())); + } + + Arc::new(m) + } + + #[actix_rt::test] + async fn test_service() { + let cmd = DeleteOrderCommand::get_cmd(); + + let s = DeleteOrderServiceBuilder::default() + .db_order_id_exists(mock_order_id_exists_db_port_true(IS_CALLED_ONLY_ONCE)) + .build() + .unwrap(); + + let res = s.delete_order(cmd.clone()).await.unwrap(); + assert_eq!(res.order().customer_name(), cmd.order().customer_name()); + assert_eq!(res.order().order_id(), cmd.order().order_id()); + assert!(res.order().deleted()); + + assert_eq!(res.added_by_user(), cmd.adding_by()); + } + + #[actix_rt::test] + async fn test_service_order_id_doesnt_exist() { + let cmd = DeleteOrderCommand::get_cmd(); + + let s = DeleteOrderServiceBuilder::default() + .db_order_id_exists(mock_order_id_exists_db_port_false(IS_CALLED_ONLY_ONCE)) + .build() + .unwrap(); + + assert_eq!( + s.delete_order(cmd.clone()).await, + Err(OrderingError::OrderIDNotFound) + ); + } +} diff --git a/src/ordering/application/services/mod.rs b/src/ordering/application/services/mod.rs index d723801..ce866b3 100644 --- a/src/ordering/application/services/mod.rs +++ b/src/ordering/application/services/mod.rs @@ -12,6 +12,7 @@ pub mod errors; pub mod add_line_item_service; pub mod add_order_service; pub mod delete_line_item_service; +pub mod delete_order_service; pub mod update_line_item_service; pub mod update_order_service; @@ -22,6 +23,7 @@ pub trait OrderingServicesInterface: Send + Sync { fn delete_line_item(&self) -> delete_line_item_service::DeleteLineItemServiceObj; fn add_order(&self) -> add_order_service::AddOrderServiceObj; fn update_order(&self) -> update_order_service::UpdateOrderServiceObj; + fn delete_order(&self) -> delete_order_service::DeleteOrderServiceObj; } #[derive(Clone, Builder)] @@ -31,6 +33,7 @@ pub struct OrderingServices { delete_line_item: delete_line_item_service::DeleteLineItemServiceObj, add_order: add_order_service::AddOrderServiceObj, update_order: update_order_service::UpdateOrderServiceObj, + delete_order: delete_order_service::DeleteOrderServiceObj, } impl OrderingServicesInterface for OrderingServices { @@ -52,4 +55,8 @@ impl OrderingServicesInterface for OrderingServices { fn update_order(&self) -> update_order_service::UpdateOrderServiceObj { self.update_order.clone() } + + fn delete_order(&self) -> delete_order_service::DeleteOrderServiceObj { + self.delete_order.clone() + } } diff --git a/src/ordering/domain/commands.rs b/src/ordering/domain/commands.rs index 13983a3..98d5738 100644 --- a/src/ordering/domain/commands.rs +++ b/src/ordering/domain/commands.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; use super::{ add_line_item_command::AddLineItemCommand, add_order_command::AddOrderCommand, - delete_line_item_command::DeleteLineItemCommand, + delete_line_item_command::DeleteLineItemCommand, delete_order_command::DeleteOrderCommand, update_line_item_command::UpdateLineItemCommand, update_order_command::UpdateOrderCommand, }; @@ -18,4 +18,5 @@ pub enum OrderingCommand { DeleteLineItem(DeleteLineItemCommand), AddOrder(AddOrderCommand), UpdateOrder(UpdateOrderCommand), + DeleteOrder(DeleteOrderCommand), } diff --git a/src/ordering/domain/delete_order_command.rs b/src/ordering/domain/delete_order_command.rs new file mode 100644 index 0000000..e96f9a3 --- /dev/null +++ b/src/ordering/domain/delete_order_command.rs @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2024 Aravinth Manivannan +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +use derive_builder::Builder; +use derive_getters::Getters; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +use super::order_aggregate::Order; + +#[derive( + Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Builder, Getters, +)] +pub struct DeleteOrderCommand { + adding_by: Uuid, + order: Order, +} + +#[cfg(test)] +mod tests { + use crate::utils::uuid::tests::UUID; + + use super::*; + + impl DeleteOrderCommand { + pub fn get_cmd() -> Self { + let adding_by = UUID; + + DeleteOrderCommandBuilder::default() + .adding_by(adding_by) + .order(Order::get_order()) + .build() + .unwrap() + } + } +} diff --git a/src/ordering/domain/events.rs b/src/ordering/domain/events.rs index 969456d..4b497a8 100644 --- a/src/ordering/domain/events.rs +++ b/src/ordering/domain/events.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; use super::{ line_item_added_event::LineItemAddedEvent, line_item_deleted_event::LineItemDeletedEvent, line_item_updated_event::LineItemUpdatedEvent, order_added_event::OrderAddedEvent, - order_updated_event::OrderUpdatedEvent, + order_deleted_event::OrderDeletedEvent, order_updated_event::OrderUpdatedEvent, }; #[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd)] @@ -18,6 +18,7 @@ pub enum OrderingEvent { LineItemDeleted(LineItemDeletedEvent), OrderAdded(OrderAddedEvent), OrderUpdated(OrderUpdatedEvent), + OrderDeleted(OrderDeletedEvent), } impl DomainEvent for OrderingEvent { @@ -32,6 +33,7 @@ impl DomainEvent for OrderingEvent { OrderingEvent::LineItemDeleted { .. } => "OrderingLineItemDeleted", OrderingEvent::OrderAdded { .. } => "OrderingOrderAdded", OrderingEvent::OrderUpdated { .. } => "OrderingOrderUpdated", + OrderingEvent::OrderDeleted { .. } => "OrderingOrderDeleted", }; e.to_string() diff --git a/src/ordering/domain/mod.rs b/src/ordering/domain/mod.rs index 907a767..747c89d 100644 --- a/src/ordering/domain/mod.rs +++ b/src/ordering/domain/mod.rs @@ -12,6 +12,7 @@ pub mod add_line_item_command; pub mod add_order_command; pub mod commands; pub mod delete_line_item_command; +pub mod delete_order_command; pub mod update_line_item_command; pub mod update_order_command; @@ -21,4 +22,5 @@ pub mod line_item_added_event; pub mod line_item_deleted_event; pub mod line_item_updated_event; pub mod order_added_event; +pub mod order_deleted_event; pub mod order_updated_event; diff --git a/src/ordering/domain/order_aggregate.rs b/src/ordering/domain/order_aggregate.rs index bbf8a1d..e7c0f29 100644 --- a/src/ordering/domain/order_aggregate.rs +++ b/src/ordering/domain/order_aggregate.rs @@ -87,6 +87,10 @@ impl Aggregate for Order { let res = services.update_order().update_order(cmd).await?; Ok(vec![OrderingEvent::OrderUpdated(res)]) } + OrderingCommand::DeleteOrder(cmd) => { + let res = services.delete_order().delete_order(cmd).await?; + Ok(vec![OrderingEvent::OrderDeleted(res)]) + } _ => Ok(Vec::default()), } } @@ -95,7 +99,7 @@ impl Aggregate for Order { match event { OrderingEvent::OrderAdded(e) => *self = e.order().clone(), OrderingEvent::OrderUpdated(e) => *self = e.new_order().clone(), - // OrderingEvent::OrderDeleted(e) => *self = e.order().clone(), + OrderingEvent::OrderDeleted(e) => *self = e.order().clone(), _ => (), } } @@ -107,13 +111,13 @@ mod aggregate_tests { use add_order_service::tests::mock_add_order_service; use cqrs_es::test::TestFramework; - // use delete_order_service::tests::mock_delete_order_service; + use delete_order_service::tests::mock_delete_order_service; use update_order_service::tests::mock_update_order_service; use super::*; - // use crate::ordering::domain::delete_order_command::DeleteOrderCommand; - // use crate::ordering::domain::order_deleted_event::tests::get_deleted_order_event_from_command; + use crate::ordering::domain::delete_order_command::DeleteOrderCommand; + use crate::ordering::domain::order_deleted_event::tests::get_deleted_order_event_from_command; use crate::ordering::domain::order_updated_event::tests::get_updated_order_event_from_command; use crate::ordering::domain::update_order_command::UpdateOrderCommand; use crate::tests::bdd::*; @@ -159,25 +163,22 @@ mod aggregate_tests { .when(OrderingCommand::UpdateOrder(cmd)) .then_expect_events(vec![expected]); } - // - // #[test] - // fn test_delete_order() { - // let cmd = DeleteOrderCommand::get_cmd(); - // let expected = get_deleted_order_event_from_command(&cmd); - // let expected = OrderingEvent::OrderDeleted(expected); - // - // let mut services = MockOrderingServicesInterface::new(); - // services - // .expect_delete_order() - // .times(IS_CALLED_ONLY_ONCE.unwrap()) - // .return_const(mock_delete_order_service( - // IS_CALLED_ONLY_ONCE, - // cmd.clone(), - // )); - // - // OrderTestFramework::with(Arc::new(services)) - // .given_no_previous_events() - // .when(OrderingCommand::DeleteOrder(cmd)) - // .then_expect_events(vec![expected]); - // } + + #[test] + fn test_delete_order() { + let cmd = DeleteOrderCommand::get_cmd(); + let expected = get_deleted_order_event_from_command(&cmd); + let expected = OrderingEvent::OrderDeleted(expected); + + let mut services = MockOrderingServicesInterface::new(); + services + .expect_delete_order() + .times(IS_CALLED_ONLY_ONCE.unwrap()) + .return_const(mock_delete_order_service(IS_CALLED_ONLY_ONCE, cmd.clone())); + + OrderTestFramework::with(Arc::new(services)) + .given_no_previous_events() + .when(OrderingCommand::DeleteOrder(cmd)) + .then_expect_events(vec![expected]); + } } diff --git a/src/ordering/domain/order_deleted_event.rs b/src/ordering/domain/order_deleted_event.rs new file mode 100644 index 0000000..981b204 --- /dev/null +++ b/src/ordering/domain/order_deleted_event.rs @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: 2024 Aravinth Manivannan +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +use derive_builder::Builder; +use derive_getters::Getters; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +use super::order_aggregate::*; + +#[derive( + Clone, Debug, Builder, Serialize, Deserialize, Getters, Eq, PartialEq, Ord, PartialOrd, +)] +pub struct OrderDeletedEvent { + added_by_user: Uuid, + + order: Order, +} + +#[cfg(test)] +pub mod tests { + use crate::ordering::domain::delete_order_command::DeleteOrderCommand; + + use super::*; + + pub fn get_deleted_order_event_from_command(cmd: &DeleteOrderCommand) -> OrderDeletedEvent { + let deleted_order = OrderBuilder::default() + .created_time(cmd.order().created_time().clone()) + .customer_name(cmd.order().customer_name().into()) + .order_id(*cmd.order().order_id()) + .deleted(true) + .build() + .unwrap(); + + OrderDeletedEventBuilder::default() + .added_by_user(cmd.adding_by().clone()) + .order(deleted_order) + .build() + .unwrap() + } + + #[test] + fn test_event() { + let event = get_deleted_order_event_from_command(&DeleteOrderCommand::get_cmd()); + assert!(event.order().deleted()); + } +}