feat: delete order service #68

Merged
realaravinth merged 1 commit from delete-order-service into master 2024-07-23 20:05:48 +05:30
9 changed files with 240 additions and 30 deletions

View file

@ -38,7 +38,6 @@ pub struct LineItemView {
deleted: bool, deleted: bool,
} }
impl Default for LineItemView { impl Default for LineItemView {
fn default() -> Self { fn default() -> Self {
Self { Self {
@ -57,8 +56,6 @@ impl Default for LineItemView {
} }
} }
impl From<LineItemView> for LineItem { impl From<LineItemView> for LineItem {
fn from(v: LineItemView) -> Self { fn from(v: LineItemView) -> Self {
let quantity = QuantityBuilder::default() let quantity = QuantityBuilder::default()

View file

@ -0,0 +1,115 @@
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
//
// 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<OrderDeletedEvent>;
}
pub type DeleteOrderServiceObj = Arc<dyn DeleteOrderUseCase>;
#[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<OrderDeletedEvent> {
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<usize>,
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)
);
}
}

View file

@ -12,6 +12,7 @@ pub mod errors;
pub mod add_line_item_service; pub mod add_line_item_service;
pub mod add_order_service; pub mod add_order_service;
pub mod delete_line_item_service; pub mod delete_line_item_service;
pub mod delete_order_service;
pub mod update_line_item_service; pub mod update_line_item_service;
pub mod update_order_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 delete_line_item(&self) -> delete_line_item_service::DeleteLineItemServiceObj;
fn add_order(&self) -> add_order_service::AddOrderServiceObj; fn add_order(&self) -> add_order_service::AddOrderServiceObj;
fn update_order(&self) -> update_order_service::UpdateOrderServiceObj; fn update_order(&self) -> update_order_service::UpdateOrderServiceObj;
fn delete_order(&self) -> delete_order_service::DeleteOrderServiceObj;
} }
#[derive(Clone, Builder)] #[derive(Clone, Builder)]
@ -31,6 +33,7 @@ pub struct OrderingServices {
delete_line_item: delete_line_item_service::DeleteLineItemServiceObj, delete_line_item: delete_line_item_service::DeleteLineItemServiceObj,
add_order: add_order_service::AddOrderServiceObj, add_order: add_order_service::AddOrderServiceObj,
update_order: update_order_service::UpdateOrderServiceObj, update_order: update_order_service::UpdateOrderServiceObj,
delete_order: delete_order_service::DeleteOrderServiceObj,
} }
impl OrderingServicesInterface for OrderingServices { impl OrderingServicesInterface for OrderingServices {
@ -52,4 +55,8 @@ impl OrderingServicesInterface for OrderingServices {
fn update_order(&self) -> update_order_service::UpdateOrderServiceObj { fn update_order(&self) -> update_order_service::UpdateOrderServiceObj {
self.update_order.clone() self.update_order.clone()
} }
fn delete_order(&self) -> delete_order_service::DeleteOrderServiceObj {
self.delete_order.clone()
}
} }

View file

@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
use super::{ use super::{
add_line_item_command::AddLineItemCommand, add_order_command::AddOrderCommand, 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, update_line_item_command::UpdateLineItemCommand, update_order_command::UpdateOrderCommand,
}; };
@ -18,4 +18,5 @@ pub enum OrderingCommand {
DeleteLineItem(DeleteLineItemCommand), DeleteLineItem(DeleteLineItemCommand),
AddOrder(AddOrderCommand), AddOrder(AddOrderCommand),
UpdateOrder(UpdateOrderCommand), UpdateOrder(UpdateOrderCommand),
DeleteOrder(DeleteOrderCommand),
} }

View file

@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
//
// 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()
}
}
}

View file

@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
use super::{ use super::{
line_item_added_event::LineItemAddedEvent, line_item_deleted_event::LineItemDeletedEvent, line_item_added_event::LineItemAddedEvent, line_item_deleted_event::LineItemDeletedEvent,
line_item_updated_event::LineItemUpdatedEvent, order_added_event::OrderAddedEvent, 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)] #[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd)]
@ -18,6 +18,7 @@ pub enum OrderingEvent {
LineItemDeleted(LineItemDeletedEvent), LineItemDeleted(LineItemDeletedEvent),
OrderAdded(OrderAddedEvent), OrderAdded(OrderAddedEvent),
OrderUpdated(OrderUpdatedEvent), OrderUpdated(OrderUpdatedEvent),
OrderDeleted(OrderDeletedEvent),
} }
impl DomainEvent for OrderingEvent { impl DomainEvent for OrderingEvent {
@ -32,6 +33,7 @@ impl DomainEvent for OrderingEvent {
OrderingEvent::LineItemDeleted { .. } => "OrderingLineItemDeleted", OrderingEvent::LineItemDeleted { .. } => "OrderingLineItemDeleted",
OrderingEvent::OrderAdded { .. } => "OrderingOrderAdded", OrderingEvent::OrderAdded { .. } => "OrderingOrderAdded",
OrderingEvent::OrderUpdated { .. } => "OrderingOrderUpdated", OrderingEvent::OrderUpdated { .. } => "OrderingOrderUpdated",
OrderingEvent::OrderDeleted { .. } => "OrderingOrderDeleted",
}; };
e.to_string() e.to_string()

View file

@ -12,6 +12,7 @@ pub mod add_line_item_command;
pub mod add_order_command; pub mod add_order_command;
pub mod commands; pub mod commands;
pub mod delete_line_item_command; pub mod delete_line_item_command;
pub mod delete_order_command;
pub mod update_line_item_command; pub mod update_line_item_command;
pub mod update_order_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_deleted_event;
pub mod line_item_updated_event; pub mod line_item_updated_event;
pub mod order_added_event; pub mod order_added_event;
pub mod order_deleted_event;
pub mod order_updated_event; pub mod order_updated_event;

View file

@ -87,6 +87,10 @@ impl Aggregate for Order {
let res = services.update_order().update_order(cmd).await?; let res = services.update_order().update_order(cmd).await?;
Ok(vec![OrderingEvent::OrderUpdated(res)]) 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()), _ => Ok(Vec::default()),
} }
} }
@ -95,7 +99,7 @@ impl Aggregate for Order {
match event { match event {
OrderingEvent::OrderAdded(e) => *self = e.order().clone(), OrderingEvent::OrderAdded(e) => *self = e.order().clone(),
OrderingEvent::OrderUpdated(e) => *self = e.new_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 add_order_service::tests::mock_add_order_service;
use cqrs_es::test::TestFramework; 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 update_order_service::tests::mock_update_order_service;
use super::*; use super::*;
// use crate::ordering::domain::delete_order_command::DeleteOrderCommand; 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_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::order_updated_event::tests::get_updated_order_event_from_command;
use crate::ordering::domain::update_order_command::UpdateOrderCommand; use crate::ordering::domain::update_order_command::UpdateOrderCommand;
use crate::tests::bdd::*; use crate::tests::bdd::*;
@ -159,25 +163,22 @@ mod aggregate_tests {
.when(OrderingCommand::UpdateOrder(cmd)) .when(OrderingCommand::UpdateOrder(cmd))
.then_expect_events(vec![expected]); .then_expect_events(vec![expected]);
} }
//
// #[test] #[test]
// fn test_delete_order() { fn test_delete_order() {
// let cmd = DeleteOrderCommand::get_cmd(); let cmd = DeleteOrderCommand::get_cmd();
// let expected = get_deleted_order_event_from_command(&cmd); let expected = get_deleted_order_event_from_command(&cmd);
// let expected = OrderingEvent::OrderDeleted(expected); let expected = OrderingEvent::OrderDeleted(expected);
//
// let mut services = MockOrderingServicesInterface::new(); let mut services = MockOrderingServicesInterface::new();
// services services
// .expect_delete_order() .expect_delete_order()
// .times(IS_CALLED_ONLY_ONCE.unwrap()) .times(IS_CALLED_ONLY_ONCE.unwrap())
// .return_const(mock_delete_order_service( .return_const(mock_delete_order_service(IS_CALLED_ONLY_ONCE, cmd.clone()));
// IS_CALLED_ONLY_ONCE,
// cmd.clone(), OrderTestFramework::with(Arc::new(services))
// )); .given_no_previous_events()
// .when(OrderingCommand::DeleteOrder(cmd))
// OrderTestFramework::with(Arc::new(services)) .then_expect_events(vec![expected]);
// .given_no_previous_events() }
// .when(OrderingCommand::DeleteOrder(cmd))
// .then_expect_events(vec![expected]);
// }
} }

View file

@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
//
// 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());
}
}