feat: order: LineItem add and update cmd,events&service #59
2 changed files with 100 additions and 0 deletions
|
@ -64,6 +64,8 @@ pub struct AddLineItemCommand {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use time::macros::datetime;
|
||||||
|
|
||||||
use crate::utils::uuid::tests::UUID;
|
use crate::utils::uuid::tests::UUID;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -78,6 +80,7 @@ mod tests {
|
||||||
UnvalidatedAddLineItemCommandBuilder::default()
|
UnvalidatedAddLineItemCommandBuilder::default()
|
||||||
.product_name(product_name.into())
|
.product_name(product_name.into())
|
||||||
.adding_by(adding_by)
|
.adding_by(adding_by)
|
||||||
|
.sale_time(datetime!(1970-01-01 0:00 UTC))
|
||||||
.quantity(quantity.clone())
|
.quantity(quantity.clone())
|
||||||
.product_id(product_id)
|
.product_id(product_id)
|
||||||
.build()
|
.build()
|
||||||
|
|
|
@ -10,6 +10,10 @@ use serde::{Deserialize, Serialize};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::ordering::{
|
||||||
|
application::services::{errors::*, *},
|
||||||
|
domain::{commands::*, events::*},
|
||||||
|
};
|
||||||
use crate::types::quantity::Quantity;
|
use crate::types::quantity::Quantity;
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
|
@ -26,6 +30,19 @@ pub struct LineItem {
|
||||||
deleted: bool,
|
deleted: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for LineItem {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
sale_time: OffsetDateTime::now_utc(),
|
||||||
|
product_name: String::default(),
|
||||||
|
product_id: Default::default(),
|
||||||
|
line_item_id: Default::default(),
|
||||||
|
quantity: Default::default(),
|
||||||
|
deleted: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -49,3 +66,83 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Aggregate for LineItem {
|
||||||
|
type Command = OrderingCommand;
|
||||||
|
type Event = OrderingEvent;
|
||||||
|
type Error = OrderingError;
|
||||||
|
type Services = std::sync::Arc<dyn OrderingServicesInterface>;
|
||||||
|
|
||||||
|
// This identifier should be unique to the system.
|
||||||
|
fn aggregate_type() -> String {
|
||||||
|
"ordering.line_item".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
// The aggregate logic goes here. Note that this will be the _bulk_ of a CQRS system
|
||||||
|
// so expect to use helper functions elsewhere to keep the code clean.
|
||||||
|
async fn handle(
|
||||||
|
&self,
|
||||||
|
command: Self::Command,
|
||||||
|
services: &Self::Services,
|
||||||
|
) -> Result<Vec<Self::Event>, Self::Error> {
|
||||||
|
match command {
|
||||||
|
OrderingCommand::AddLineItem(cmd) => {
|
||||||
|
let res = services.add_line_item().add_line_item(cmd).await?;
|
||||||
|
Ok(vec![OrderingEvent::LineItemAdded(res)])
|
||||||
|
}
|
||||||
|
OrderingCommand::UpdateLineItem(cmd) => {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
OrderingCommand::DeleteLineItem(cmd) => {
|
||||||
|
unimplemented!()
|
||||||
|
} // _ => Ok(Vec::default()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply(&mut self, event: Self::Event) {
|
||||||
|
match event {
|
||||||
|
OrderingEvent::LineItemAdded(e) => *self = e.line_item().clone(),
|
||||||
|
OrderingEvent::LineItemUpdated(e) => *self = e.new_line_item().clone(),
|
||||||
|
OrderingEvent::LineItemDeleted(e) => *self = e.line_item().clone(),
|
||||||
|
// _ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod aggregate_tests {
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use add_line_item_service::tests::mock_add_line_item_service;
|
||||||
|
use cqrs_es::test::TestFramework;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use crate::tests::bdd::*;
|
||||||
|
|
||||||
|
use crate::ordering::domain::{
|
||||||
|
add_line_item_command::*,
|
||||||
|
line_item_added_event::tests::get_added_line_item_event_from_command,
|
||||||
|
};
|
||||||
|
|
||||||
|
type LineItemTestFramework = TestFramework<LineItem>;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create_product() {
|
||||||
|
let cmd = AddLineItemCommand::get_cmd();
|
||||||
|
let expected = get_added_line_item_event_from_command(&cmd);
|
||||||
|
let expected = OrderingEvent::LineItemAdded(expected);
|
||||||
|
|
||||||
|
let mut services = MockOrderingServicesInterface::new();
|
||||||
|
services
|
||||||
|
.expect_add_line_item()
|
||||||
|
.times(IS_CALLED_ONLY_ONCE.unwrap())
|
||||||
|
.return_const(mock_add_line_item_service(IS_CALLED_ONLY_ONCE, cmd.clone()));
|
||||||
|
|
||||||
|
LineItemTestFramework::with(Arc::new(services))
|
||||||
|
.given_no_previous_events()
|
||||||
|
.when(OrderingCommand::AddLineItem(cmd))
|
||||||
|
.then_expect_events(vec![expected]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue