diff --git a/src/ordering/application/services/add_line_item_service.rs b/src/ordering/application/services/add_line_item_service.rs new file mode 100644 index 0000000..75a7e4e --- /dev/null +++ b/src/ordering/application/services/add_line_item_service.rs @@ -0,0 +1,113 @@ +// 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::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] +pub trait AddLineItemUseCase: Send + Sync { + async fn add_line_item(&self, cmd: AddLineItemCommand) -> OrderingResult; +} + +pub type AddLineItemServiceObj = Arc; + +#[derive(Clone, Builder)] +pub struct AddLineItemService { + db_line_item_id_exists: LineItemIDExistsDBPortObj, + get_uuid: GetUUIDInterfaceObj, +} + +#[async_trait::async_trait] +impl AddLineItemUseCase for AddLineItemService { + async fn add_line_item(&self, cmd: AddLineItemCommand) -> OrderingResult { + 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) + .await? + { + line_item_id = self.get_uuid.get_uuid(); + continue; + } else { + break; + } + } + + let line_item = LineItemBuilder::default() + .sale_time(OffsetDateTime::now_utc()) + .product_name(cmd.product_name().into()) + .product_id(*cmd.product_id()) + .line_item_id(line_item_id) + .quantity(cmd.quantity().clone()) + .deleted(false) + .build() + .unwrap(); + + Ok(LineItemAddedEventBuilder::default() + .added_by_user(*cmd.adding_by()) + .line_item(line_item) + .build() + .unwrap()) + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + + 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}; + + pub fn mock_add_line_item_service( + times: Option, + cmd: AddLineItemCommand, + ) -> AddLineItemServiceObj { + let mut m = MockAddLineItemUseCase::new(); + + let res = get_added_line_item_event_from_command(&cmd); + if let Some(times) = times { + m.expect_add_line_item() + .times(times) + .returning(move |_| Ok(res.clone())); + } else { + m.expect_add_line_item().returning(move |_| Ok(res.clone())); + } + + Arc::new(m) + } + + #[actix_rt::test] + async fn test_service() { + let cmd = AddLineItemCommand::get_cmd(); + + let s = AddLineItemServiceBuilder::default() + .db_line_item_id_exists(mock_line_item_id_exists_db_port_false(IS_CALLED_ONLY_ONCE)) + .get_uuid(mock_get_uuid(IS_CALLED_ONLY_ONCE)) + .build() + .unwrap(); + + let res = s.add_line_item(cmd.clone()).await.unwrap(); + assert_eq!(res.line_item().product_name(), cmd.product_name()); + assert_eq!(res.line_item().product_id(), cmd.product_id()); + assert_eq!(res.line_item().quantity(), cmd.quantity()); + assert_eq!(res.line_item().quantity(), cmd.quantity()); + assert!(!res.line_item().deleted()); + assert_eq!(res.added_by_user(), cmd.adding_by()); + } +} diff --git a/src/ordering/application/services/errors.rs b/src/ordering/application/services/errors.rs new file mode 100644 index 0000000..175d921 --- /dev/null +++ b/src/ordering/application/services/errors.rs @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2024 Aravinth Manivannan +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +use derive_more::{Display, Error}; +use log::error; +use serde::{Deserialize, Serialize}; + +use crate::ordering::application::port::output::db::errors::OrderingDBError; + +pub type OrderingResult = Result; + +#[derive(Debug, Error, Display, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] +pub enum OrderingError { + LineItemIDNotFound, + InternalError, +} +// +impl From for OrderingError { + fn from(value: OrderingDBError) -> Self { + match value { + OrderingDBError::DuplicateLineItemID => { + error!("DuplicateLineItemID"); + Self::InternalError + } + OrderingDBError::LineItemIDNotFound => OrderingError::LineItemIDNotFound, + OrderingDBError::InternalError => Self::InternalError, + } + } +} diff --git a/src/ordering/application/services/mod.rs b/src/ordering/application/services/mod.rs index 56f60de..ac7dd23 100644 --- a/src/ordering/application/services/mod.rs +++ b/src/ordering/application/services/mod.rs @@ -1,3 +1,28 @@ // SPDX-FileCopyrightText: 2024 Aravinth Manivannan // // SPDX-License-Identifier: AGPL-3.0-or-later + +use derive_builder::Builder; +use mockall::predicate::*; +use mockall::*; + +pub mod errors; + +//services +pub mod add_line_item_service; + +#[automock] +pub trait OrderingServicesInterface: Send + Sync { + fn add_line_item(&self) -> add_line_item_service::AddLineItemServiceObj; +} + +#[derive(Clone, Builder)] +pub struct OrderingServices { + add_line_item: add_line_item_service::AddLineItemServiceObj, +} + +impl OrderingServicesInterface for OrderingServices { + fn add_line_item(&self) -> add_line_item_service::AddLineItemServiceObj { + self.add_line_item.clone() + } +}