// AUTO-GENERATED by puerto from puerto.toml — never hand-edit. The snapshot follows the entity's
// fields; add an event with `puerto generate event {{ pascal }} <name>`.
use puerto_core::DomainEvent;
use serde::{Deserialize, Serialize};
use uuid::Uuid;{% for import in imports %}
use {{ import }};{% endfor %}
use super::model::{{ pascal }};
{% for e in events %}
pub const {{ e.constant }}: &str = "{{ e.event_type }}";{% endfor %}
/// What every {{ snake }} event carries: the {{ snake }} as the API shows it — primitives, value
/// objects unwrapped. A public contract: consumers decode it with
/// `envelope.decode::<{{ pascal }}Snapshot>()`.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct {{ pascal }}Snapshot {
pub id: Uuid,
{% for f in fields %} pub {{ f.name }}: {{ f.field_type }},
{% endfor %}}
impl {{ pascal }}Snapshot {
pub fn from_entity(entity: &{{ pascal }}) -> Self {
Self {
id: entity.id.value(),
{% for f in fields %} {{ f.name }}: {{ f.accessor }},
{% endfor %} }
}
}
/// What happened to a {{ snake }}. Its write use cases record one through their unit of work.
#[derive(Debug, Clone, PartialEq)]
pub enum {{ pascal }}Event {
{% for e in events %} {{ e.variant }}({{ pascal }}Snapshot),
{% endfor %}}
impl {{ pascal }}Event {
pub fn snapshot(&self) -> &{{ pascal }}Snapshot {
match self {
{% for e in events %}Self::{{ e.variant }}(snapshot){% if not loop.last %} | {% endif %}{% endfor %} => snapshot,
}
}
}
impl DomainEvent for {{ pascal }}Event {
fn event_type(&self) -> &'static str {
match self {
{% for e in events %} Self::{{ e.variant }}(_) => {{ e.constant }},
{% endfor %} }
}
fn aggregate_type(&self) -> &'static str {
"{{ snake }}"
}
fn aggregate_id(&self) -> Uuid {
self.snapshot().id
}
fn schema_version(&self) -> u16 {
1
}
fn payload(&self) -> serde_json::Value {
// A snapshot holds primitives only: serialising it cannot fail.
serde_json::to_value(self.snapshot()).unwrap_or_default()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::mothers::{{ snake }}_mother::{{ pascal }}Mother;
#[test]
fn should_describe_every_event_when_one_is_recorded() {
// Arrange
let entity = {{ pascal }}Mother::random();
let snapshot = {{ pascal }}Snapshot::from_entity(&entity);
let events = [
{% for e in events %} ({{ pascal }}Event::{{ e.variant }}(snapshot.clone()), "{{ e.event_type }}"),
{% endfor %} ];
for (event, event_type) in events {
// Act + Assert
assert_eq!(event.event_type(), event_type);
assert_eq!(event.aggregate_type(), "{{ snake }}");
assert_eq!(event.aggregate_id(), entity.id.value());
assert_eq!(event.schema_version(), 1);
assert_eq!(event.snapshot(), &snapshot);
}
}
#[test]
fn should_keep_the_json_contract_when_the_snapshot_is_serialised() {
// Arrange
let snapshot = {{ pascal }}Snapshot::from_entity(&{{ pascal }}Mother::random());
// Act
let payload = {{ pascal }}Event::{{ events.0.variant }}(snapshot.clone()).payload();
// Assert — a renamed or removed key breaks every consumer of these events.
let mut keys: Vec<&str> = payload
.as_object()
.map(|object| object.keys().map(String::as_str).collect())
.unwrap_or_default();
keys.sort_unstable();
assert_eq!(keys, [{% for key in json_keys %}"{{ key }}"{% if not loop.last %}, {% endif %}{% endfor %}]);
assert_eq!(
serde_json::from_value::<{{ pascal }}Snapshot>(payload).ok(),
Some(snapshot)
);
}
}