feat: order: LineItem add and update cmd,events&service #59
3 changed files with 168 additions and 0 deletions
113
src/ordering/application/services/add_line_item_service.rs
Normal file
113
src/ordering/application/services/add_line_item_service.rs
Normal file
|
@ -0,0 +1,113 @@
|
|||
// 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::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<LineItemAddedEvent>;
|
||||
}
|
||||
|
||||
pub type AddLineItemServiceObj = Arc<dyn AddLineItemUseCase>;
|
||||
|
||||
#[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<LineItemAddedEvent> {
|
||||
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<usize>,
|
||||
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());
|
||||
}
|
||||
}
|
30
src/ordering/application/services/errors.rs
Normal file
30
src/ordering/application/services/errors.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// 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<V> = Result<V, OrderingError>;
|
||||
|
||||
#[derive(Debug, Error, Display, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum OrderingError {
|
||||
LineItemIDNotFound,
|
||||
InternalError,
|
||||
}
|
||||
//
|
||||
impl From<OrderingDBError> for OrderingError {
|
||||
fn from(value: OrderingDBError) -> Self {
|
||||
match value {
|
||||
OrderingDBError::DuplicateLineItemID => {
|
||||
error!("DuplicateLineItemID");
|
||||
Self::InternalError
|
||||
}
|
||||
OrderingDBError::LineItemIDNotFound => OrderingError::LineItemIDNotFound,
|
||||
OrderingDBError::InternalError => Self::InternalError,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,28 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue