Skip to main content

notifica_rust_sdk/payload/
mod.rs

1pub mod email;
2pub mod push;
3pub mod request;
4
5use std::collections::HashMap;
6
7use serde::Serialize;
8
9pub use email::EmailPayload;
10pub use push::PushPayload;
11pub use request::RequestPayload;
12
13/// The JSON body posted to `POST /webhook/{tenant_id}/{event_name}`.
14///
15/// All fields are optional — the Notifica service fills any absent fields with
16/// the defaults configured per tenant and event.
17#[derive(Debug, Default, Serialize)]
18pub struct WebhookPayload {
19    #[serde(skip_serializing_if = "Option::is_none")]
20    pub email: Option<EmailPayload>,
21
22    #[serde(skip_serializing_if = "Option::is_none")]
23    pub push: Option<Vec<String>>,
24
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub request: Option<HashMap<String, String>>,
27
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub automation: Option<HashMap<String, String>>,
30}
31
32/// Fluent builder for a notification sent to a specific event name.
33///
34/// # Example
35/// ```rust
36/// use notifica_crate::{EmailPayload, Notification};
37///
38/// let n = Notification::new()
39///     .email(EmailPayload::new("user@example.com").subject("Welcome!"))
40///     .push(vec!["device-token".into()])
41///     .request([("key".into(), "value".into())]);
42/// ```
43#[derive(Debug, Default)]
44pub struct Notification {
45    payload: WebhookPayload,
46}
47
48impl Notification {
49    /// Create an empty notification.
50    pub fn new() -> Self {
51        Self::default()
52    }
53
54    /// Attach an email payload.
55    pub fn email(mut self, p: EmailPayload) -> Self {
56        self.payload.email = Some(p);
57        self
58    }
59
60    /// Attach a push notification payload (device tokens).
61    pub fn push(mut self, tokens: impl Into<Vec<String>>) -> Self {
62        self.payload.push = Some(tokens.into());
63        self
64    }
65
66    /// Attach a request (webhook-forward) payload.
67    pub fn request(mut self, data: impl Into<HashMap<String, String>>) -> Self {
68        self.payload.request = Some(data.into());
69        self
70    }
71
72    /// Attach an automation payload.
73    pub fn automation(mut self, data: impl Into<HashMap<String, String>>) -> Self {
74        self.payload.automation = Some(data.into());
75        self
76    }
77
78    /// Consume the builder and return the inner [`WebhookPayload`].
79    pub(crate) fn into_payload(self) -> WebhookPayload {
80        self.payload
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn empty_notification_serializes_to_empty_object() {
90        let n = Notification::new();
91        let v = serde_json::to_value(n.into_payload()).unwrap();
92        assert_eq!(v, serde_json::json!({}));
93    }
94
95    #[test]
96    fn absent_channels_not_present_in_json() {
97        let n = Notification::new().email(EmailPayload::new("a@b.com"));
98        let v = serde_json::to_value(n.into_payload()).unwrap();
99        assert!(v.get("push").is_none());
100        assert!(v.get("request").is_none());
101        assert!(v.get("automation").is_none());
102        assert!(v["email"]["target_email"] == "a@b.com");
103    }
104
105    #[test]
106    fn all_channels_serialize() {
107        let mut req = HashMap::new();
108        req.insert("k".into(), "v".into());
109
110        let n = Notification::new()
111            .email(EmailPayload::new("a@b.com"))
112            .push(vec!["tok".into()])
113            .request(req.clone())
114            .automation(req);
115
116        let v = serde_json::to_value(n.into_payload()).unwrap();
117        assert!(v.get("email").is_some());
118        assert!(v.get("push").is_some());
119        assert!(v.get("request").is_some());
120        assert!(v.get("automation").is_some());
121    }
122}