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-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
// 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