Skip to content

Feature request: Improve Batch tools for SNS->SQS->Lambda triggers #2163

@kylez-ithaka

Description

@kylez-ithaka

Use case

When a lambda is trigger by an SQS Queue, and that Queue itself is given messages from a subscription with an SNS topic, it results in multiple layers of serialization that need to be unwrapped to get to the real message body. The current implementation of Batch will only deserialize the SQS Message body, which leaves you with an SNS Message, that itself has a String body that needt o be deserialized again.

This results in code like

public HandlerSqs() {
        handler = new BatchMessageHandlerBuilder()
                .withSqsBatchHandler()
                .buildWithMessageHandler(this::processSNSMessage, SNSMessage.class);
    }

private String processSNSMessage(SNSMessage snsMessage) {
        MyInput myInput = extractDataFrom(snsMessage.getMessage()).as(MyInput.class);
        return processMessage(myInput);
    }

public SQSBatchResponse handleRequest(SQSEvent input, Context context) {
        return handler.processBatch(input, context);
    }

As well as the SNSMessage class I had to make myself due to case sensitivity that isn't accounted for in the Lambda Events classes

public class SNSMessage {
    @JsonProperty("Type")
    private String type;
    @JsonProperty("MessageId")
    private String messageId;
    @JsonProperty("TopicArn")
    private String topicArn;
    @JsonProperty("Subject")
    private String subject;
    @JsonProperty("Message")
    private String message;
...
}

Example of the input event from hooking up SNS->SQS->Lambda as json

{
  "Records": [
    {
      "messageId": "dummy-message-id",
      "receiptHandle": "dummy-receipt-handle",
      "eventSourceARN": "arn:aws:sqs:region:account-id:dummy-queue",
      "eventSource": "aws:sqs",
      "awsRegion": "dummy-region",
      "body": "{\"Type\": \"Notification\",\"MessageId\": \"dummy-sns-message-id\",\"TopicArn\": \"arn:aws:sns:region:account-id:dummy-topic\",\"Message\": \"{\\\"field\\\": \\\"dummy\\\", \\\"name\\\": \\\"dummy-name\\\"}\",\"Timestamp\": \"2000-01-01T00:00:00.000Z\",\"SignatureVersion\": \"1\",\"Signature\": \"dummy-signature\",\"SigningCertURL\": \"https://sns.region.amazonaws.com/dummy-cert.pem\",\"UnsubscribeURL\": \"https://sns.region.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:region:account-id:dummy-topic:dummy-subscription\"}",
      "md5OfBody": "dummy-md5",
      "attributes": {
        "ApproximateReceiveCount": "1",
        "SentTimestamp": "0000000000000",
        "SenderId": "dummy-sender-id",
        "ApproximateFirstReceiveTimestamp": "0000000000000"
      },
      "messageAttributes": {}
    }
  ]
}

Solution/User Experience

It would be great if this was a 1 step process like the rest, maybe like:

public HandlerSqs() {
    handler = new BatchMessageHandlerBuilder()
                .withSnsToSqsBatchHandler()
                .buildWithMessageHandler(this::processMessage, MyInput.class);
}

public SQSBatchResponse handleRequest(SQSEvent input, Context context) {
        return handler.processBatch(input, context);
    }

Alternative solutions

Alternatively, if the Events library included an `SNSMessage` or such, that actually could deserialize it directly, instead of maintaining a custom class for it.

Acknowledgment

Future readers

Please react with 👍 and your use case to help us understand customer demand.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    Ideas

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions