227 lines
7.2 KiB
Rust
227 lines
7.2 KiB
Rust
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
|
//
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
use std::str::FromStr;
|
|
|
|
use async_trait::async_trait;
|
|
use cqrs_es::persist::{PersistenceError, ViewContext, ViewRepository};
|
|
use cqrs_es::{EventEnvelope, Query, View};
|
|
use serde::{Deserialize, Serialize};
|
|
use uuid::Uuid;
|
|
|
|
use super::errors::*;
|
|
use super::InventoryDBPostgresAdapter;
|
|
use crate::inventory::domain::{customization_aggregate::*, events::InventoryEvent};
|
|
use crate::utils::parse_aggregate_id::parse_aggregate_id;
|
|
|
|
pub const NEW_CUSTOMIZATION_NON_UUID: &str = "new_customization_non_uuid-asdfa";
|
|
|
|
//#[derive(Debug, Default, Serialize, Deserialize)]
|
|
//struct Customizations {
|
|
// customizations: Vec<CustomizationView>,
|
|
//}
|
|
|
|
#[derive(Debug, Default, Serialize, Deserialize)]
|
|
struct CustomizationView {
|
|
name: String,
|
|
product_id: Uuid,
|
|
customization_id: Uuid,
|
|
deleted: bool,
|
|
}
|
|
|
|
impl From<CustomizationView> for Customization {
|
|
fn from(v: CustomizationView) -> Self {
|
|
CustomizationBuilder::default()
|
|
.name(v.name)
|
|
.customization_id(v.customization_id)
|
|
.product_id(v.product_id)
|
|
.deleted(v.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.
|
|
impl View<Customization> for CustomizationView {
|
|
fn update(&mut self, event: &EventEnvelope<Customization>) {
|
|
match &event.payload {
|
|
InventoryEvent::CustomizationAdded(val) => {
|
|
self.name = val.customization().name().into();
|
|
self.product_id = *val.customization().product_id();
|
|
self.customization_id = *val.customization().customization_id();
|
|
|
|
self.deleted = false;
|
|
}
|
|
InventoryEvent::CustomizationUpdated(e) => {
|
|
let new = e.new_customization();
|
|
self.name = new.name().into();
|
|
self.product_id = *new.product_id();
|
|
self.customization_id = *new.customization_id();
|
|
}
|
|
_ => (),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl ViewRepository<CustomizationView, Customization> for InventoryDBPostgresAdapter {
|
|
async fn load(
|
|
&self,
|
|
customization_id: &str,
|
|
) -> Result<Option<CustomizationView>, PersistenceError> {
|
|
let customization_id =
|
|
match parse_aggregate_id(customization_id, NEW_CUSTOMIZATION_NON_UUID)? {
|
|
Some((val, _)) => return Ok(Some(val)),
|
|
None => Uuid::parse_str(customization_id).unwrap(),
|
|
};
|
|
|
|
let res = sqlx::query_as!(
|
|
CustomizationView,
|
|
"SELECT
|
|
name,
|
|
customization_id,
|
|
product_id,
|
|
deleted
|
|
FROM
|
|
cqrs_inventory_product_customizations_query
|
|
WHERE
|
|
customization_id = $1;",
|
|
customization_id
|
|
)
|
|
.fetch_one(&self.pool)
|
|
.await
|
|
.map_err(PostgresAggregateError::from)?;
|
|
|
|
// let customizations = res.get_customizations(&self).await?;
|
|
Ok(Some(res))
|
|
}
|
|
|
|
async fn load_with_context(
|
|
&self,
|
|
customization_id: &str,
|
|
) -> Result<Option<(CustomizationView, ViewContext)>, PersistenceError> {
|
|
let customization_id =
|
|
match parse_aggregate_id(customization_id, NEW_CUSTOMIZATION_NON_UUID)? {
|
|
Some(val) => return Ok(Some(val)),
|
|
None => Uuid::parse_str(customization_id).unwrap(),
|
|
};
|
|
|
|
let res = sqlx::query_as!(
|
|
CustomizationView,
|
|
"SELECT
|
|
name,
|
|
customization_id,
|
|
product_id,
|
|
deleted
|
|
FROM
|
|
cqrs_inventory_product_customizations_query
|
|
WHERE
|
|
customization_id = $1;",
|
|
customization_id
|
|
)
|
|
.fetch_one(&self.pool)
|
|
.await
|
|
.map_err(PostgresAggregateError::from)?;
|
|
|
|
// let customizations = res.get_customizations(&self).await?;
|
|
|
|
struct Context {
|
|
version: i64,
|
|
customization_id: Uuid,
|
|
}
|
|
|
|
let ctx = sqlx::query_as!(
|
|
Context,
|
|
"SELECT
|
|
customization_id, version
|
|
FROM
|
|
cqrs_inventory_product_customizations_query
|
|
WHERE
|
|
customization_id = $1;",
|
|
customization_id
|
|
)
|
|
.fetch_one(&self.pool)
|
|
.await
|
|
.map_err(PostgresAggregateError::from)?;
|
|
|
|
let view_context = ViewContext::new(ctx.customization_id.to_string(), ctx.version);
|
|
Ok(Some((res, view_context)))
|
|
}
|
|
|
|
async fn update_view(
|
|
&self,
|
|
view: CustomizationView,
|
|
context: ViewContext,
|
|
) -> Result<(), PersistenceError> {
|
|
match context.version {
|
|
0 => {
|
|
let version = context.version + 1;
|
|
sqlx::query!(
|
|
"INSERT INTO cqrs_inventory_product_customizations_query (
|
|
version,
|
|
name,
|
|
customization_id,
|
|
product_id,
|
|
deleted
|
|
) VALUES (
|
|
$1, $2, $3, $4, $5
|
|
);",
|
|
version,
|
|
view.name,
|
|
view.customization_id,
|
|
view.product_id,
|
|
view.deleted,
|
|
)
|
|
.execute(&self.pool)
|
|
.await
|
|
.map_err(PostgresAggregateError::from)?;
|
|
}
|
|
_ => {
|
|
let version = context.version + 1;
|
|
sqlx::query!(
|
|
"UPDATE
|
|
cqrs_inventory_product_customizations_query
|
|
SET
|
|
version = $1,
|
|
name = $2,
|
|
customization_id = $3,
|
|
product_id = $4,
|
|
deleted = $5;",
|
|
version,
|
|
view.name,
|
|
view.customization_id,
|
|
view.product_id,
|
|
view.deleted,
|
|
)
|
|
.execute(&self.pool)
|
|
.await
|
|
.map_err(PostgresAggregateError::from)?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl Query<Customization> for InventoryDBPostgresAdapter {
|
|
async fn dispatch(&self, customization_id: &str, events: &[EventEnvelope<Customization>]) {
|
|
let res = self
|
|
.load_with_context(&customization_id)
|
|
.await
|
|
.unwrap_or_else(|_| {
|
|
Some((
|
|
CustomizationView::default(),
|
|
ViewContext::new(customization_id.into(), 0),
|
|
))
|
|
});
|
|
let (mut view, view_context): (CustomizationView, ViewContext) = res.unwrap();
|
|
for event in events {
|
|
view.update(event);
|
|
}
|
|
self.update_view(view, view_context).await.unwrap();
|
|
}
|
|
}
|