Skip to content

Add SQS API event structs #711

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 23, 2023
Merged
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
90 changes: 90 additions & 0 deletions lambda-events/src/event/sqs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,74 @@ pub struct BatchItemFailure {
pub item_identifier: String,
}

/// The Event sent to Lambda from the SQS API. Contains 1 or more individual SQS Messages
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "PascalCase")]
#[serde(bound(deserialize = "T: DeserializeOwned"))]
pub struct SqsApiEventObj<T: Serialize> {
#[serde(bound(deserialize = "T: DeserializeOwned"))]
pub messages: Vec<SqsApiMessageObj<T>>,
}

/// The Event sent to Lambda from SQS API. Contains 1 or more individual SQS Messages
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SqsApiEvent {
pub messages: Vec<SqsApiMessage>,
}

/// Alternative to SqsApiEvent to be used alongside SqsApiMessageObj<T> when you need to
/// deserialize a nested object into a struct of type T within the SQS Message rather
/// than just using the raw SQS Message string
#[serde_with::serde_as]
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[serde(bound(deserialize = "T: DeserializeOwned"))]
#[serde(rename_all = "PascalCase")]
pub struct SqsApiMessageObj<T: Serialize> {
/// nolint: stylecheck
#[serde(default)]
pub message_id: Option<String>,
#[serde(default)]
pub receipt_handle: Option<String>,
/// Deserialized into a `T` from nested JSON inside the SQS body string. `T` must implement the `Deserialize` or `DeserializeOwned` trait.
#[serde_as(as = "serde_with::json::JsonString")]
#[serde(bound(deserialize = "T: DeserializeOwned"))]
pub body: T,
#[serde(default)]
pub md5_of_body: Option<String>,
#[serde(default)]
pub md5_of_message_attributes: Option<String>,
#[serde(deserialize_with = "deserialize_lambda_map")]
#[serde(default)]
pub attributes: HashMap<String, String>,
#[serde(deserialize_with = "deserialize_lambda_map")]
#[serde(default)]
pub message_attributes: HashMap<String, SqsMessageAttribute>,
}

/// An individual SQS API Message, its metadata, and Message Attributes
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct SqsApiMessage {
/// nolint: stylecheck
#[serde(default)]
pub message_id: Option<String>,
#[serde(default)]
pub receipt_handle: Option<String>,
#[serde(default)]
pub body: Option<String>,
#[serde(default)]
pub md5_of_body: Option<String>,
#[serde(default)]
pub md5_of_message_attributes: Option<String>,
#[serde(deserialize_with = "deserialize_lambda_map")]
#[serde(default)]
pub attributes: HashMap<String, String>,
#[serde(deserialize_with = "deserialize_lambda_map")]
#[serde(default)]
pub message_attributes: HashMap<String, SqsMessageAttribute>,
}

#[cfg(test)]
mod test {
use super::*;
Expand Down Expand Up @@ -159,4 +227,26 @@ mod test {
let reparsed: SqsBatchResponse = serde_json::from_slice(output.as_bytes()).unwrap();
assert_eq!(parsed, reparsed);
}

#[test]
#[cfg(feature = "sqs")]
fn example_sqs_api_obj_event() {
// Example sqs api receive message response, fetched 2023-10-23, inspired from:
// https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_ReceiveMessage.html#API_ReceiveMessage_ResponseSyntax
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
struct CustStruct {
city: String,
country: String,
}

let data = include_bytes!("../../fixtures/example-sqs-api-event-obj.json");
let parsed: SqsApiEventObj<CustStruct> = serde_json::from_slice(data).unwrap();

assert_eq!(parsed.messages[0].body.city, "provincetown");
assert_eq!(parsed.messages[0].body.country, "usa");

let output: String = serde_json::to_string(&parsed).unwrap();
let reparsed: SqsApiEventObj<CustStruct> = serde_json::from_slice(output.as_bytes()).unwrap();
assert_eq!(parsed, reparsed);
}
}
10 changes: 10 additions & 0 deletions lambda-events/src/fixtures/example-sqs-api-event-obj.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"Messages": [
{
"Body": "{\"country\": \"usa\", \"city\": \"provincetown\"}",
"Md5OfBody": "2b3e4f40b57e80d67ac5b9660c56d787",
"MessageId": "f663a189-97e2-41f5-9c0e-cfb595d8322c",
"ReceiptHandle": "AQEBdObBZIl7FWJiK9c3KmqKNvusy6+eqG51SLIp5Gs6lQ6+e4SI0lJ6Glw+qcOi+2RRrnfOjlsF8uDlo13TgubmtgP+CH7s+YKDdpbg2jA931vLi6qnU0ZFXcf/H8BDZ4kcz29npMu9/N2DT9F+kI9Q9pTfLsISg/7XFMvRTqAtjSfa2wI5TVcOPZBdkGqTLUoKqAYni0L7NTLzFUTjCN/HiOcvG+16zahhsTniM1MwOTSpbOO2uTZmY25V/PCfNdF1PBXtdNA9mWW2Ym6THV28ug3cuK6dXbFQBuxIGVhOq+mRVU6gKN/eZpZediiBt75oHD6ASu8jIUpJGeUWEZm6qSWU+YTivr6QoqGLwAVvI3CXOIZQ/+Wp/RJAxMQxtRIe/MOsOITcmGlFqhWnjlGQdg=="
}
]
}