use std::fmt;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Clone, Serialize, Deserialize)]
pub struct Event {
pub event_id: String,
pub topic_name: String,
pub topic_key: Option<String>,
pub seq: i64,
pub actor_json: serde_json::Value,
pub payload_json: serde_json::Value,
pub created_at: DateTime<Utc>,
}
impl fmt::Debug for Event {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Event")
.field("event_id", &self.event_id)
.field("topic_name", &self.topic_name)
.field("topic_key", &self.topic_key)
.field("seq", &self.seq)
.field("actor_json", &"<redacted>")
.field("payload_json", &"<redacted>")
.field("created_at", &self.created_at)
.finish()
}
}
#[derive(Debug, Clone)]
pub struct Envelope<T> {
pub event: Event,
pub payload: T,
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::TimeZone;
#[test]
fn debug_redacts_actor_and_payload() {
let event = Event {
event_id: "e1".into(),
topic_name: "t".into(),
topic_key: None,
seq: 1,
actor_json: serde_json::json!({"secret": "actor-secret"}),
payload_json: serde_json::json!({"secret": "payload-secret"}),
created_at: Utc.timestamp_opt(0, 0).unwrap(),
};
let dbg = format!("{event:?}");
assert!(dbg.contains("<redacted>"));
assert!(!dbg.contains("actor-secret"));
assert!(!dbg.contains("payload-secret"));
assert!(dbg.contains("e1"));
}
}