feat: implement Aggregate for LineItem aggregate
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful

This commit is contained in:
Aravinth Manivannan 2024-07-23 17:43:14 +05:30
parent 1412cb19ec
commit ebcdc25e90
Signed by: realaravinth
GPG key ID: F8F50389936984FF
2 changed files with 100 additions and 0 deletions

View file

@ -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()

View file

@ -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]);
}
}