use serde::{Deserialize, Serialize};
use super::{InternalVerb, VerbCategory};
pub struct MessagingEmailSend;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
pub struct MessagingEmailSendInput {
pub from: String,
pub to: Vec<String>,
pub subject: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub html: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub text: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reply_to: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
pub struct MessagingEmailSendOutput {
pub id: String,
}
impl InternalVerb for MessagingEmailSend {
type Input = MessagingEmailSendInput;
type Output = MessagingEmailSendOutput;
const ID: &'static str = "messaging.email.send";
const CATEGORY: VerbCategory = VerbCategory::Messaging;
}
pub struct MessagingSmsSend;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
pub struct MessagingSmsSendInput {
pub from: String,
pub to: String,
pub body: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
pub struct MessagingSmsSendOutput {
pub id: String,
pub status: String,
}
impl InternalVerb for MessagingSmsSend {
type Input = MessagingSmsSendInput;
type Output = MessagingSmsSendOutput;
const ID: &'static str = "messaging.sms.send";
const CATEGORY: VerbCategory = VerbCategory::Messaging;
}
pub struct MessagingWebhookDispatch;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
pub struct MessagingWebhookDispatchInput {
pub url: String,
pub payload: serde_json::Value,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub headers: Option<std::collections::BTreeMap<String, String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
pub struct MessagingWebhookDispatchOutput {
pub status_code: u16,
pub ok: bool,
}
impl InternalVerb for MessagingWebhookDispatch {
type Input = MessagingWebhookDispatchInput;
type Output = MessagingWebhookDispatchOutput;
const ID: &'static str = "messaging.webhook.dispatch";
const CATEGORY: VerbCategory = VerbCategory::Messaging;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn verb_ids_match_canonical_namespace() {
for id in [
MessagingEmailSend::ID,
MessagingSmsSend::ID,
MessagingWebhookDispatch::ID,
] {
assert!(id.starts_with("messaging."), "{id}");
}
}
#[test]
fn verbs_are_under_messaging_category() {
assert_eq!(MessagingEmailSend::CATEGORY, VerbCategory::Messaging);
assert_eq!(MessagingSmsSend::CATEGORY, VerbCategory::Messaging);
assert_eq!(MessagingWebhookDispatch::CATEGORY, VerbCategory::Messaging);
}
#[test]
fn email_send_optional_fields_omitted() {
let input = MessagingEmailSendInput {
from: "noreply@yah.dev".into(),
to: vec!["user@example.com".into()],
subject: "Hello".into(),
html: Some("<p>Hi</p>".into()),
text: None,
reply_to: None,
};
let wire = serde_json::to_value(&input).unwrap();
assert!(!wire.as_object().unwrap().contains_key("text"));
assert!(!wire.as_object().unwrap().contains_key("reply_to"));
assert_eq!(wire["html"], "<p>Hi</p>");
}
#[test]
fn email_send_to_is_vec() {
let wire = r#"{"from":"a@b.com","to":["x@y.com","z@w.com"],"subject":"S"}"#;
let parsed: MessagingEmailSendInput = serde_json::from_str(wire).unwrap();
assert_eq!(parsed.to.len(), 2);
}
#[test]
fn sms_send_round_trips() {
let input = MessagingSmsSendInput {
from: "+15550100".into(),
to: "+15550101".into(),
body: "Hello".into(),
};
let wire = serde_json::to_string(&input).unwrap();
let back: MessagingSmsSendInput = serde_json::from_str(&wire).unwrap();
assert_eq!(back.to, "+15550101");
}
#[test]
fn webhook_dispatch_payload_accepts_arbitrary_json() {
let wire = r#"{"url":"https://hook.example.com","payload":{"event":"deploy","sha":"abc"}}"#;
let parsed: MessagingWebhookDispatchInput = serde_json::from_str(wire).unwrap();
assert_eq!(parsed.payload["event"], "deploy");
assert!(parsed.headers.is_none());
}
#[test]
fn webhook_dispatch_output_ok_reflects_status() {
let ok = MessagingWebhookDispatchOutput {
status_code: 200,
ok: true,
};
let err = MessagingWebhookDispatchOutput {
status_code: 503,
ok: false,
};
assert_eq!(serde_json::to_value(&ok).unwrap()["ok"], true);
assert_eq!(serde_json::to_value(&err).unwrap()["status_code"], 503);
}
#[cfg(feature = "json-schema")]
#[test]
fn verbs_emit_schemas_via_for_verb() {
use super::super::VerbDescriptor;
let email = VerbDescriptor::for_verb::<MessagingEmailSend>();
assert_eq!(email.id, "messaging.email.send");
assert!(email.input_schema.to_string().contains("subject"));
let sms = VerbDescriptor::for_verb::<MessagingSmsSend>();
assert_eq!(sms.id, "messaging.sms.send");
let webhook = VerbDescriptor::for_verb::<MessagingWebhookDispatch>();
assert_eq!(webhook.id, "messaging.webhook.dispatch");
assert!(webhook.output_schema.to_string().contains("status_code"));
}
}