diff --git a/.sqlx/query-0ccd86643c13d9d7c793f362aedc2d720d9dda982a0416849bf97291fc645ea5.json b/.sqlx/query-8d62d7b612fd7f323aee586047dec9603f05782f55f72d21fc29954a65622b5d.json similarity index 64% rename from .sqlx/query-0ccd86643c13d9d7c793f362aedc2d720d9dda982a0416849bf97291fc645ea5.json rename to .sqlx/query-8d62d7b612fd7f323aee586047dec9603f05782f55f72d21fc29954a65622b5d.json index f0b010a..6d8947b 100644 --- a/.sqlx/query-0ccd86643c13d9d7c793f362aedc2d720d9dda982a0416849bf97291fc645ea5.json +++ b/.sqlx/query-8d62d7b612fd7f323aee586047dec9603f05782f55f72d21fc29954a65622b5d.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "UPDATE\n cqrs_inventory_store_query\n SET\n version = $1,\n name = $2,\n address = $3,\n store_id = $4,\n owner = $5,\n deleted = $6;", + "query": "UPDATE\n cqrs_inventory_store_query\n SET\n version = $1,\n name = $2,\n address = $3,\n owner = $4,\n deleted = $5;", "describe": { "columns": [], "parameters": { @@ -9,11 +9,10 @@ "Text", "Text", "Uuid", - "Uuid", "Bool" ] }, "nullable": [] }, - "hash": "0ccd86643c13d9d7c793f362aedc2d720d9dda982a0416849bf97291fc645ea5" + "hash": "8d62d7b612fd7f323aee586047dec9603f05782f55f72d21fc29954a65622b5d" } diff --git a/src/inventory/adapters/output/db/postgres/store_view.rs b/src/inventory/adapters/output/db/postgres/store_view.rs index 6c114c2..ced26ec 100644 --- a/src/inventory/adapters/output/db/postgres/store_view.rs +++ b/src/inventory/adapters/output/db/postgres/store_view.rs @@ -11,7 +11,7 @@ use uuid::Uuid; use super::errors::*; use super::InventoryDBPostgresAdapter; use crate::inventory::domain::events::InventoryEvent; -use crate::inventory::domain::store_aggregate::Store; +use crate::inventory::domain::store_aggregate::{Store, StoreBuilder}; use crate::utils::parse_aggregate_id::parse_aggregate_id; pub const NEW_STORE_NON_UUID: &str = "new_store_non_uuid-asdfa"; @@ -27,6 +27,19 @@ pub struct StoreView { deleted: bool, } +impl From for Store { + fn from(value: StoreView) -> Self { + StoreBuilder::default() + .name(value.name) + .address(value.address) + .store_id(value.store_id) + .owner(value.owner) + .deleted(value.deleted) + .build() + .unwrap() + } +} + // This updates the view with events as they are committed. // The logic should be minimal here, e.g., don't calculate the account balance, // design the events to carry the balance information instead. @@ -156,13 +169,11 @@ impl ViewRepository for InventoryDBPostgresAdapter { version = $1, name = $2, address = $3, - store_id = $4, - owner = $5, - deleted = $6;", + owner = $4, + deleted = $5;", version, view.name, view.address, - view.store_id, view.owner, view.deleted, ) @@ -192,12 +203,21 @@ impl Query for SimpleLoggingQuery { #[async_trait] impl Query for InventoryDBPostgresAdapter { - async fn dispatch(&self, store_id: &str, events: &[EventEnvelope]) { + async fn dispatch(&self, aggregate_id: &str, events: &[EventEnvelope]) { + println!("Got store ID: {}", aggregate_id); + let res = self - .load_with_context(store_id) + .load_with_context(&aggregate_id) .await - .unwrap_or_else(|_| Some((StoreView::default(), ViewContext::new(store_id.into(), 0)))); + .unwrap_or_else(|_| { + println!("generating default"); + Some(( + StoreView::default(), + ViewContext::new(aggregate_id.into(), 0), + )) + }); let (mut view, view_context): (StoreView, ViewContext) = res.unwrap(); + for event in events { view.update(event); } @@ -205,11 +225,6 @@ impl Query for InventoryDBPostgresAdapter { } } -// Our second query, this one will be handled with Postgres `GenericQuery` -// which will serialize and persist our view after it is updated. It also -// provides a `load` method to deserialize the view on request. -//pub type StoreQuery = GenericQuery; -//pub type StoreQuery = Query; #[cfg(test)] mod tests { @@ -221,31 +236,20 @@ mod tests { db::migrate::*, inventory::{ application::services::{ - add_category_service::tests::mock_add_category_service, - add_customization_service::tests::mock_add_customization_service, - add_product_service::tests::mock_add_product_service, - add_store_service::AddStoreServiceBuilder, - update_category_service::tests::mock_update_category_service, - update_customization_service::tests::mock_update_customization_service, - update_product_service::tests::mock_update_product_service, - update_store_service::tests::mock_update_store_service, InventoryServicesBuilder, - }, - domain::{ - add_category_command::AddCategoryCommand, add_customization_command, - add_product_command::tests::get_command, add_store_command::AddStoreCommand, - commands::InventoryCommand, - update_category_command::tests::get_update_category_command, - update_customization_command::tests::get_update_customization_command, - update_product_command, update_store_command::tests::get_update_store_cmd, + add_store_service::AddStoreServiceBuilder, update_store_service::*, + MockInventoryServicesInterface, }, + domain::add_store_command::*, + domain::commands::InventoryCommand, + domain::update_store_command::*, }, - tests::bdd::IS_NEVER_CALLED, + tests::bdd::*, utils::{random_string::GenerateRandomStringInterface, uuid::tests::UUID}, }; use std::sync::Arc; #[actix_rt::test] - async fn pg_query() { + async fn pg_query_inventory_store_view() { let settings = crate::settings::tests::get_settings().await; //let settings = crate::settings::Settings::new().unwrap(); settings.create_db().await; @@ -259,61 +263,101 @@ mod tests { let queries: Vec>> = vec![Box::new(simple_query), Box::new(db.clone())]; - let services = InventoryServicesBuilder::default() - .add_store(Arc::new( - AddStoreServiceBuilder::default() - .db_store_id_exists(Arc::new(db.clone())) - .db_store_name_exists(Arc::new(db.clone())) - .get_uuid(Arc::new(crate::utils::uuid::GenerateUUID {})) - .build() - .unwrap(), - )) - .add_category(mock_add_category_service( - IS_NEVER_CALLED, - AddCategoryCommand::new("foo".into(), None, UUID, UUID).unwrap(), - )) - .add_product(mock_add_product_service(IS_NEVER_CALLED, get_command())) - .add_customization(mock_add_customization_service( - IS_NEVER_CALLED, - add_customization_command::tests::get_command(), - )) - .update_product(mock_update_product_service( - IS_NEVER_CALLED, - update_product_command::tests::get_command(), - )) - .update_customization(mock_update_customization_service( - IS_NEVER_CALLED, - get_update_customization_command(), - )) - .update_category(mock_update_category_service( - IS_NEVER_CALLED, - get_update_category_command(), - )) - .update_store(mock_update_store_service( - IS_NEVER_CALLED, - get_update_store_cmd(), - )) - .build() - .unwrap(); + let mut mock_services = MockInventoryServicesInterface::new(); - let (cqrs, _store_query): ( + let db2 = db.clone(); + mock_services + .expect_add_store() + .times(IS_CALLED_ONLY_ONCE.unwrap()) + .returning(move || { + Arc::new( + AddStoreServiceBuilder::default() + .db_store_id_exists(Arc::new(db2.clone())) + .db_store_name_exists(Arc::new(db2.clone())) + .build() + .unwrap(), + ) + }); + + let db2 = db.clone(); + mock_services + .expect_update_store() + .times(IS_CALLED_ONLY_ONCE.unwrap()) + .returning(move || { + Arc::new( + UpdateStoreServiceBuilder::default() + .db_store_id_exists(Arc::new(db2.clone())) + .db_store_name_exists(Arc::new(db2.clone())) + .build() + .unwrap(), + ) + }); + + let (cqrs, store_query): ( Arc>, Arc>, ) = ( Arc::new(postgres_es::postgres_cqrs( db.pool.clone(), queries, - Arc::new(services), + Arc::new(mock_services), )), Arc::new(db.clone()), ); let rand = crate::utils::random_string::GenerateRandomString {}; - let cmd = AddStoreCommand::new(rand.get_random(10), None, UUID).unwrap(); - cqrs.execute("", InventoryCommand::AddStore(cmd.clone())) - .await + let cmd = AddStoreCommandBuilder::default() + .name(rand.get_random(10)) + .address(None) + .owner(UUID) + .store_id(UUID) + .build() .unwrap(); + cqrs.execute( + &cmd.store_id().to_string(), + InventoryCommand::AddStore(cmd.clone()), + ) + .await + .unwrap(); - settings.drop_db().await; + let store = store_query + .load(&(*cmd.store_id()).to_string()) + .await + .unwrap() + .unwrap(); + let store: Store = store.into(); + assert_eq!(store.name(), cmd.name()); + assert_eq!(store.address(), cmd.address()); + assert_eq!(store.owner(), cmd.owner()); + assert_eq!(store.store_id(), cmd.store_id()); + assert!(!store.deleted()); + + let update_store_cmd = UpdateStoreCommand::new( + rand.get_random(10), + Some(rand.get_random(10)), + UUID, + store, + UUID, + ) + .unwrap(); + cqrs.execute( + &cmd.store_id().to_string(), + InventoryCommand::UpdateStore(update_store_cmd.clone()), + ) + .await + .unwrap(); + let store = store_query + .load(&(*cmd.store_id()).to_string()) + .await + .unwrap() + .unwrap(); + let store: Store = store.into(); + assert_eq!(store.name(), update_store_cmd.name()); + assert_eq!(store.address(), update_store_cmd.address()); + assert_eq!(store.owner(), update_store_cmd.owner()); + assert_eq!(store.store_id(), update_store_cmd.old_store().store_id()); + assert!(!store.deleted()); + + settings.drop_db().await; } }