fix: inventory: aggregate IDs are provided by the caller #112
4 changed files with 89 additions and 49 deletions
|
@ -12,12 +12,11 @@ use super::errors::*;
|
||||||
use crate::inventory::{
|
use crate::inventory::{
|
||||||
application::port::output::db::{store_id_exists::*, store_name_exists::*},
|
application::port::output::db::{store_id_exists::*, store_name_exists::*},
|
||||||
domain::{
|
domain::{
|
||||||
add_store_command::AddStoreCommand,
|
add_store_command::*,
|
||||||
store_added_event::{StoreAddedEvent, StoreAddedEventBuilder},
|
store_added_event::{StoreAddedEvent, StoreAddedEventBuilder},
|
||||||
store_aggregate::*,
|
store_aggregate::*,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use crate::utils::uuid::*;
|
|
||||||
|
|
||||||
#[automock]
|
#[automock]
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
|
@ -31,28 +30,24 @@ pub type AddStoreServiceObj = Arc<dyn AddStoreUseCase>;
|
||||||
pub struct AddStoreService {
|
pub struct AddStoreService {
|
||||||
db_store_id_exists: StoreIDExistsDBPortObj,
|
db_store_id_exists: StoreIDExistsDBPortObj,
|
||||||
db_store_name_exists: StoreNameExistsDBPortObj,
|
db_store_name_exists: StoreNameExistsDBPortObj,
|
||||||
get_uuid: GetUUIDInterfaceObj,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl AddStoreUseCase for AddStoreService {
|
impl AddStoreUseCase for AddStoreService {
|
||||||
async fn add_store(&self, cmd: AddStoreCommand) -> InventoryResult<StoreAddedEvent> {
|
async fn add_store(&self, cmd: AddStoreCommand) -> InventoryResult<StoreAddedEvent> {
|
||||||
let mut store_id = self.get_uuid.get_uuid();
|
if self
|
||||||
|
.db_store_id_exists
|
||||||
loop {
|
.store_id_exists(cmd.store_id())
|
||||||
if self.db_store_id_exists.store_id_exists(&store_id).await? {
|
.await?
|
||||||
store_id = self.get_uuid.get_uuid();
|
{
|
||||||
continue;
|
return Err(InventoryError::DuplicateStoreID);
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let store = StoreBuilder::default()
|
let store = StoreBuilder::default()
|
||||||
.name(cmd.name().into())
|
.name(cmd.name().into())
|
||||||
.address(cmd.address().as_ref().map(|s| s.to_string()))
|
.address(cmd.address().as_ref().map(|s| s.to_string()))
|
||||||
.owner(*cmd.owner())
|
.owner(*cmd.owner())
|
||||||
.store_id(store_id)
|
.store_id(*cmd.store_id())
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -64,7 +59,7 @@ impl AddStoreUseCase for AddStoreService {
|
||||||
.name(store.name().into())
|
.name(store.name().into())
|
||||||
.address(store.address().as_ref().map(|s| s.to_string()))
|
.address(store.address().as_ref().map(|s| s.to_string()))
|
||||||
.owner(*cmd.owner())
|
.owner(*cmd.owner())
|
||||||
.store_id(store_id)
|
.store_id(*cmd.store_id())
|
||||||
.build()
|
.build()
|
||||||
.unwrap())
|
.unwrap())
|
||||||
}
|
}
|
||||||
|
@ -109,12 +104,17 @@ pub mod tests {
|
||||||
let owner = UUID;
|
let owner = UUID;
|
||||||
|
|
||||||
// address = None
|
// address = None
|
||||||
let cmd = AddStoreCommand::new(name.into(), Some(address.into()), owner).unwrap();
|
let cmd = AddStoreCommandBuilder::default()
|
||||||
|
.name(name.into())
|
||||||
|
.address(None)
|
||||||
|
.owner(owner)
|
||||||
|
.store_id(UUID)
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let s = AddStoreServiceBuilder::default()
|
let s = AddStoreServiceBuilder::default()
|
||||||
.db_store_id_exists(mock_store_id_exists_db_port_false(IS_CALLED_ONLY_ONCE))
|
.db_store_id_exists(mock_store_id_exists_db_port_false(IS_CALLED_ONLY_ONCE))
|
||||||
.db_store_name_exists(mock_store_name_exists_db_port_false(IS_CALLED_ONLY_ONCE))
|
.db_store_name_exists(mock_store_name_exists_db_port_false(IS_CALLED_ONLY_ONCE))
|
||||||
.get_uuid(mock_get_uuid(IS_CALLED_ONLY_ONCE))
|
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -132,12 +132,17 @@ pub mod tests {
|
||||||
let owner = UUID;
|
let owner = UUID;
|
||||||
|
|
||||||
// address = None
|
// address = None
|
||||||
let cmd = AddStoreCommand::new(name.into(), Some(address.into()), owner).unwrap();
|
let cmd = AddStoreCommandBuilder::default()
|
||||||
|
.name(name.into())
|
||||||
|
.address(Some(address.into()))
|
||||||
|
.owner(owner)
|
||||||
|
.store_id(UUID)
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let s = AddStoreServiceBuilder::default()
|
let s = AddStoreServiceBuilder::default()
|
||||||
.db_store_id_exists(mock_store_id_exists_db_port_false(IS_CALLED_ONLY_ONCE))
|
.db_store_id_exists(mock_store_id_exists_db_port_false(IS_CALLED_ONLY_ONCE))
|
||||||
.db_store_name_exists(mock_store_name_exists_db_port_true(IS_CALLED_ONLY_ONCE))
|
.db_store_name_exists(mock_store_name_exists_db_port_true(IS_CALLED_ONLY_ONCE))
|
||||||
.get_uuid(mock_get_uuid(IS_CALLED_ONLY_ONCE))
|
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ pub enum InventoryError {
|
||||||
DuplicateStoreName,
|
DuplicateStoreName,
|
||||||
DuplicateProductName,
|
DuplicateProductName,
|
||||||
DuplicateCustomizationName,
|
DuplicateCustomizationName,
|
||||||
|
DuplicateStoreID,
|
||||||
ProductIDNotFound,
|
ProductIDNotFound,
|
||||||
CategoryIDNotFound,
|
CategoryIDNotFound,
|
||||||
CustomizationIDNotFound,
|
CustomizationIDNotFound,
|
||||||
|
@ -32,10 +33,7 @@ impl From<InventoryDBError> for InventoryError {
|
||||||
InventoryDBError::DuplicateStoreName => Self::DuplicateStoreName,
|
InventoryDBError::DuplicateStoreName => Self::DuplicateStoreName,
|
||||||
InventoryDBError::DuplicateProductName => Self::DuplicateProductName,
|
InventoryDBError::DuplicateProductName => Self::DuplicateProductName,
|
||||||
InventoryDBError::DuplicateCustomizationName => Self::DuplicateCustomizationName,
|
InventoryDBError::DuplicateCustomizationName => Self::DuplicateCustomizationName,
|
||||||
InventoryDBError::DuplicateStoreID => {
|
InventoryDBError::DuplicateStoreID => Self::DuplicateStoreID,
|
||||||
error!("DuplicateStoreID");
|
|
||||||
Self::InternalError
|
|
||||||
}
|
|
||||||
InventoryDBError::DuplicateProductID => {
|
InventoryDBError::DuplicateProductID => {
|
||||||
error!("DuplicateProductID");
|
error!("DuplicateProductID");
|
||||||
Self::InternalError
|
Self::InternalError
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
use derive_builder::Builder;
|
||||||
use derive_getters::Getters;
|
use derive_getters::Getters;
|
||||||
use derive_more::{Display, Error};
|
use derive_more::{Display, Error};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -12,46 +13,56 @@ pub enum AddStoreCommandError {
|
||||||
NameIsEmpty,
|
NameIsEmpty,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Getters)]
|
#[derive(
|
||||||
|
Clone, Builder, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Getters,
|
||||||
|
)]
|
||||||
|
#[builder(build_fn(validate = "Self::validate"))]
|
||||||
pub struct AddStoreCommand {
|
pub struct AddStoreCommand {
|
||||||
|
#[builder(setter(custom))]
|
||||||
name: String,
|
name: String,
|
||||||
|
#[builder(setter(custom))]
|
||||||
address: Option<String>,
|
address: Option<String>,
|
||||||
|
store_id: Uuid,
|
||||||
owner: Uuid,
|
owner: Uuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AddStoreCommand {
|
impl AddStoreCommandBuilder {
|
||||||
pub fn new(
|
// pub fn custom_address(&mut self, address: Option<String>) -> &mut Self {
|
||||||
name: String,
|
// fn address(&mut self, address: Option<String>) {
|
||||||
address: Option<String>,
|
pub fn address(&mut self, address: Option<String>) -> &mut Self {
|
||||||
owner: Uuid,
|
self.address = if let Some(address) = address {
|
||||||
) -> Result<Self, AddStoreCommandError> {
|
|
||||||
let address: Option<String> = if let Some(address) = address {
|
|
||||||
let address = address.trim();
|
let address = address.trim();
|
||||||
if address.is_empty() {
|
if address.is_empty() {
|
||||||
None
|
Some(None)
|
||||||
} else {
|
} else {
|
||||||
Some(address.to_owned())
|
Some(Some(address.to_owned()))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
Some(None)
|
||||||
};
|
};
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
let name = name.trim().to_owned();
|
//pub fn custom_name(&mut self, name: String) -> &mut Self {
|
||||||
|
//fn name(&mut self, name: String) {
|
||||||
|
pub fn name(&mut self, name: String) -> &mut Self {
|
||||||
|
self.name = Some(name.trim().to_owned());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate(&self) -> Result<(), String> {
|
||||||
|
let name = self.name.as_ref().unwrap().trim().to_owned();
|
||||||
if name.is_empty() {
|
if name.is_empty() {
|
||||||
return Err(AddStoreCommandError::NameIsEmpty);
|
return Err(AddStoreCommandError::NameIsEmpty.to_string());
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
Ok(Self {
|
|
||||||
name,
|
|
||||||
address,
|
|
||||||
owner,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::utils::uuid::tests::UUID;
|
use crate::tests::bdd::*;
|
||||||
|
use crate::utils::uuid::tests::*;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -62,21 +73,41 @@ mod tests {
|
||||||
let owner = UUID;
|
let owner = UUID;
|
||||||
|
|
||||||
// address = None
|
// address = None
|
||||||
let cmd = AddStoreCommand::new(name.into(), None, owner).unwrap();
|
let cmd = AddStoreCommandBuilder::default()
|
||||||
|
.name(name.into())
|
||||||
|
.address(None)
|
||||||
|
.owner(owner)
|
||||||
|
.store_id(UUID)
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
// let cmd = AddStoreCommand::new(name.into(), None, owner, UUID).unwrap();
|
||||||
assert_eq!(cmd.name(), name);
|
assert_eq!(cmd.name(), name);
|
||||||
assert_eq!(cmd.address(), &None);
|
assert_eq!(cmd.address(), &None);
|
||||||
assert_eq!(cmd.owner(), &owner);
|
assert_eq!(cmd.owner(), &owner);
|
||||||
|
assert_eq!(*cmd.store_id(), UUID);
|
||||||
|
|
||||||
// address = Some
|
// address = Some
|
||||||
let cmd = AddStoreCommand::new(name.into(), Some(address.into()), owner).unwrap();
|
let cmd = AddStoreCommandBuilder::default()
|
||||||
|
.name(name.into())
|
||||||
|
.address(Some(address.into()))
|
||||||
|
.owner(owner)
|
||||||
|
.store_id(UUID)
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
// let cmd = AddStoreCommand::new(name.into(), Some(address.into()), owner, UUID).unwrap();
|
||||||
assert_eq!(cmd.name(), name);
|
assert_eq!(cmd.name(), name);
|
||||||
assert_eq!(cmd.address(), &Some(address.to_owned()));
|
assert_eq!(cmd.address(), &Some(address.to_owned()));
|
||||||
assert_eq!(cmd.owner(), &owner);
|
assert_eq!(cmd.owner(), &owner);
|
||||||
|
assert_eq!(*cmd.store_id(), UUID);
|
||||||
|
|
||||||
// AddStoreCommandError::NameIsEmpty
|
// AddStoreCommandError::NameIsEmpty
|
||||||
assert_eq!(
|
|
||||||
AddStoreCommand::new("".into(), Some(address.into()), owner),
|
assert!(AddStoreCommandBuilder::default()
|
||||||
Err(AddStoreCommandError::NameIsEmpty)
|
.name("".into())
|
||||||
)
|
.address(Some(address.into()))
|
||||||
|
.owner(owner)
|
||||||
|
.store_id(UUID)
|
||||||
|
.build()
|
||||||
|
.is_err())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,7 +113,13 @@ mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let expected = InventoryEvent::StoreAdded(expected);
|
let expected = InventoryEvent::StoreAdded(expected);
|
||||||
|
|
||||||
let cmd = AddStoreCommand::new(name.into(), address.clone(), owner).unwrap();
|
let cmd = AddStoreCommandBuilder::default()
|
||||||
|
.name(name.into())
|
||||||
|
.address(address.clone())
|
||||||
|
.owner(owner)
|
||||||
|
.store_id(UUID)
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let mut services = MockInventoryServicesInterface::new();
|
let mut services = MockInventoryServicesInterface::new();
|
||||||
services
|
services
|
||||||
|
|
Loading…
Reference in a new issue