Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 75 additions & 2 deletions lambda-events/src/event/sns/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,24 @@ pub struct SnsMessage {
pub signing_cert_url: String,

/// A URL that you can use to unsubscribe the endpoint from this topic. If you visit this URL, Amazon SNS unsubscribes the endpoint and stops sending notifications to this endpoint.
///
/// Note: This field is only present in Notification messages. It is not present in SubscriptionConfirmation or UnsubscribeConfirmation messages.
#[serde(alias = "UnsubscribeURL")]
pub unsubscribe_url: String,
#[serde(default)]
pub unsubscribe_url: Option<String>,

/// A URL that you can visit to re-confirm the subscription or confirm the unsubscription.
///
/// Note: This field is only present in SubscriptionConfirmation and UnsubscribeConfirmation messages.
#[serde(alias = "SubscribeURL")]
#[serde(default)]
pub subscribe_url: Option<String>,

/// A value you can use with the ConfirmSubscription action to re-confirm the subscription.
///
/// Note: This field is only present in SubscriptionConfirmation and UnsubscribeConfirmation messages.
#[serde(default)]
pub token: Option<String>,

/// The Message value specified when the notification was published to the topic.
pub message: String,
Expand Down Expand Up @@ -205,8 +221,24 @@ pub struct SnsMessageObj<T: Serialize> {
pub signing_cert_url: String,

/// A URL that you can use to unsubscribe the endpoint from this topic. If you visit this URL, Amazon SNS unsubscribes the endpoint and stops sending notifications to this endpoint.
///
/// Note: This field is only present in Notification messages. It is not present in SubscriptionConfirmation or UnsubscribeConfirmation messages.
#[serde(alias = "UnsubscribeURL")]
pub unsubscribe_url: String,
#[serde(default)]
pub unsubscribe_url: Option<String>,

/// A URL that you can visit to re-confirm the subscription or confirm the unsubscription.
///
/// Note: This field is only present in SubscriptionConfirmation and UnsubscribeConfirmation messages.
#[serde(alias = "SubscribeURL")]
#[serde(default)]
pub subscribe_url: Option<String>,

/// A value you can use with the ConfirmSubscription action to re-confirm the subscription.
///
/// Note: This field is only present in SubscriptionConfirmation and UnsubscribeConfirmation messages.
#[serde(default)]
pub token: Option<String>,

/// Deserialized into a `T` from nested JSON inside the SNS message string. `T` must implement the `Deserialize` or `DeserializeOwned` trait.
#[serde_as(as = "serde_with::json::JsonString")]
Expand Down Expand Up @@ -462,4 +494,45 @@ mod test {
let reparsed: SnsEventObj<CustStruct> = serde_json::from_slice(output.as_bytes()).unwrap();
assert_eq!(parsed, reparsed);
}

#[test]
#[cfg(feature = "sns")]
fn my_example_sns_subscription_confirmation() {
// Test for issue #966: SnsMessage struct fails with SubscriptionConfirmation types
// SubscriptionConfirmation messages have SubscribeURL and Token fields instead of UnsubscribeURL
let data = include_bytes!("../../fixtures/example-sns-subscription-confirmation.json");
let parsed: SnsEvent = serde_json::from_slice(data).unwrap();
assert_eq!(1, parsed.records.len());

let sns_message = &parsed.records[0].sns;
assert_eq!("SubscriptionConfirmation", sns_message.sns_message_type);
assert!(sns_message.unsubscribe_url.is_none());
assert!(sns_message.subscribe_url.is_some());
assert!(sns_message.token.is_some());
assert_eq!(
"https://sns.us-east-1.amazonaws.com/?Action=ConfirmSubscription&TopicArn=arn:aws:sns:us-east-1:123456789012:MyTopic&Token=2336412f37fb687f5d51e6e2425dacbbffff",
sns_message.subscribe_url.as_ref().unwrap()
);
assert_eq!(
"2336412f37fb687f5d51e6e2425dacbbffff",
sns_message.token.as_ref().unwrap()
);

let output: String = serde_json::to_string(&parsed).unwrap();
let reparsed: SnsEvent = serde_json::from_slice(output.as_bytes()).unwrap();
assert_eq!(parsed, reparsed);
}

#[test]
#[cfg(feature = "sns")]
fn my_example_sns_notification_has_unsubscribe_url() {
// Verify that Notification messages still have unsubscribe_url
let data = include_bytes!("../../fixtures/example-sns-event.json");
let parsed: SnsEvent = serde_json::from_slice(data).unwrap();
let sns_message = &parsed.records[0].sns;
assert_eq!("Notification", sns_message.sns_message_type);
assert!(sns_message.unsubscribe_url.is_some());
assert!(sns_message.subscribe_url.is_none());
assert!(sns_message.token.is_none());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"Records": [
{
"EventVersion": "1.0",
"EventSubscriptionArn": "arn:aws:sns:us-east-1:123456789012:MyTopic:00000000-0000-0000-0000-000000000000",
"EventSource": "aws:sns",
"Sns": {
"Type": "SubscriptionConfirmation",
"MessageId": "165545c9-2a5c-472c-8df2-7ff2be2b3b1b",
"TopicArn": "arn:aws:sns:us-east-1:123456789012:MyTopic",
"Message": "You have chosen to subscribe to the topic arn:aws:sns:us-east-1:123456789012:MyTopic.\nTo confirm the subscription, visit the SubscribeURL included in this message.",
"Timestamp": "2012-04-26T20:45:04.751Z",
"SignatureVersion": "1",
"Signature": "EXAMPLEpH+DcEwjAPg8O9mY8dReBSwksfg2S7WKQcikcNKWLQjwu6A4VbeS0QHVCkhRS7fUQvi2egU3N858fiTDN6bkkOxYDVrY0Ad8L10Hs3zH81mtnPk5uvvolIC1CXGu43obcgFxeL3khZl8IKvO61GWB6jI9b5+gLPoBc1Q=",
"SigningCertURL": "https://sns.us-east-1.amazonaws.com/SimpleNotificationService-f3ecfb7224c7233fe7bb5f59f96de52f.pem",
"SubscribeURL": "https://sns.us-east-1.amazonaws.com/?Action=ConfirmSubscription&TopicArn=arn:aws:sns:us-east-1:123456789012:MyTopic&Token=2336412f37fb687f5d51e6e2425dacbbffff",
"Token": "2336412f37fb687f5d51e6e2425dacbbffff",
"Subject": null,
"MessageAttributes": {}
}
}
]
}