diff --git a/.gitignore b/.gitignore index ea8c4bf..de231e1 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ +/messages /target diff --git a/src/model/sns.rs b/src/model/sns.rs index 688cd25..d0f9bf0 100644 --- a/src/model/sns.rs +++ b/src/model/sns.rs @@ -4,7 +4,7 @@ //! the AWS Documentation. //! //! [0]: https://docs.aws.amazon.com/sns/latest/dg/sns-message-and-json-formats.html -use serde::Deserialize; +use serde::{Deserialize, Serialize}; /// Union for all known SNS message types /// @@ -24,7 +24,7 @@ pub enum Message { /// See also: [HTTP/HTTPS subscription confirmation JSON format][0] /// /// [0]: https://docs.aws.amazon.com/sns/latest/dg/sns-message-and-json-formats.html#http-subscription-confirmation-json -#[derive(Deserialize)] +#[derive(Deserialize, Serialize)] #[serde(rename_all = "PascalCase")] pub struct SubscriptionConfirmationMessage { #[serde(rename = "MessageId")] @@ -46,7 +46,7 @@ pub struct SubscriptionConfirmationMessage { /// See also: [HTTP/HTTPS notification JSON format][0] /// /// [0]: https://docs.aws.amazon.com/sns/latest/dg/sns-message-and-json-formats.html#http-notification-json -#[derive(Deserialize)] +#[derive(Deserialize, Serialize)] #[serde(rename_all = "PascalCase")] pub struct NotificationMessage { #[serde(rename = "MessageId")] @@ -67,7 +67,7 @@ pub struct NotificationMessage { /// See also: [HTTP/HTTPS unsubscribe confirmation JSON format][0] /// /// [0]: https://docs.aws.amazon.com/sns/latest/dg/sns-message-and-json-formats.html#http-unsubscribe-confirmation-json -#[derive(Deserialize)] +#[derive(Deserialize, Serialize)] #[serde(rename_all = "PascalCase")] pub struct UnsubscribeConfirmationMessage { #[serde(rename = "MessageId")] diff --git a/src/sns/mod.rs b/src/sns/mod.rs index fd8f639..f341775 100644 --- a/src/sns/mod.rs +++ b/src/sns/mod.rs @@ -2,8 +2,11 @@ pub mod error; pub mod sig; +use std::path::PathBuf; + use log::{debug, error, info}; use reqwest::Url; +use serde::Serialize; use crate::model::sns::*; use error::SnsError; @@ -29,6 +32,7 @@ pub async fn handle_unsubscribe( msg: UnsubscribeConfirmationMessage, ) -> Result<(), SnsError> { verify(&msg, &msg.signing_cert_url).await?; + save_message(&msg.topic_arn, &msg.timestamp, &msg.message_id, &msg); Ok(()) } @@ -38,6 +42,7 @@ pub async fn handle_unsubscribe( /// a file for later inspection. pub async fn handle_notify(msg: NotificationMessage) -> Result<(), SnsError> { verify(&msg, &msg.signing_cert_url).await?; + save_message(&msg.topic_arn, &msg.timestamp, &msg.message_id, &msg); Ok(()) } @@ -141,3 +146,51 @@ async fn confirm_subscription(msg: &SubscriptionConfirmationMessage) { } } } + +fn save_message( + topic: &str, + timestamp: &str, + msgid: &str, + msg: &T, +) { + let mut path = PathBuf::from("messages"); + path.push(topic); + if !path.is_dir() { + if let Err(e) = std::fs::create_dir_all(&path) { + error!("Could not create message storage directory: {}", e); + return; + } + } + path.push(format!("{}.{}", timestamp, msgid)); + match serde_json::to_string(msg) { + Ok(data) => match std::fs::write(&path, &data) { + Ok(_) => info!( + "Wrote message {} to {}", + &msgid, + &path.to_string_lossy() + ), + Err(e) => error!("Could not save message: {}", e), + }, + Err(e) => error!("Failed to serialize message: {}", e), + }; +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_save_message() { + let msg = NotificationMessage { + message_id: "f0bc6e06-24d1-471d-a828-8411c9873996".into(), + topic_arn: "arn:aws:sns:us-east-2-566967686773:dchtest1".into(), + message: "Hello, world!".into(), + subject: None, + timestamp: "2022-09-05T214:29:34.232Z".into(), + signature_version: "0".into(), + signature: "".into(), + signing_cert_url: "".into(), + }; + save_message(&msg.topic_arn, &msg.timestamp, &msg.message_id, &msg); + } +}