Skip to content

Commit

Permalink
Add SQS API event structs
Browse files Browse the repository at this point in the history
Adds strucs to allow serializing data coming from the AWS SQS API.

Fixes awslabs#710

Signed-off-by: Rémy Greinhofer <[email protected]>
  • Loading branch information
rgreinho committed Oct 23, 2023
1 parent b9d64e8 commit 88f620e
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 0 deletions.
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=="
}
]
}

0 comments on commit 88f620e

Please sign in to comment.