diff --git a/README.md b/README.md index 70cfab314..47fb59bd7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# Powertools for AWS Lambda (Java) +# Powertools for AWS Lambda (Java) V2 + +**This is pre-release code for Powertools for AWS Lambda (Java) V2! Please check out the `main` branch for the stable release** ![aws provider](https://img.shields.io/badge/provider-AWS-orange?logo=amazon-aws&color=ff9900) ![Build status](https://github.com/aws-powertools/powertools-lambda-java/actions/workflows/build.yml/badge.svg) ![Maven Central](https://img.shields.io/maven-central/v/software.amazon.lambda/powertools-parent) [![codecov.io](https://codecov.io/github/aws-powertools/powertools-lambda-java/branch/main/graphs/badge.svg)](https://app.codecov.io/gh/aws-powertools/powertools-lambda-java) diff --git a/docs/utilities/large_messages.md b/docs/utilities/large_messages.md index c4947a6e8..c0c1cd599 100644 --- a/docs/utilities/large_messages.md +++ b/docs/utilities/large_messages.md @@ -6,12 +6,6 @@ description: Utility The large message utility handles SQS and SNS messages which have had their payloads offloaded to S3 if they are larger than the maximum allowed size (256 KB). -!!! Notice - The large message utility (available in the `powertools-sqs` module for versions v1.16.1 and earlier) is now deprecated - and replaced by the `powertools-large-messages` described in this page. - You can still get the documentation [here](sqs_large_message_handling.md) - and the migration guide [here](#migration-from-the-sqs-large-message-utility). - ## Features - Automatically retrieve the content of S3 objects when SQS or SNS messages have been offloaded to S3. diff --git a/docs/utilities/sqs_batch.md b/docs/utilities/sqs_batch.md deleted file mode 100644 index 658f7b085..000000000 --- a/docs/utilities/sqs_batch.md +++ /dev/null @@ -1,489 +0,0 @@ ---- -title: SQS Batch Processing (Deprecated) -description: Utility ---- - -!!! warning - The SQS batch module is now deprecated and will be removed in v2 of the library. Use the [batch module](batch.md), - and check out **[migrating to the batch library](#migrating-to-the-batch-library)** for migration instructions. - -The SQS batch processing utility provides a way to handle partial failures when processing batches of messages from SQS. -The utility handles batch processing for both -[standard](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/standard-queues.html) and -[FIFO](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/FIFO-queues.html) SQS queues. - -**Key Features** - -* Prevent successfully processed messages from being returned to SQS -* A simple interface for individually processing messages from a batch - -**Background** - -When using SQS as a Lambda event source mapping, Lambda functions can be triggered with a batch of messages from SQS. -If your function fails to process any message from the batch, the entire batch returns to your SQS queue, and your -Lambda function will be triggered with the same batch again. With this utility, messages within a batch will be handled individually - only messages that were not successfully processed -are returned to the queue. - -!!! warning - While this utility lowers the chance of processing messages more than once, it is not guaranteed. We recommend implementing processing logic in an idempotent manner wherever possible. - More details on how Lambda works with SQS can be found in the [AWS documentation](https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html) - -## Install - -Depending on your version of Java (either Java 1.8 or 11+), the configuration slightly changes. - -=== "Maven Java 11+" - - ```xml hl_lines="3-7 16 18 24-27"" - - ... - - software.amazon.lambda - powertools-sqs - {{ powertools.version }} - - ... - - ... - - - - ... - - dev.aspectj - aspectj-maven-plugin - 1.13.1 - - 11 - 11 - 11 - - - software.amazon.lambda - powertools-sqs - - - - - - - compile - - - - - ... - - - ``` - -=== "Maven Java 1.8" - - ```xml hl_lines="3-7 16 18 24-27" - - ... - - software.amazon.lambda - powertools-sqs - {{ powertools.version }} - - ... - - ... - - - - ... - - org.codehaus.mojo - aspectj-maven-plugin - 1.14.0 - - 1.8 - 1.8 - 1.8 - - - software.amazon.lambda - powertools-sqs - - - - - - - compile - - - - - ... - - - ``` - -=== "Gradle Java 11+" - - ```groovy hl_lines="3 11" - plugins { - id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' - } - - repositories { - mavenCentral() - } - - dependencies { - aspect 'software.amazon.lambda:powertools-sqs:{{ powertools.version }}' - } - - sourceCompatibility = 11 // or higher - targetCompatibility = 11 // or higher - ``` - -=== "Gradle Java 1.8" - - ```groovy hl_lines="3 11" - plugins { - id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '6.6.3' - } - - repositories { - mavenCentral() - } - - dependencies { - aspect 'software.amazon.lambda:powertools-sqs:{{ powertools.version }}' - } - - sourceCompatibility = 1.8 - targetCompatibility = 1.8 - ``` - -## IAM Permissions - -This utility requires additional permissions to work as expected. Lambda functions using this utility require the `sqs:DeleteMessageBatch` permission. - -If you are also using [nonRetryableExceptions](#move-non-retryable-messages-to-a-dead-letter-queue) attribute, utility will need additional permission of `sqs:GetQueueAttributes` on source SQS. -It also needs `sqs:SendMessage` and `sqs:SendMessageBatch` on configured dead letter queue. - -If source or dead letter queue is configured to use encryption at rest using [AWS Key Management Service (KMS)](https://aws.amazon.com/kms/), function will need additional permissions of -`kms:GenerateDataKey` and `kms:Decrypt` on the KMS key being used for encryption. Refer [docs](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-key-management.html#compatibility-with-aws-services) for more details. - -Refer [example project](https://github.com/aws-samples/aws-lambda-powertools-examples/blob/main/java/SqsBatchProcessing/template.yaml#L105) for policy details example. - - -## Processing messages from SQS - -You can use either **[SqsBatch annotation](#sqsbatch-annotation)**, or **[SqsUtils Utility API](#sqsutils-utility-api)** as a fluent API. - -Both have nearly the same behaviour when it comes to processing messages from the batch: - -* **Entire batch has been successfully processed**, where your Lambda handler returned successfully, we will let SQS delete the batch to optimize your cost -* **Entire Batch has been partially processed successfully**, where exceptions were raised within your `SqsMessageHandler` interface implementation, we will: - - **1)** Delete successfully processed messages from the queue by directly calling `sqs:DeleteMessageBatch` - - **2)** If a message with a [message group ID](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/using-messagegroupid-property.html) fails, - the processing of the batch will be stopped and the remainder of the messages will be returned to SQS. - This behaviour [is required to handle SQS FIFO queues](https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html#services-sqs-batchfailurereporting). - - **3)** if non retryable exceptions occur, messages resulting in configured exceptions during processing will be immediately moved to the dead letter queue associated to the source SQS queue or deleted from the source SQS queue if `deleteNonRetryableMessageFromQueue` is set to `true`. - - **4)** Raise `SQSBatchProcessingException` to ensure failed messages return to your SQS queue - -The only difference is that **SqsUtils Utility API** will give you access to return from the processed messages if you need. Exception `SQSBatchProcessingException` thrown from the -utility will have access to both successful and failed messaged along with failure exceptions. - -## Functional Interface SqsMessageHandler - -Both [annotation](#sqsbatch-annotation) and [SqsUtils Utility API](#sqsutils-utility-api) requires an implementation of functional interface `SqsMessageHandler`. - -This implementation is responsible for processing each individual message from the batch, and to raise an exception if unable to process any of the messages sent. - -**Any non-exception/successful return from your record handler function** will instruct utility to queue up each individual message for deletion. - -### SqsBatch annotation - -When using this annotation, you need provide a class implementation of `SqsMessageHandler` that will process individual messages from the batch - It should raise an exception if it is unable to process the record. - -All records in the batch will be passed to this handler for processing, even if exceptions are thrown - Here's the behaviour after completing the batch: - -* **Any successfully processed messages**, we will delete them from the queue via `sqs:DeleteMessageBatch`. -* **if, nonRetryableExceptions attribute is used**, messages resulting in configured exceptions during processing will be immediately moved to the dead letter queue associated to the source SQS queue or deleted from the source SQS queue if `deleteNonRetryableMessageFromQueue` is set to `true`. -* **Any unprocessed messages detected**, we will raise `SQSBatchProcessingException` to ensure failed messages return to your SQS queue. - -!!! warning - You will not have access to the **processed messages** within the Lambda Handler - all processing logic will and should be performed by the implemented `#!java SqsMessageHandler#process()` function. - -=== "AppSqsEvent.java" - - ```java hl_lines="7" - import software.amazon.lambda.powertools.sqs.SqsBatch; - import software.amazon.lambda.powertools.sqs.SqsMessageHandler; - import software.amazon.lambda.powertools.sqs.SqsUtils; - - public class AppSqsEvent implements RequestHandler { - @Override - @SqsBatch(SampleMessageHandler.class) - public String handleRequest(SQSEvent input, Context context) { - return "{\"statusCode\": 200}"; - } - - public class SampleMessageHandler implements SqsMessageHandler { - - @Override - public String process(SQSMessage message) { - // This will be called for each individual message from a batch - // It should raise an exception if the message was not processed successfully - String returnVal = doSomething(message.getBody()); - return returnVal; - } - } - } - ``` - -=== "AppSqsEventWithNonRetryableExceptions.java" - - ```java hl_lines="7 21" - import software.amazon.lambda.powertools.sqs.SqsBatch; - import software.amazon.lambda.powertools.sqs.SqsMessageHandler; - import software.amazon.lambda.powertools.sqs.SqsUtils; - - public class AppSqsEvent implements RequestHandler { - @Override - @SqsBatch(value = SampleMessageHandler.class, nonRetryableExceptions = {IllegalArgumentException.class}) - public String handleRequest(SQSEvent input, Context context) { - return "{\"statusCode\": 200}"; - } - - public class SampleMessageHandler implements SqsMessageHandler { - - @Override - public String process(SQSMessage message) { - // This will be called for each individual message from a batch - // It should raise an exception if the message was not processed successfully - String returnVal = doSomething(message.getBody()); - - if(/**Business validation failure**/) { - throw new IllegalArgumentException("Failed business validation. No point of retrying. Move me to DLQ." + message.getMessageId()); - } - - return returnVal; - } - } - } - ``` - - -### SqsUtils Utility API - -If you require access to the result of processed messages, you can use this utility. The result from calling **`#!java SqsUtils#batchProcessor()`** on the context manager will be a list of all the return values -from your **`#!java SqsMessageHandler#process()`** function. - -You can also use the utility in functional way by providing inline implementation of functional interface **`#!java SqsMessageHandler#process()`** - - -=== "Utility API" - - ```java hl_lines="4" - public class AppSqsEvent implements RequestHandler> { - @Override - public List handleRequest(SQSEvent input, Context context) { - List returnValues = SqsUtils.batchProcessor(input, SampleMessageHandler.class); - - return returnValues; - } - - public class SampleMessageHandler implements SqsMessageHandler { - - @Override - public String process(SQSMessage message) { - // This will be called for each individual message from a batch - // It should raise an exception if the message was not processed successfully - String returnVal = doSomething(message.getBody()); - return returnVal; - } - } - } - ``` - -=== "Function implementation" - - ```java hl_lines="5 6 7 8 9 10" - public class AppSqsEvent implements RequestHandler> { - - @Override - public List handleRequest(SQSEvent input, Context context) { - List returnValues = SqsUtils.batchProcessor(input, (message) -> { - // This will be called for each individual message from a batch - // It should raise an exception if the message was not processed successfully - String returnVal = doSomething(message.getBody()); - return returnVal; - }); - - return returnValues; - } - } - ``` - -## Passing custom SqsClient - -If you need to pass custom SqsClient such as region to the SDK, you can pass your own `SqsClient` to be used by utility either for -**[SqsBatch annotation](#sqsbatch-annotation)**, or **[SqsUtils Utility API](#sqsutils-utility-api)**. - -=== "App.java" - - ```java hl_lines="3 4" - public class AppSqsEvent implements RequestHandler> { - static { - SqsUtils.overrideSqsClient(SqsClient.builder() - .build()); - } - - @Override - public List handleRequest(SQSEvent input, Context context) { - List returnValues = SqsUtils.batchProcessor(input, SampleMessageHandler.class); - - return returnValues; - } - - public class SampleMessageHandler implements SqsMessageHandler { - - @Override - public String process(SQSMessage message) { - // This will be called for each individual message from a batch - // It should raise an exception if the message was not processed successfully - String returnVal = doSomething(message.getBody()); - return returnVal; - } - } - } - ``` - -## Suppressing exceptions - -If you want to disable the default behavior where `SQSBatchProcessingException` is raised if there are any exception, you can pass the `suppressException` boolean argument. - -=== "Within SqsBatch annotation" - - ```java hl_lines="2" - @Override - @SqsBatch(value = SampleMessageHandler.class, suppressException = true) - public String handleRequest(SQSEvent input, Context context) { - return "{\"statusCode\": 200}"; - } - ``` - -=== "Within SqsUtils Utility API" - - ```java hl_lines="3" - @Override - public List handleRequest(SQSEvent input, Context context) { - List returnValues = SqsUtils.batchProcessor(input, true, SampleMessageHandler.class); - - return returnValues; - } - ``` - -## Move non retryable messages to a dead letter queue - -If you want certain exceptions to be treated as permanent failures during batch processing, i.e. exceptions where the result of retrying will -always be a failure and want these can be immediately moved to the dead letter queue associated to the source SQS queue, you can use `SqsBatch#nonRetryableExceptions()` -to configure such exceptions. - -If you want such messages to be deleted instead, set `SqsBatch#deleteNonRetryableMessageFromQueue()` to `true`. By default, its value is `false`. - -Same capability is also provided by [SqsUtils Utility API](#sqsutils-utility-api). - -!!! info - Make sure the lambda function has required permissions needed by utility. Refer [this section](#iam-permissions). - -=== "SqsBatch annotation" - - ```java hl_lines="7 21" - import software.amazon.lambda.powertools.sqs.SqsBatch; - import software.amazon.lambda.powertools.sqs.SqsMessageHandler; - import software.amazon.lambda.powertools.sqs.SqsUtils; - - public class AppSqsEvent implements RequestHandler { - @Override - @SqsBatch(value = SampleMessageHandler.class, nonRetryableExceptions = {IllegalArgumentException.class}) - public String handleRequest(SQSEvent input, Context context) { - return "{\"statusCode\": 200}"; - } - - public class SampleMessageHandler implements SqsMessageHandler { - - @Override - public String process(SQSMessage message) { - // This will be called for each individual message from a batch - // It should raise an exception if the message was not processed successfully - String returnVal = doSomething(message.getBody()); - - if(/**Business validation failure**/) { - throw new IllegalArgumentException("Failed business validation. No point of retrying. Move me to DLQ." + message.getMessageId()); - } - - return returnVal; - } - } - } - ``` - -=== "SqsBatch API" - - ```java hl_lines="9 23" - import software.amazon.lambda.powertools.sqs.SqsBatch; - import software.amazon.lambda.powertools.sqs.SqsMessageHandler; - import software.amazon.lambda.powertools.sqs.SqsUtils; - - public class AppSqsEvent implements RequestHandler { - @Override - public String handleRequest(SQSEvent input, Context context) { - - SqsUtils.batchProcessor(input, BatchProcessor.class, IllegalArgumentException.class); - - return "{\"statusCode\": 200}"; - } - - public class SampleMessageHandler implements SqsMessageHandler { - - @Override - public String process(SQSMessage message) { - // This will be called for each individual message from a batch - // It should raise an exception if the message was not processed successfully - String returnVal = doSomething(message.getBody()); - - if(/**Business validation failure**/) { - throw new IllegalArgumentException("Failed business validation. No point of retrying. Move me to DLQ." + message.getMessageId()); - } - - return returnVal; - } - } - } - ``` - -## Migrating to the Batch Library -The [batch processing library](batch.md) provides a way to process messages and gracefully handle partial failures for -SQS, Kinesis Streams, and DynamoDB Streams batch sources. In comparison the legacy SQS Batch library, it relies on -[Lambda partial batch responses](https://aws.amazon.com/about-aws/whats-new/2021/11/aws-lambda-partial-batch-response-sqs-event-source/), -which allows the library to provide a simpler, reliable interface for processing batches. - -In order to get started, check out the [processing messages from SQS](batch/#processing-messages-from-sqs) documentation. -In most cases, you will simply be able to retain your existing batch message handler function, and wrap it with the new -batch processing interface. Unlike this module, As the batch processor uses *partial batch responses* to communicate to -Lambda which messages have been processed and must be removed from the queue, the return of the handler's process function -must be returned to Lambda. - -The new library also no longer requires the `SQS:DeleteMessage` action on the Lambda function's role policy, as Lambda -itself now manages removal of messages from the queue. - -!!! info - Some tuneables from this library are no longer provided. - - * **Non-retryable Exceptions** - there is no mechanism to indicate in a partial batch response that a particular message - should not be retried and instead moved to DLQ - a message either succeeds, or fails and is retried. A message - will be moved to the DLQ once the normal retry process has expired. - * **Suppress Exception** - The new batch processor does not throw an exception on failure of a handler. Instead, - its result must be returned by your code from your message handler to Lambda, so that Lambda can manage - the completed messages and retry behaviour. \ No newline at end of file diff --git a/docs/utilities/sqs_large_message_handling.md b/docs/utilities/sqs_large_message_handling.md deleted file mode 100644 index 0924d01cf..000000000 --- a/docs/utilities/sqs_large_message_handling.md +++ /dev/null @@ -1,296 +0,0 @@ ---- -title: SQS Large Message Handling (Deprecated) -description: Utility ---- - -!!! warning - This module is now deprecated and will be removed in version 2. - See [Large Message Handling](large_messages.md) and - [the migration guide](http://localhost:8000/lambda-java/utilities/large_messages/#migration-from-the-sqs-large-message-utility) - for the new module (`powertools-large-messages`) documentation - -The large message handling utility handles SQS messages which have had their payloads -offloaded to S3 due to them being larger than the SQS maximum. - -The utility automatically retrieves messages which have been offloaded to S3 using the -[amazon-sqs-java-extended-client-lib](https://github.com/awslabs/amazon-sqs-java-extended-client-lib) -client library. Once the message payloads have been processed successful the -utility can delete the message payloads from S3. - -This utility is compatible with versions *[1.1.0+](https://github.com/awslabs/amazon-sqs-java-extended-client-lib)* of -amazon-sqs-java-extended-client-lib. - -=== "Maven" - -```xml - - - com.amazonaws - amazon-sqs-java-extended-client-lib - 1.1.0 - -``` -=== "Gradle" - - ```groovy - dependencies { - implementation 'com.amazonaws:amazon-sqs-java-extended-client-lib:1.1.0' - } - ``` - -## Install -Depending on your version of Java (either Java 1.8 or 11+), the configuration slightly changes. - -=== "Maven Java 11+" - -```xml hl_lines="3-7 16 18 24-27" - -... - -software.amazon.lambda -powertools-sqs -{{ powertools.version }} - -... - -... - - - -... - -dev.aspectj -aspectj-maven-plugin -1.13.1 - -11 -11 -11 - - -software.amazon.lambda -powertools-sqs - - - - - - -compile - - - - -... - - -``` - -=== "Maven Java 1.8" - - ```xml hl_lines="3-7 16 18 24-27" - - ... - - software.amazon.lambda - powertools-sqs - {{ powertools.version }} - - ... - - ... - - - - ... - - org.codehaus.mojo - aspectj-maven-plugin - 1.14.0 - - 1.8 - 1.8 - 1.8 - - - software.amazon.lambda - powertools-sqs - - - - - - - compile - - - - - ... - - - ``` - -=== "Gradle Java 11+" - - ```groovy hl_lines="3 11" - plugins { - id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' - } - - repositories { - mavenCentral() - } - - dependencies { - aspect 'software.amazon.lambda:powertools-sqs:{{ powertools.version }}' - } - - sourceCompatibility = 11 // or higher - targetCompatibility = 11 // or higher - ``` - -=== "Gradle Java 1.8" - - ```groovy hl_lines="3 11" - plugins { - id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '6.6.3' - } - - repositories { - mavenCentral() - } - - dependencies { - aspect 'software.amazon.lambda:powertools-sqs:{{ powertools.version }}' - } - - sourceCompatibility = 1.8 - targetCompatibility = 1.8 - ``` - -## Lambda handler - -The annotation `@SqsLargeMessage` should be used with the handleRequest method of a class -which implements `com.amazonaws.services.lambda.runtime.RequestHandler` with -`com.amazonaws.services.lambda.runtime.events.SQSEvent` as the first parameter. - -=== "SqsMessageHandler.java" - - ```java hl_lines="6" - import software.amazon.lambda.powertools.sqs.SqsLargeMessage; - - public class SqsMessageHandler implements RequestHandler { - - @Override - @SqsLargeMessage - public String handleRequest(SQSEvent sqsEvent, Context context) { - // process messages - - return "ok"; - } - } - ``` - -`@SqsLargeMessage` creates a default S3 Client `AmazonS3 amazonS3 = AmazonS3ClientBuilder.defaultClient()`. - -!!! tip -When the Lambda function is invoked with an event from SQS, each received record -in the SQSEvent is checked to see to validate if it is offloaded to S3. -If it does then `getObject(bucket, key)` will be called, and the payload retrieved. -If there is an error during this process then the function will fail with a `FailedProcessingLargePayloadException` -exception. - - If the request handler method returns without error then each payload will be - deleted from S3 using `deleteObject(bucket, key)` - -To disable deletion of payloads setting the following annotation parameter: - -=== "Disable payload deletion" - - ```java hl_lines="3" - import software.amazon.lambda.powertools.sqs.SqsLargeMessage; - - @SqsLargeMessage(deletePayloads=false) - public class SqsMessageHandler implements RequestHandler { - - } - ``` - -## Utility - -If you want to avoid using annotation and have control over error that can happen during payload enrichment use `SqsUtils.enrichedMessageFromS3()`. -It provides you access with a list of `SQSMessage` object enriched from S3 payload. - -Original `SQSEvent` object is never mutated. You can also control if the S3 payload should be deleted after successful -processing. - -=== "Functional API without annotation" - - ```java hl_lines="9 10 11 14 15 16 17 18 19 20 21 22 27 28 29" - import software.amazon.lambda.powertools.sqs.SqsLargeMessage; - import software.amazon.lambda.powertools.sqs.SqsUtils; - - public class SqsMessageHandler implements RequestHandler { - - @Override - public String handleRequest(SQSEvent sqsEvent, Context context) { - - Map sqsMessage = SqsUtils.enrichedMessageFromS3(sqsEvent, sqsMessages -> { - // Some business logic - Map someBusinessLogic = new HashMap<>(); - someBusinessLogic.put("Message", sqsMessages.get(0).getBody()); - return someBusinessLogic; - }); - - // Do not delete payload after processing. - Map sqsMessage = SqsUtils.enrichedMessageFromS3(sqsEvent, false, sqsMessages -> { - // Some business logic - Map someBusinessLogic = new HashMap<>(); - someBusinessLogic.put("Message", sqsMessages.get(0).getBody()); - return someBusinessLogic; - }); - - // Better control over exception during enrichment - try { - // Do not delete payload after processing. - SqsUtils.enrichedMessageFromS3(sqsEvent, false, sqsMessages -> { - // Some business logic - }); - } catch (FailedProcessingLargePayloadException e) { - // handle any exception. - } - - return "ok"; - } - } - ``` - -## Overriding the default S3Client - -If you require customisations to the default S3Client, you can create your own `S3Client` and pass it to be used by utility either for -**[SqsLargeMessage annotation](#lambda-handler)**, or **[SqsUtils Utility API](#utility)**. - -=== "App.java" - - ```java hl_lines="4 5 11" - import software.amazon.lambda.powertools.sqs.SqsLargeMessage; - - static { - SqsUtils.overrideS3Client(S3Client.builder() - .build()); - } - - public class SqsMessageHandler implements RequestHandler { - - @Override - @SqsLargeMessage - public String handleRequest(SQSEvent sqsEvent, Context context) { - // process messages - - return "ok"; - } - } - ``` \ No newline at end of file diff --git a/examples/pom.xml b/examples/pom.xml index 5d19a20fb..0cd62e326 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -33,7 +33,6 @@ powertools-examples-idempotency powertools-examples-parameters powertools-examples-serialization - powertools-examples-sqs powertools-examples-batch powertools-examples-validation powertools-examples-cloudformation diff --git a/examples/powertools-examples-sqs/README.md b/examples/powertools-examples-sqs/README.md deleted file mode 100644 index 45f4a4a74..000000000 --- a/examples/powertools-examples-sqs/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# Powertools for AWS Lambda (Java) - SQS Batch Processing Example - -This project contains an example of Lambda function using the batch processing utilities module of Powertools for AWS Lambda (Java). -For more information on this module, please refer to the [documentation](https://docs.powertools.aws.dev/lambda/java/utilities/batch/). - -The project contains two functions: - -* [SqsMessageSender](src/main/java/org/demo/sqs/SqsMessageSender.java) - Sends a set of messages to an SQS queue. -This function is triggered every 5 minutes by an EventBridge schedule rule. -* [SqsPoller](src/main/java/org/demo/sqs/SqsPoller.java) - Listens to the same queue, processing items off in batches - -The poller intentionally fails intermittently processing messages to demonstrate the replay behaviour of the batch -module: - -
- -SqsPoller.java - -[SqsPoller.java:43](src/main/java/org/demo/sqs/SqsPoller.java) - -```java - public String process(SQSMessage message) { - log.info("Processing message with id {}", message.getMessageId()); - - int nextInt = random.nextInt(100); - - if(nextInt <= 10) { - log.info("Randomly picked message with id {} as business validation failure.", message.getMessageId()); - throw new IllegalArgumentException("Failed business validation. No point of retrying. Move me to DLQ." + message.getMessageId()); - } - - if(nextInt > 90) { - log.info("Randomly picked message with id {} as intermittent failure.", message.getMessageId()); - throw new RuntimeException("Failed due to intermittent issue. Will be sent back for retry." + message.getMessageId()); - } - - return "Success"; - } -``` - -
- -## Deploy the sample application - -This sample is based on Serverless Application Model (SAM). To deploy it, check out the instructions for getting -started with SAM in [the examples directory](../README.md) - -## Test the application - -As the test is pushing through a batch every 5 minutes, we can simply watch the logs to see the batches being processed: - -```bash - sam logs --tail --stack-name $MY_STACK -``` - -As the handler intentionally introduces intermittent failures, we should expect to see error messages too! diff --git a/examples/powertools-examples-sqs/events/event.json b/examples/powertools-examples-sqs/events/event.json deleted file mode 100644 index 3822fadaa..000000000 --- a/examples/powertools-examples-sqs/events/event.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "body": "{\"message\": \"hello world\"}", - "resource": "/{proxy+}", - "path": "/path/to/resource", - "httpMethod": "POST", - "isBase64Encoded": false, - "queryStringParameters": { - "foo": "bar" - }, - "pathParameters": { - "proxy": "/path/to/resource" - }, - "stageVariables": { - "baz": "qux" - }, - "headers": { - "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", - "Accept-Encoding": "gzip, deflate, sdch", - "Accept-Language": "en-US,en;q=0.8", - "Cache-Control": "max-age=0", - "CloudFront-Forwarded-Proto": "https", - "CloudFront-Is-Desktop-Viewer": "true", - "CloudFront-Is-Mobile-Viewer": "false", - "CloudFront-Is-SmartTV-Viewer": "false", - "CloudFront-Is-Tablet-Viewer": "false", - "CloudFront-Viewer-Country": "US", - "Host": "1234567890.execute-api.us-east-1.amazonaws.com", - "Upgrade-Insecure-Requests": "1", - "User-Agent": "Custom User Agent String", - "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", - "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", - "X-Forwarded-For": "127.0.0.1, 127.0.0.2", - "X-Forwarded-Port": "443", - "X-Forwarded-Proto": "https" - }, - "requestContext": { - "accountId": "123456789012", - "resourceId": "123456", - "stage": "prod", - "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", - "requestTime": "09/Apr/2015:12:34:56 +0000", - "requestTimeEpoch": 1428582896000, - "identity": { - "cognitoIdentityPoolId": null, - "accountId": null, - "cognitoIdentityId": null, - "caller": null, - "accessKey": null, - "sourceIp": "127.0.0.1", - "cognitoAuthenticationType": null, - "cognitoAuthenticationProvider": null, - "userArn": null, - "userAgent": "Custom User Agent String", - "user": null - }, - "path": "/prod/path/to/resource", - "resourcePath": "/{proxy+}", - "httpMethod": "POST", - "apiId": "1234567890", - "protocol": "HTTP/1.1" - } - } - \ No newline at end of file diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml deleted file mode 100644 index aeffcac40..000000000 --- a/examples/powertools-examples-sqs/pom.xml +++ /dev/null @@ -1,196 +0,0 @@ - - 4.0.0 - software.amazon.lambda.examples - 1.17.0-SNAPSHOT - powertools-examples-sqs - jar - Powertools for AWS Lambda (Java) library Examples - SQS - - - 2.20.0 - 1.8 - 1.8 - true - - - - - software.amazon.lambda - powertools-logging - ${project.version} - - - software.amazon.lambda - powertools-sqs - ${project.version} - - - software.amazon.awssdk - url-connection-client - 2.20.119 - - - com.amazonaws - aws-lambda-java-core - 1.2.2 - - - com.amazonaws - aws-lambda-java-events - 3.11.2 - - - org.apache.logging.log4j - log4j-core - ${log4j.version} - - - org.apache.logging.log4j - log4j-api - ${log4j.version} - - - com.fasterxml.jackson.datatype - jackson-datatype-joda - 2.15.2 - - - - junit - junit - 4.13.2 - test - - - - - - - dev.aspectj - aspectj-maven-plugin - 1.13.1 - - ${maven.compiler.source} - ${maven.compiler.target} - ${maven.compiler.target} - - - software.amazon.lambda - powertools-logging - - - software.amazon.lambda - powertools-sqs - - - - - - - compile - - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.5.0 - - - package - - shade - - - - - - - - - - - - com.github.edwgiz - maven-shade-plugin.log4j2-cachefile-transformer - 2.15 - - - - - - - - - jdk8 - - (,11) - - - 1.9.7 - - - - - org.aspectj - aspectjtools - ${aspectj.version} - - - - - - - - dev.aspectj - aspectj-maven-plugin - ${aspectj.plugin.version} - - ${maven.compiler.source} - ${maven.compiler.target} - ${maven.compiler.target} - - - software.amazon.lambda - powertools-logging - - - software.amazon.lambda - powertools-sqs - - - - - - - compile - test-compile - - - - - - - org.aspectj - aspectjtools - ${aspectj.version} - - - - - - - - - diff --git a/examples/powertools-examples-sqs/src/main/java/org/demo/sqs/SqsMessageSender.java b/examples/powertools-examples-sqs/src/main/java/org/demo/sqs/SqsMessageSender.java deleted file mode 100644 index 701d6808f..000000000 --- a/examples/powertools-examples-sqs/src/main/java/org/demo/sqs/SqsMessageSender.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.demo.sqs; - -import static java.util.stream.Collectors.toList; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.events.ScheduledEvent; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.joda.JodaModule; -import java.security.SecureRandom; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.stream.IntStream; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; -import software.amazon.awssdk.services.sqs.SqsClient; -import software.amazon.awssdk.services.sqs.model.MessageAttributeValue; -import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequest; -import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequestEntry; -import software.amazon.awssdk.services.sqs.model.SendMessageBatchResponse; -import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.logging.LoggingUtils; - -public class SqsMessageSender implements RequestHandler { - - private static final Logger log = LogManager.getLogger(SqsMessageSender.class); - - private static final SqsClient sqsClient = SqsClient.builder() - .httpClient(UrlConnectionHttpClient.create()) - .build(); - - private static final Random random = new SecureRandom(); - - private static final ObjectMapper objectMapper; - - static { - objectMapper = new ObjectMapper(); - objectMapper.registerModule(new JodaModule()); - LoggingUtils.defaultObjectMapper(objectMapper); - } - - @Logging(logEvent = true) - public String handleRequest(final ScheduledEvent input, final Context context) { - String queueUrl = System.getenv("QUEUE_URL"); - - // Push 5 messages on each invoke. - List batchRequestEntries = IntStream.range(0, 5) - .mapToObj(value -> - { - Map attributeValueHashMap = new HashMap<>(); - attributeValueHashMap.put("Key" + value, MessageAttributeValue.builder() - .dataType("String") - .stringValue("Value" + value) - .build()); - - byte[] array = new byte[7]; - random.nextBytes(array); - - return SendMessageBatchRequestEntry.builder() - .messageAttributes(attributeValueHashMap) - .id(input.getId() + value) - .messageBody("Sample Message " + value) - .build(); - }).collect(toList()); - - SendMessageBatchResponse sendMessageBatchResponse = sqsClient.sendMessageBatch(SendMessageBatchRequest.builder() - .queueUrl(queueUrl) - .entries(batchRequestEntries) - .build()); - - log.info("Sent Message {}", sendMessageBatchResponse); - - return "Success"; - } -} \ No newline at end of file diff --git a/examples/powertools-examples-sqs/src/main/java/org/demo/sqs/SqsPoller.java b/examples/powertools-examples-sqs/src/main/java/org/demo/sqs/SqsPoller.java deleted file mode 100644 index 9ad5c7868..000000000 --- a/examples/powertools-examples-sqs/src/main/java/org/demo/sqs/SqsPoller.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.demo.sqs; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import java.security.SecureRandom; -import java.util.Random; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; -import software.amazon.awssdk.services.sqs.SqsClient; -import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.sqs.SqsBatch; -import software.amazon.lambda.powertools.sqs.SqsMessageHandler; -import software.amazon.lambda.powertools.sqs.SqsUtils; - -/** - * Handler for requests to Lambda function. - */ -public class SqsPoller implements RequestHandler { - - static { - // https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/lambda-optimize-starttime.html - SqsUtils.overrideSqsClient(SqsClient.builder() - .httpClient(UrlConnectionHttpClient.create()) - .build()); - } - - Logger log = LogManager.getLogger(SqsPoller.class); - Random random = new SecureRandom(); - - @SqsBatch(value = BatchProcessor.class, nonRetryableExceptions = {IllegalArgumentException.class}) - @Logging(logEvent = true) - public String handleRequest(final SQSEvent input, final Context context) { - return "Success"; - } - - private class BatchProcessor implements SqsMessageHandler { - @Override - public String process(SQSMessage message) { - log.info("Processing message with id {}", message.getMessageId()); - - int nextInt = random.nextInt(100); - - if (nextInt <= 10) { - log.info("Randomly picked message with id {} as business validation failure.", message.getMessageId()); - throw new IllegalArgumentException( - "Failed business validation. No point of retrying. Move me to DLQ." + message.getMessageId()); - } - - if (nextInt > 90) { - log.info("Randomly picked message with id {} as intermittent failure.", message.getMessageId()); - throw new RuntimeException( - "Failed due to intermittent issue. Will be sent back for retry." + message.getMessageId()); - } - - return "Success"; - } - } -} \ No newline at end of file diff --git a/examples/powertools-examples-sqs/src/main/resources/log4j2.xml b/examples/powertools-examples-sqs/src/main/resources/log4j2.xml deleted file mode 100644 index e1fd14cea..000000000 --- a/examples/powertools-examples-sqs/src/main/resources/log4j2.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/examples/powertools-examples-sqs/template.yaml b/examples/powertools-examples-sqs/template.yaml deleted file mode 100644 index 50327de18..000000000 --- a/examples/powertools-examples-sqs/template.yaml +++ /dev/null @@ -1,148 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: > - sqs batch processing demo - -Globals: - Function: - Timeout: 20 - Runtime: java11 - MemorySize: 512 - Tracing: Active - Environment: - Variables: - # Powertools for AWS Lambda (Java) env vars: https://docs.powertools.aws.dev/lambda/java/#environment-variables - POWERTOOLS_LOG_LEVEL: INFO - POWERTOOLS_LOGGER_SAMPLE_RATE: 0.1 - POWERTOOLS_LOGGER_LOG_EVENT: true - -Resources: - CustomerKey: - Type: AWS::KMS::Key - Properties: - Description: KMS key for encrypted queues - Enabled: true - KeyPolicy: - Version: '2012-10-17' - Statement: - - Sid: Enable IAM User Permissions - Effect: Allow - Principal: - AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root' - Action: 'kms:*' - Resource: '*' - - Sid: Allow use of the key - Effect: Allow - Principal: - Service: lambda.amazonaws.com - Action: - - kms:Decrypt - - kms:GenerateDataKey - Resource: '*' - - CustomerKeyAlias: - Type: AWS::KMS::Alias - Properties: - AliasName: alias/sqs-key - TargetKeyId: !Ref CustomerKey - - DemoDlqSqsQueue: - Type: AWS::SQS::Queue - Properties: - KmsMasterKeyId: !Ref CustomerKey - - DemoSqsQueue: - Type: AWS::SQS::Queue - Properties: - RedrivePolicy: - deadLetterTargetArn: - Fn::GetAtt: - - "DemoDlqSqsQueue" - - "Arn" - maxReceiveCount: 2 - KmsMasterKeyId: !Ref CustomerKey - - DemoSQSSenderFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: . - Handler: org.demo.sqs.SqsMessageSender::handleRequest - Environment: - Variables: - POWERTOOLS_SERVICE_NAME: sqs-demo - QUEUE_URL: !Ref DemoSqsQueue - Policies: - - Statement: - - Sid: SQSSendMessageBatch - Effect: Allow - Action: - - sqs:SendMessageBatch - - sqs:SendMessage - Resource: !GetAtt DemoSqsQueue.Arn - - Sid: SQSKMSKey - Effect: Allow - Action: - - kms:GenerateDataKey - - kms:Decrypt - Resource: !GetAtt CustomerKey.Arn - Events: - CWSchedule: - Type: Schedule - Properties: - Schedule: 'rate(5 minutes)' - Name: !Join ["-", ["message-producer-schedule", !Select [0, !Split [-, !Select [2, !Split [/, !Ref AWS::StackId ]]]]]] - Description: Produce message to SQS via a Lambda function - Enabled: true - - DemoSQSConsumerFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: . - Handler: org.demo.sqs.SqsPoller::handleRequest - Environment: - Variables: - POWERTOOLS_SERVICE_NAME: sqs-demo - Policies: - - Statement: - - Sid: SQSDeleteGetAttribute - Effect: Allow - Action: - - sqs:DeleteMessageBatch - - sqs:GetQueueAttributes - Resource: !GetAtt DemoSqsQueue.Arn - - Sid: SQSSendMessageBatch - Effect: Allow - Action: - - sqs:SendMessageBatch - - sqs:SendMessage - Resource: !GetAtt DemoDlqSqsQueue.Arn - - Sid: SQSKMSKey - Effect: Allow - Action: - - kms:GenerateDataKey - - kms:Decrypt - Resource: !GetAtt CustomerKey.Arn - Events: - MySQSEvent: - Type: SQS - Properties: - Queue: !GetAtt DemoSqsQueue.Arn - BatchSize: 2 - MaximumBatchingWindowInSeconds: 300 - -Outputs: - DemoSqsQueue: - Description: "ARN for main SQS queue" - Value: !GetAtt DemoSqsQueue.Arn - DemoDlqSqsQueue: - Description: "ARN for DLQ" - Value: !GetAtt DemoDlqSqsQueue.Arn - DemoSQSSenderFunction: - Description: "Sender SQS Lambda Function ARN" - Value: !GetAtt DemoSQSSenderFunction.Arn - DemoSQSConsumerFunction: - Description: "Consumer SQS Lambda Function ARN" - Value: !GetAtt DemoSQSConsumerFunction.Arn - DemoSQSConsumerFunctionRole: - Description: "Implicit IAM Role created for SQS Lambda Function ARN" - Value: !GetAtt DemoSQSConsumerFunctionRole.Arn diff --git a/mkdocs.yml b/mkdocs.yml index d54ece508..62d8d75ce 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -18,9 +18,6 @@ nav: - utilities/validation.md - utilities/custom_resources.md - utilities/serialization.md - - Deprecated: - - utilities/sqs_large_message_handling.md - - utilities/sqs_batch.md - Processes: - processes/maintainers.md diff --git a/pom.xml b/pom.xml index 8c9e540f5..bebb2493f 100644 --- a/pom.xml +++ b/pom.xml @@ -45,11 +45,9 @@ powertools-serialization powertools-logging powertools-tracing - powertools-sqs powertools-metrics powertools-parameters powertools-validation - powertools-test-suite powertools-cloudformation powertools-idempotency powertools-large-messages diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml deleted file mode 100644 index c21943fba..000000000 --- a/powertools-sqs/pom.xml +++ /dev/null @@ -1,144 +0,0 @@ - - - - - 4.0.0 - - powertools-sqs - jar - - - powertools-parent - software.amazon.lambda - 1.17.0-SNAPSHOT - - - Powertools for AWS Lambda (Java) library SQS - - Deprecated: Batch processing is now handled in powertools-batch and large messages in powertools-large-messages modules. - A suite of utilities for AWS Lambda Functions that makes tracing with AWS X-Ray, structured logging and creating custom metrics asynchronously easier. - - https://aws.amazon.com/lambda/ - - GitHub Issues - https://github.com/aws-powertools/powertools-lambda-java/issues - - - https://github.com/aws-powertools/powertools-lambda-java.git - - - - Powertools for AWS Lambda team - Amazon Web Services - https://aws.amazon.com/ - - - - - - ossrh - https://aws.oss.sonatype.org/content/repositories/snapshots - - - - - - software.amazon.lambda - powertools-core - - - com.amazonaws - aws-lambda-java-tests - - - com.amazonaws - aws-lambda-java-core - - - software.amazon.payloadoffloading - payloadoffloading-common - - - com.amazonaws - aws-lambda-java-events - - - software.amazon.awssdk - sqs - - - software.amazon.awssdk - s3 - - - com.fasterxml.jackson.core - jackson-databind - - - - org.aspectj - aspectjrt - - - - - org.junit.jupiter - junit-jupiter-api - test - - - org.junit.jupiter - junit-jupiter-engine - test - - - org.junit.jupiter - junit-jupiter-params - test - - - org.apache.commons - commons-lang3 - test - - - org.mockito - mockito-core - test - - - org.aspectj - aspectjweaver - test - - - org.assertj - assertj-core - test - - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - - - - \ No newline at end of file diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SQSBatchProcessingException.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SQSBatchProcessingException.java deleted file mode 100644 index 7adc2afe5..000000000 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SQSBatchProcessingException.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static java.util.Collections.unmodifiableList; -import static java.util.stream.Collectors.joining; - -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import java.util.ArrayList; -import java.util.List; - -/** - *

- * When one or more {@link SQSMessage} fails and if any exception is thrown from {@link SqsMessageHandler#process(SQSMessage)} - * during processing of a messages, this exception is with all the details of successful and failed messages. - *

- * - *

- * This exception can be thrown form: - *

    - *
  • {@link SqsBatch}
  • - *
  • {@link SqsUtils#batchProcessor(SQSEvent, Class)}
  • - *
  • {@link SqsUtils#batchProcessor(SQSEvent, boolean, Class)}
  • - *
  • {@link SqsUtils#batchProcessor(SQSEvent, SqsMessageHandler)}
  • - *
  • {@link SqsUtils#batchProcessor(SQSEvent, boolean, SqsMessageHandler)}
  • - *
- *

- */ -public class SQSBatchProcessingException extends RuntimeException { - - private final List exceptions; - private final List failures; - private final List returnValues; - - public SQSBatchProcessingException(final List exceptions, - final List failures, - final List successReturns) { - super(exceptions.stream() - .map(Throwable::toString) - .collect(joining("\n"))); - - this.exceptions = new ArrayList<>(exceptions); - this.failures = new ArrayList<>(failures); - this.returnValues = new ArrayList<>(successReturns); - } - - /** - * Details for exceptions that occurred while processing messages in {@link SqsMessageHandler#process(SQSMessage)} - * - * @return List of exceptions that occurred while processing messages - */ - public List getExceptions() { - return unmodifiableList(exceptions); - } - - /** - * List of returns from {@link SqsMessageHandler#process(SQSMessage)} that were successfully processed. - * - * @return List of returns from successfully processed messages - */ - public List successMessageReturnValues() { - return unmodifiableList(returnValues); - } - - /** - * Details of {@link SQSMessage} that failed in {@link SqsMessageHandler#process(SQSMessage)} - * - * @return List of failed messages - */ - public List getFailures() { - return unmodifiableList(failures); - } - - @Override - public void printStackTrace() { - for (Exception exception : exceptions) { - exception.printStackTrace(); - } - } -} diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsBatch.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsBatch.java deleted file mode 100644 index 4378fa707..000000000 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsBatch.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; - -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @deprecated - * @see software.amazon.lambda.powertools.batch in powertools-batch module. - * Will be removed in V2. - * - * {@link SqsBatch} is used to process batch messages in {@link SQSEvent} - * - *

- * When using the annotation, implementation of {@link SqsMessageHandler} is required. Annotation will take care of - * calling {@link SqsMessageHandler#process(SQSMessage)} method for each {@link SQSMessage} in the received {@link SQSEvent} - *

- * - *

- * If any exception is thrown from {@link SqsMessageHandler#process(SQSMessage)} during processing of a messages, Utility - * will take care of deleting all the successful messages from SQS. When one or more single message fails processing due - * to exception thrown from {@link SqsMessageHandler#process(SQSMessage)}, Lambda execution will fail - * with {@link SQSBatchProcessingException}. - *

- * If all the messages are successfully processes, No SQS messages are deleted explicitly but is rather delegated to - * Lambda execution context for deletion. - *

- * - *

- * If you want to suppress the exception even if any message in batch fails, set - * {@link SqsBatch#suppressException()} to true. By default its value is false - *

- * - *

- * If you want certain exceptions to be treated as permanent failures, i.e. exceptions where the result of retrying will - * always be a failure and want these can be immediately moved to the dead letter queue associated to the source SQS queue, - *

- * you can use {@link SqsBatch#nonRetryableExceptions()} to configure such exceptions. - * Make sure function execution role has sqs:GetQueueAttributes permission on source SQS queue and sqs:SendMessage, - * sqs:SendMessageBatch permission for configured DLQ. - *

- * If you want such messages to be deleted instead, set {@link SqsBatch#deleteNonRetryableMessageFromQueue()} to true. - * By default its value is false. - *

- * If there is no DLQ configured on source SQS queue and {@link SqsBatch#nonRetryableExceptions()} attribute is set, if - * nonRetryableExceptions occurs from {@link SqsMessageHandler}, such exceptions will still be treated as temporary - * exceptions and the message will be moved back to source SQS queue for reprocessing. The same behaviour will occur if - * for some reason the utility is unable to move the message to the DLQ. An example of this could be because the function - * is missing the correct permissions. - *

- * - *
- * public class SqsMessageHandler implements RequestHandler {
- *
- *    {@literal @}Override
- *    {@literal @}{@link SqsBatch (SqsMessageHandler)}
- *     public String handleRequest(SQSEvent sqsEvent, Context context) {
- *
- *         return "ok";
- *     }
- *
- *     public class DummySqsMessageHandler implements SqsMessageHandler{
- *     @Override
- *     public Object process(SQSEvent.SQSMessage message) {
- *         throw new UnsupportedOperationException();
- *     }
- * }
- *
- *     ...
- * 
- *
- * @see Amazon SQS dead-letter queues
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.METHOD)
-@Deprecated
-public @interface SqsBatch {
-
-    Class> value();
-
-    boolean suppressException() default false;
-
-    Class[] nonRetryableExceptions() default {};
-
-    boolean deleteNonRetryableMessageFromQueue() default false;
-}
diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsLargeMessage.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsLargeMessage.java
deleted file mode 100644
index a3a92cea1..000000000
--- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsLargeMessage.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright 2023 Amazon.com, Inc. or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *     http://www.apache.org/licenses/LICENSE-2.0
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package software.amazon.lambda.powertools.sqs;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * @deprecated See software.amazon.lambda.powertools.largemessages.LargeMessage in powertools-large-messages module.
- * Will be removed in version 2.
- *
- * 

{@code SqsLargeMessage} is used to signal that the annotated method - * should be extended to handle large SQS messages which have been offloaded - * to S3

- * - *

{@code SqsLargeMessage} automatically retrieves and deletes messages - * which have been offloaded to S3 using the {@code amazon-sqs-java-extended-client-lib} - * client library.

- * - *

This version of the {@code SqsLargeMessage} is compatible with version - * 1.1.0+ of {@code amazon-sqs-java-extended-client-lib}.

- * - *
- * <dependency>
- *   <groupId>com.amazonaws</groupId>
- *   <artifactId>amazon-sqs-java-extended-client-lib</artifactId>
- *   <version>1.1.0</version>
- * </dependency>
- * 
- * - *

{@code SqsLargeMessage} should be used with the handleRequest method of a class - * which implements {@code com.amazonaws.services.lambda.runtime.RequestHandler} with - * {@code com.amazonaws.services.lambda.runtime.events.SQSEvent} as the first parameter.

- * - *
- * public class SqsMessageHandler implements RequestHandler {
- *
- *    {@literal @}Override
- *    {@literal @}SqsLargeMessage
- *     public String handleRequest(SQSEvent sqsEvent, Context context) {
- *
- *         // process messages
- *
- *         return "ok";
- *     }
- *
- *     ...
- * 
- * - *

Using the default S3 Client {@code AmazonS3 amazonS3 = AmazonS3ClientBuilder.defaultClient();} - * each record received in the SQSEvent {@code SqsLargeMessage} will checked - * to see if it's body contains a payload which has been offloaded to S3. If it - * does then {@code getObject(bucket, key)} will be called and the payload - * retrieved.

- * - *

Note: Retreiving payloads from S3 will increase the duration of the - * Lambda function.

- * - *

If the request handler method returns then each payload will be deleted - * from S3 using {@code deleteObject(bucket, key)}

- * - *

To disable deletion of payloads setting the following annotation parameter - * {@code @SqsLargeMessage(deletePayloads=false)}

- */ -@Deprecated -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface SqsLargeMessage { - - boolean deletePayloads() default true; -} diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsMessageHandler.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsMessageHandler.java deleted file mode 100644 index 0c8f03ee9..000000000 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsMessageHandler.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; - -import com.amazonaws.services.lambda.runtime.events.SQSEvent; - -/** - *

- * This interface should be implemented for processing {@link SQSMessage} inside {@link SQSEvent} received by lambda - * function. - *

- * - *

- * It is required by utilities: - *

    - *
  • {@link SqsBatch}
  • - *
  • {@link SqsUtils#batchProcessor(SQSEvent, Class)}
  • - *
  • {@link SqsUtils#batchProcessor(SQSEvent, boolean, Class)}
  • - *
  • {@link SqsUtils#batchProcessor(SQSEvent, SqsMessageHandler)}
  • - *
  • {@link SqsUtils#batchProcessor(SQSEvent, boolean, SqsMessageHandler)}
  • - *
- *

- * - * @param Return value type from {@link SqsMessageHandler#process(SQSMessage)} - */ -@FunctionalInterface -public interface SqsMessageHandler { - - R process(SQSMessage message); -} diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsUtils.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsUtils.java deleted file mode 100644 index d89642780..000000000 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsUtils.java +++ /dev/null @@ -1,653 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static software.amazon.lambda.powertools.sqs.internal.SqsLargeMessageAspect.processMessages; - -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.lang.reflect.Constructor; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Function; -import java.util.stream.Collectors; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; -import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; -import software.amazon.awssdk.services.s3.S3Client; -import software.amazon.awssdk.services.sqs.SqsClient; -import software.amazon.lambda.powertools.core.internal.UserAgentConfigurator; -import software.amazon.lambda.powertools.sqs.exception.SkippedMessageDueToFailedBatchException; -import software.amazon.lambda.powertools.sqs.internal.BatchContext; -import software.amazon.lambda.powertools.sqs.internal.SqsLargeMessageAspect; -import software.amazon.payloadoffloading.PayloadS3Pointer; - -/** - * A class of helper functions to add additional functionality to {@link SQSEvent} processing. - * - * @deprecated Batch processing is now handled in powertools-batch and large messages in powertools-large-messages. - * This class will no longer be available in version 2. - */ -@Deprecated -public final class SqsUtils { - - public static final String SQS = "sqs"; - private static final Logger LOG = LoggerFactory.getLogger(SqsUtils.class); - private static final ObjectMapper objectMapper = new ObjectMapper(); - private static final String MESSAGE_GROUP_ID = "MessageGroupId"; - private static SqsClient client; - private static S3Client s3Client; - - private SqsUtils() { - } - - /** - * This is a utility method when you want to avoid using {@code SqsLargeMessage} annotation. - * Gives you access to enriched messages from S3 in the SQS event produced via extended client lib. - * If all the large S3 payload are successfully retrieved, it will delete them from S3 post success. - * - * @param sqsEvent Event received from SQS Extended client library - * @param messageFunction Function to execute you business logic which provides access to enriched messages from S3 when needed. - * @return Return value from the function. - */ - public static R enrichedMessageFromS3(final SQSEvent sqsEvent, - final Function, R> messageFunction) { - return enrichedMessageFromS3(sqsEvent, true, messageFunction); - } - - /** - * This is a utility method when you want to avoid using {@code SqsLargeMessage} annotation. - * Gives you access to enriched messages from S3 in the SQS event produced via extended client lib. - * if all the large S3 payload are successfully retrieved, Control if it will delete payload from S3 post success. - * - * @param sqsEvent Event received from SQS Extended client library - * @param messageFunction Function to execute you business logic which provides access to enriched messages from S3 when needed. - * @return Return value from the function. - */ - public static R enrichedMessageFromS3(final SQSEvent sqsEvent, - final boolean deleteS3Payload, - final Function, R> messageFunction) { - - List sqsMessages = sqsEvent.getRecords().stream() - .map(SqsUtils::clonedMessage) - .collect(Collectors.toList()); - - List s3Pointers = processMessages(sqsMessages); - - R returnValue = messageFunction.apply(sqsMessages); - - if (deleteS3Payload) { - s3Pointers.forEach(SqsLargeMessageAspect::deleteMessage); - } - - return returnValue; - } - - /** - * Provides ability to set default {@link SqsClient} to be used by utility. - * If no default configuration is provided, client is instantiated via {@link SqsClient#create()} - * - * @param client {@link SqsClient} to be used by utility - */ - public static void overrideSqsClient(SqsClient client) { - SqsUtils.client = client; - } - - /** - * By default, the S3Client is instantiated via {@link S3Client#create()}. - * This method provides the ability to override the S3Client with your own custom version. - * - * @param s3Client {@link S3Client} to be used by utility - */ - public static void overrideS3Client(S3Client s3Client) { - SqsUtils.s3Client = s3Client; - } - - /** - * - * @deprecated - * @see software.amazon.lambda.powertools.batch in powertools-batch module. - * Will be removed in V2. - * - * This utility method is used to process each {@link SQSMessage} inside the received {@link SQSEvent} - * - *

- * The Utility will call {@link SqsMessageHandler#process(SQSMessage)} method for each {@link SQSMessage} - * in the received {@link SQSEvent} - *

- * - *

- * If any exception is thrown from {@link SqsMessageHandler#process(SQSMessage)} during processing of a message, - * the utility will take care of deleting all the successful messages from SQS. When one or more single message fails - * processing due to exception thrown from {@link SqsMessageHandler#process(SQSMessage)} - * {@link SQSBatchProcessingException} is thrown with all the details of successful and failed messages. - *

- * If all the messages are successfully processes, No SQS messages are deleted explicitly but is rather delegated to - * Lambda execution context for deletion. - *

- * - *

- * If you dont want the utility to throw {@link SQSBatchProcessingException} in case of failures but rather suppress - * it, Refer {@link SqsUtils#batchProcessor(SQSEvent, boolean, Class)} - *

- * - * @param event {@link SQSEvent} received by lambda function. - * @param handler Class implementing {@link SqsMessageHandler} which will be called for each message in event. - * @return List of values returned by {@link SqsMessageHandler#process(SQSMessage)} while processing each message. - * @throws SQSBatchProcessingException if some messages fail during processing. - */ - @Deprecated - public static List batchProcessor(final SQSEvent event, - final Class> handler) { - return batchProcessor(event, false, handler); - } - - /** - * @deprecated - * @see software.amazon.lambda.powertools.batch in powertools-batch module. - * Will be removed in V2. - * - * This utility method is used to process each {@link SQSMessage} inside the received {@link SQSEvent} - * - *

- * The Utility will call {@link SqsMessageHandler#process(SQSMessage)} method for each {@link SQSMessage} - * in the received {@link SQSEvent} - *

- *

- * If any exception is thrown from {@link SqsMessageHandler#process(SQSMessage)} during processing of a message, - * the utility will take care of deleting all the successful messages from SQS. When one or more single message fails - * processing due to exception thrown from {@link SqsMessageHandler#process(SQSMessage)} - * {@link SQSBatchProcessingException} is thrown with all the details of successful and failed messages. - * - *

- * - *

- * If all the messages are successfully processes, No SQS messages are deleted explicitly but is rather delegated to - * Lambda execution context for deletion. - *

- * - *

- * If you dont want the utility to throw {@link SQSBatchProcessingException} in case of failures but rather suppress - * it, Refer {@link SqsUtils#batchProcessor(SQSEvent, boolean, Class)} - *

- * - *

- * If you want certain exceptions to be treated as permanent failures, i.e. exceptions where the result of retrying will - * always be a failure and want these can be immediately moved to the dead letter queue associated to the source SQS queue, - * you can use nonRetryableExceptions parameter to configure such exceptions. - *

- * Make sure function execution role has sqs:GetQueueAttributes permission on source SQS queue and sqs:SendMessage, - * sqs:SendMessageBatch permission for configured DLQ. - *

- * If there is no DLQ configured on source SQS queue and {@link SqsBatch#nonRetryableExceptions()} attribute is set, if - * nonRetryableExceptions occurs from {@link SqsMessageHandler}, such exceptions will still be treated as temporary - * exceptions and the message will be moved back to source SQS queue for reprocessing. The same behaviour will occur if - * for some reason the utility is unable to move the message to the DLQ. An example of this could be because the function - * is missing the correct permissions. - *

- * - * @param event {@link SQSEvent} received by lambda function. - * @param handler Class implementing {@link SqsMessageHandler} which will be called for each message in event. - * @param nonRetryableExceptions exception classes that are to be treated as permanent exceptions and to be moved - * to DLQ. - * @return List of values returned by {@link SqsMessageHandler#process(SQSMessage)} while processing each message. - * @throws SQSBatchProcessingException if some messages fail during processing. - * @see Amazon SQS dead-letter queues - */ - @SafeVarargs - @Deprecated - public static List batchProcessor(final SQSEvent event, - final Class> handler, - final Class... nonRetryableExceptions) { - return batchProcessor(event, false, handler, nonRetryableExceptions); - } - - /** - * @deprecated - * @see software.amazon.lambda.powertools.batch in powertools-batch module. - * Will be removed in V2. - * - * This utility method is used to process each {@link SQSMessage} inside the received {@link SQSEvent} - * - *

- * The Utility will call {@link SqsMessageHandler#process(SQSMessage)} method for each {@link SQSMessage} - * in the received {@link SQSEvent} - *

- *

- * If any exception is thrown from {@link SqsMessageHandler#process(SQSMessage)} during processing of a message, - * the utility will take care of deleting all the successful messages from SQS. When one or more single message fails - * processing due to exception thrown from {@link SqsMessageHandler#process(SQSMessage)} - * {@link SQSBatchProcessingException} is thrown with all the details of successful and failed messages. - *

- * Exception can also be suppressed if desired. - *

- * If all the messages are successfully processes, No SQS messages are deleted explicitly but is rather delegated to - * Lambda execution context for deletion. - *

- * - * @param event {@link SQSEvent} received by lambda function. - * @param suppressException if this is set to true, No {@link SQSBatchProcessingException} is thrown even on failed - * messages. - * @param handler Class implementing {@link SqsMessageHandler} which will be called for each message in event. - * @return List of values returned by {@link SqsMessageHandler#process(SQSMessage)} while processing each message. - * @throws SQSBatchProcessingException if some messages fail during processing and no suppression enabled. - */ - @Deprecated - public static List batchProcessor(final SQSEvent event, - final boolean suppressException, - final Class> handler) { - - SqsMessageHandler handlerInstance = instantiatedHandler(handler); - return batchProcessor(event, suppressException, handlerInstance); - } - - /** - * @deprecated - * @see software.amazon.lambda.powertools.batch in powertools-batch module. - * Will be removed in V2. - * - * This utility method is used to process each {@link SQSMessage} inside the received {@link SQSEvent} - * - *

- * The Utility will call {@link SqsMessageHandler#process(SQSMessage)} method for each {@link SQSMessage} - * in the received {@link SQSEvent} - *

- *

- * If any exception is thrown from {@link SqsMessageHandler#process(SQSMessage)} during processing of a message, - * the utility will take care of deleting all the successful messages from SQS. When one or more single message fails - * processing due to exception thrown from {@link SqsMessageHandler#process(SQSMessage)} - * {@link SQSBatchProcessingException} is thrown with all the details of successful and failed messages. - * - *

- * - *

- * If all the messages are successfully processes, No SQS messages are deleted explicitly but is rather delegated to - * Lambda execution context for deletion. - *

- * - *

- * If you dont want the utility to throw {@link SQSBatchProcessingException} in case of failures but rather suppress - * it, Refer {@link SqsUtils#batchProcessor(SQSEvent, boolean, Class)} - *

- * - *

- * If you want certain exceptions to be treated as permanent failures, i.e. exceptions where the result of retrying will - * always be a failure and want these can be immediately moved to the dead letter queue associated to the source SQS queue, - * you can use nonRetryableExceptions parameter to configure such exceptions. - *

- * Make sure function execution role has sqs:GetQueueAttributes permission on source SQS queue and sqs:SendMessage, - * sqs:SendMessageBatch permission for configured DLQ. - *

- * If there is no DLQ configured on source SQS queue and {@link SqsBatch#nonRetryableExceptions()} attribute is set, if - * nonRetryableExceptions occurs from {@link SqsMessageHandler}, such exceptions will still be treated as temporary - * exceptions and the message will be moved back to source SQS queue for reprocessing. The same behaviour will occur if - * for some reason the utility is unable to move the message to the DLQ. An example of this could be because the function - * is missing the correct permissions. - *

- * - * @param event {@link SQSEvent} received by lambda function. - * @param suppressException if this is set to true, No {@link SQSBatchProcessingException} is thrown even on failed - * messages. - * @param handler Class implementing {@link SqsMessageHandler} which will be called for each message in event. - * @param nonRetryableExceptions exception classes that are to be treated as permanent exceptions and to be moved - * to DLQ. - * @return List of values returned by {@link SqsMessageHandler#process(SQSMessage)} while processing each message. - * @throws SQSBatchProcessingException if some messages fail during processing. - * @see Amazon SQS dead-letter queues - */ - @SafeVarargs - @Deprecated - public static List batchProcessor(final SQSEvent event, - final boolean suppressException, - final Class> handler, - final Class... nonRetryableExceptions) { - - SqsMessageHandler handlerInstance = instantiatedHandler(handler); - return batchProcessor(event, suppressException, handlerInstance, false, nonRetryableExceptions); - } - - /** - * @deprecated - * @see software.amazon.lambda.powertools.batch in powertools-batch module. - * Will be removed in V2. - * - * This utility method is used to process each {@link SQSMessage} inside the received {@link SQSEvent} - * - *

- * The Utility will call {@link SqsMessageHandler#process(SQSMessage)} method for each {@link SQSMessage} - * in the received {@link SQSEvent} - *

- * - *

- * If any exception is thrown from {@link SqsMessageHandler#process(SQSMessage)} during processing of a message, - * the utility will take care of deleting all the successful messages from SQS. When one or more single message fails - * processing due to exception thrown from {@link SqsMessageHandler#process(SQSMessage)} - * {@link SQSBatchProcessingException} is thrown with all the details of successful and failed messages. - * - *

- * - *

- * If all the messages are successfully processes, No SQS messages are deleted explicitly but is rather delegated to - * Lambda execution context for deletion. - *

- * - *

- * If you dont want the utility to throw {@link SQSBatchProcessingException} in case of failures but rather suppress - * it, Refer {@link SqsUtils#batchProcessor(SQSEvent, boolean, Class)} - *

- * - *

- * If you want certain exceptions to be treated as permanent failures, i.e. exceptions where the result of retrying will - * always be a failure and want these can be immediately moved to the dead letter queue associated to the source SQS queue, - * you can use nonRetryableExceptions parameter to configure such exceptions. - *

- * Make sure function execution role has sqs:GetQueueAttributes permission on source SQS queue and sqs:SendMessage, - * sqs:SendMessageBatch permission for configured DLQ. - *

- * If you want such messages to be deleted instead, set deleteNonRetryableMessageFromQueue to true. - *

- * If there is no DLQ configured on source SQS queue and {@link SqsBatch#nonRetryableExceptions()} attribute is set, if - * nonRetryableExceptions occurs from {@link SqsMessageHandler}, such exceptions will still be treated as temporary - * exceptions and the message will be moved back to source SQS queue for reprocessing. The same behaviour will occur if - * for some reason the utility is unable to move the message to the DLQ. An example of this could be because the function - * is missing the correct permissions. - *

- * - * @param event {@link SQSEvent} received by lambda function. - * @param suppressException if this is set to true, No {@link SQSBatchProcessingException} is thrown even on failed - * messages. - * @param handler Class implementing {@link SqsMessageHandler} which will be called for each message in event. - * @param deleteNonRetryableMessageFromQueue If messages with nonRetryableExceptions are to be deleted from SQS queue. - * @param nonRetryableExceptions exception classes that are to be treated as permanent exceptions and to be moved - * to DLQ. - * @return List of values returned by {@link SqsMessageHandler#process(SQSMessage)} while processing each message. - * @throws SQSBatchProcessingException if some messages fail during processing. - * @see Amazon SQS dead-letter queues - */ - @SafeVarargs - @Deprecated - public static List batchProcessor(final SQSEvent event, - final boolean suppressException, - final Class> handler, - final boolean deleteNonRetryableMessageFromQueue, - final Class... nonRetryableExceptions) { - - SqsMessageHandler handlerInstance = instantiatedHandler(handler); - return batchProcessor(event, suppressException, handlerInstance, deleteNonRetryableMessageFromQueue, - nonRetryableExceptions); - } - - /** - * @deprecated - * @see software.amazon.lambda.powertools.batch in powertools-batch module. - * Will be removed in V2. - * - * This utility method is used to process each {@link SQSMessage} inside the received {@link SQSEvent} - * - *

- * The Utility will call {@link SqsMessageHandler#process(SQSMessage)} method for each {@link SQSMessage} - * in the received {@link SQSEvent} - *

- * - *

- * If any exception is thrown from {@link SqsMessageHandler#process(SQSMessage)} during processing of a messages, - * Utility will take care of deleting all the successful messages from SQS. When one or more single message fails - * processing due to exception thrown from {@link SqsMessageHandler#process(SQSMessage)} - * {@link SQSBatchProcessingException} is thrown with all the details of successful and failed messages. - *

- * If all the messages are successfully processes, No SQS messages are deleted explicitly but is rather delegated to - * Lambda execution context for deletion. - *

- * - *

- * If you dont want the utility to throw {@link SQSBatchProcessingException} in case of failures but rather suppress - * it, Refer {@link SqsUtils#batchProcessor(SQSEvent, boolean, SqsMessageHandler)} - *

- * - * @param event {@link SQSEvent} received by lambda function. - * @param handler Instance of class implementing {@link SqsMessageHandler} which will be called for each message in event. - * @return List of values returned by {@link SqsMessageHandler#process(SQSMessage)} while processing each message- - * @throws SQSBatchProcessingException if some messages fail during processing. - */ - @Deprecated - public static List batchProcessor(final SQSEvent event, - final SqsMessageHandler handler) { - return batchProcessor(event, false, handler); - } - - - /** - * @deprecated - * @see software.amazon.lambda.powertools.batch in powertools-batch module. - * Will be removed in V2. - * - * This utility method is used to process each {@link SQSMessage} inside the received {@link SQSEvent} - * - *

- * The Utility will call {@link SqsMessageHandler#process(SQSMessage)} method for each {@link SQSMessage} - * in the received {@link SQSEvent} - *

- * - *

- * If any exception is thrown from {@link SqsMessageHandler#process(SQSMessage)} during processing of a message, - * the utility will take care of deleting all the successful messages from SQS. When one or more single message fails - * processing due to exception thrown from {@link SqsMessageHandler#process(SQSMessage)} - * {@link SQSBatchProcessingException} is thrown with all the details of successful and failed messages. - * - *

- * - *

- * If all the messages are successfully processes, No SQS messages are deleted explicitly but is rather delegated to - * Lambda execution context for deletion. - *

- * - *

- * If you dont want the utility to throw {@link SQSBatchProcessingException} in case of failures but rather suppress - * it, Refer {@link SqsUtils#batchProcessor(SQSEvent, boolean, Class)} - *

- * - *

- * If you want certain exceptions to be treated as permanent failures, i.e. exceptions where the result of retrying will - * always be a failure and want these can be immediately moved to the dead letter queue associated to the source SQS queue, - * you can use nonRetryableExceptions parameter to configure such exceptions. - *

- * Make sure function execution role has sqs:GetQueueAttributes permission on source SQS queue and sqs:SendMessage, - * sqs:SendMessageBatch permission for configured DLQ. - *

- * If there is no DLQ configured on source SQS queue and {@link SqsBatch#nonRetryableExceptions()} attribute is set, if - * nonRetryableExceptions occurs from {@link SqsMessageHandler}, such exceptions will still be treated as temporary - * exceptions and the message will be moved back to source SQS queue for reprocessing.The same behaviour will occur if - * for some reason the utility is unable to moved the message to the DLQ. An example of this could be because the function - * is missing the correct permissions. - *

- * - * @param event {@link SQSEvent} received by lambda function. - * @param handler Instance of class implementing {@link SqsMessageHandler} which will be called for each message in event. - * @param nonRetryableExceptions exception classes that are to be treated as permanent exceptions and to be moved - * to DLQ. - * @return List of values returned by {@link SqsMessageHandler#process(SQSMessage)} while processing each message. - * @throws SQSBatchProcessingException if some messages fail during processing. - * @see Amazon SQS dead-letter queues - */ - @SafeVarargs - @Deprecated - public static List batchProcessor(final SQSEvent event, - final SqsMessageHandler handler, - final Class... nonRetryableExceptions) { - return batchProcessor(event, false, handler, false, nonRetryableExceptions); - } - - - /** - * @deprecated - * @see software.amazon.lambda.powertools.batch in powertools-batch module. - * Will be removed in V2. - * - * This utility method is used to process each {@link SQSMessage} inside the received {@link SQSEvent} - * - *

- * The Utility will call {@link SqsMessageHandler#process(SQSMessage)} method for each {@link SQSMessage} - * in the received {@link SQSEvent} - *

- * - *

- * If any exception is thrown from {@link SqsMessageHandler#process(SQSMessage)} during processing of a messages, - * the utility will take care of deleting all the successful messages from SQS. When one or more single message fails - * processing due to exception thrown from {@link SqsMessageHandler#process(SQSMessage)} - * {@link SQSBatchProcessingException} is thrown with all the details of successful and failed messages. - *

- * Exception can also be suppressed if desired. - *

- * If all the messages are successfully processes, No SQS messages are deleted explicitly but is rather delegated to - * Lambda execution context for deletion. - *

- * - * @param event {@link SQSEvent} received by lambda function. - * @param suppressException if this is set to true, No {@link SQSBatchProcessingException} is thrown even on failed - * messages. - * @param handler Instance of class implementing {@link SqsMessageHandler} which will be called for each message in event. - * @return List of values returned by {@link SqsMessageHandler#process(SQSMessage)} while processing each message. - * @throws SQSBatchProcessingException if some messages fail during processing and no suppression enabled. - */ - @Deprecated - public static List batchProcessor(final SQSEvent event, - final boolean suppressException, - final SqsMessageHandler handler) { - return batchProcessor(event, suppressException, handler, false); - - } - - /** - * @deprecated - * @see software.amazon.lambda.powertools.batch in powertools-batch module. - * Will be removed in V2. - */ - @SafeVarargs - @Deprecated - public static List batchProcessor(final SQSEvent event, - final boolean suppressException, - final SqsMessageHandler handler, - final boolean deleteNonRetryableMessageFromQueue, - final Class... nonRetryableExceptions) { - final List handlerReturn = new ArrayList<>(); - - if (client == null) { - client = (SqsClient) SqsClient.builder() - .overrideConfiguration(ClientOverrideConfiguration.builder() - .putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, - UserAgentConfigurator.getUserAgent(SQS)) - .build()); - } - - BatchContext batchContext = new BatchContext(client); - int offset = 0; - boolean failedBatch = false; - while (offset < event.getRecords().size() && !failedBatch) { - // Get the current message and advance to the next. Doing this here - // makes it easier for us to know where we are up to if we have to - // break out of here early. - SQSMessage message = event.getRecords().get(offset); - offset++; - - // If the batch hasn't failed, try process the message - try { - handlerReturn.add(handler.process(message)); - batchContext.addSuccess(message); - } catch (Exception e) { - - // Record the failure - batchContext.addFailure(message, e); - - // If we are trying to process a message that has a messageGroupId, we are on a FIFO queue. A failure - // now stops us from processing the rest of the batch; we break out of the loop leaving unprocessed - // messages in the queue - // https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html#services-sqs-batchfailurereporting - String messageGroupId = message.getAttributes() != null ? - message.getAttributes().get(MESSAGE_GROUP_ID) : null; - if (messageGroupId != null) { - LOG.info( - "A message in a message batch with messageGroupId {} and messageId {} failed; failing the rest of the batch too" - , messageGroupId, message.getMessageId()); - failedBatch = true; - } - LOG.error("Encountered issue processing message: {}", message.getMessageId(), e); - } - } - - // If we have a FIFO batch failure, unprocessed messages will remain on the queue - // past the failed message. We have to add these to the errors - if (offset < event.getRecords().size()) { - event.getRecords() - .subList(offset, event.getRecords().size()) - .forEach(message -> - { - LOG.info("Skipping message {} as another message with a message group failed in this batch", - message.getMessageId()); - batchContext.addFailure(message, new SkippedMessageDueToFailedBatchException()); - }); - } - - batchContext.processSuccessAndHandleFailed(handlerReturn, suppressException, deleteNonRetryableMessageFromQueue, - nonRetryableExceptions); - return handlerReturn; - } - - private static SqsMessageHandler instantiatedHandler(final Class> handler) { - - try { - if (null == handler.getDeclaringClass()) { - return handler.getDeclaredConstructor().newInstance(); - } - - final Constructor> constructor = - handler.getDeclaredConstructor(handler.getDeclaringClass()); - constructor.setAccessible(true); - return constructor.newInstance(handler.getDeclaringClass().getDeclaredConstructor().newInstance()); - } catch (Exception e) { - LOG.error("Failed creating handler instance", e); - throw new RuntimeException("Unexpected error occurred. Please raise issue at " + - "https://github.com/aws-powertools/powertools-lambda-java/issues", e); - } - } - - private static SQSMessage clonedMessage(final SQSMessage sqsMessage) { - try { - return objectMapper - .readValue(objectMapper.writeValueAsString(sqsMessage), SQSMessage.class); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } - - public static ObjectMapper objectMapper() { - return objectMapper; - } - - public static S3Client s3Client() { - if (null == s3Client) { - s3Client = (S3Client) S3Client.builder() - .overrideConfiguration(ClientOverrideConfiguration.builder() - .putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, - UserAgentConfigurator.getUserAgent(SQS)) - .build()); - SqsUtils.s3Client = S3Client.create(); - } - - return s3Client; - } -} diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/exception/SkippedMessageDueToFailedBatchException.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/exception/SkippedMessageDueToFailedBatchException.java deleted file mode 100644 index fbb4289d8..000000000 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/exception/SkippedMessageDueToFailedBatchException.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs.exception; - -/** - * Indicates that a message has been skipped due to the batch it is - * within failing. - */ -public class SkippedMessageDueToFailedBatchException extends Exception { - - public SkippedMessageDueToFailedBatchException() { - } - -} diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/BatchContext.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/BatchContext.java deleted file mode 100644 index 70cf04fb8..000000000 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/BatchContext.java +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs.internal; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static java.lang.String.format; -import static java.util.Optional.ofNullable; -import static java.util.stream.Collectors.toList; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.services.sqs.SqsClient; -import software.amazon.awssdk.services.sqs.model.DeleteMessageBatchRequest; -import software.amazon.awssdk.services.sqs.model.DeleteMessageBatchRequestEntry; -import software.amazon.awssdk.services.sqs.model.DeleteMessageBatchResponse; -import software.amazon.awssdk.services.sqs.model.GetQueueAttributesRequest; -import software.amazon.awssdk.services.sqs.model.GetQueueAttributesResponse; -import software.amazon.awssdk.services.sqs.model.MessageAttributeValue; -import software.amazon.awssdk.services.sqs.model.QueueAttributeName; -import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequest; -import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequestEntry; -import software.amazon.awssdk.services.sqs.model.SendMessageBatchResponse; -import software.amazon.lambda.powertools.sqs.SQSBatchProcessingException; -import software.amazon.lambda.powertools.sqs.SqsUtils; - -public final class BatchContext { - private static final Logger LOG = LoggerFactory.getLogger(BatchContext.class); - private static final Map QUEUE_ARN_TO_DLQ_URL_MAPPING = new HashMap<>(); - - private final Map messageToException = new HashMap<>(); - private final List success = new ArrayList<>(); - - private final SqsClient client; - - public BatchContext(SqsClient client) { - this.client = client; - } - - public void addSuccess(SQSMessage event) { - success.add(event); - } - - public void addFailure(SQSMessage event, Exception e) { - messageToException.put(event, e); - } - - @SafeVarargs - public final void processSuccessAndHandleFailed(final List successReturns, - final boolean suppressException, - final boolean deleteNonRetryableMessageFromQueue, - final Class... nonRetryableExceptions) { - if (hasFailures()) { - - List exceptions = new ArrayList<>(); - List failedMessages = new ArrayList<>(); - Map nonRetryableMessageToException = new HashMap<>(); - - if (nonRetryableExceptions.length == 0) { - exceptions.addAll(messageToException.values()); - failedMessages.addAll(messageToException.keySet()); - } else { - messageToException.forEach((sqsMessage, exception) -> - { - boolean nonRetryableException = isNonRetryableException(exception, nonRetryableExceptions); - - if (nonRetryableException) { - nonRetryableMessageToException.put(sqsMessage, exception); - } else { - exceptions.add(exception); - failedMessages.add(sqsMessage); - } - }); - } - - List messagesToBeDeleted = new ArrayList<>(success); - - if (!nonRetryableMessageToException.isEmpty() && deleteNonRetryableMessageFromQueue) { - messagesToBeDeleted.addAll(nonRetryableMessageToException.keySet()); - } else if (!nonRetryableMessageToException.isEmpty()) { - - boolean isMovedToDlq = moveNonRetryableMessagesToDlqIfConfigured(nonRetryableMessageToException); - - if (!isMovedToDlq) { - exceptions.addAll(nonRetryableMessageToException.values()); - failedMessages.addAll(nonRetryableMessageToException.keySet()); - } - } - - deleteMessagesFromQueue(messagesToBeDeleted); - - processFailedMessages(successReturns, suppressException, exceptions, failedMessages); - } - } - - private void processFailedMessages(List successReturns, - boolean suppressException, - List exceptions, - List failedMessages) { - if (failedMessages.isEmpty()) { - return; - } - - if (suppressException) { - List messageIds = failedMessages.stream(). - map(SQSMessage::getMessageId) - .collect(toList()); - - LOG.debug(format("[%d] records failed processing, but exceptions are suppressed. " + - "Failed messages %s", failedMessages.size(), messageIds)); - } else { - throw new SQSBatchProcessingException(exceptions, failedMessages, successReturns); - } - } - - private boolean isNonRetryableException(Exception exception, Class[] nonRetryableExceptions) { - return Arrays.stream(nonRetryableExceptions) - .anyMatch(aClass -> aClass.isInstance(exception)); - } - - private boolean moveNonRetryableMessagesToDlqIfConfigured( - Map nonRetryableMessageToException) { - Optional dlqUrl = fetchDlqUrl(nonRetryableMessageToException); - - if (!dlqUrl.isPresent()) { - return false; - } - - List dlqMessages = nonRetryableMessageToException.keySet().stream() - .map(sqsMessage -> - { - Map messageAttributesMap = new HashMap<>(); - - sqsMessage.getMessageAttributes().forEach((s, messageAttribute) -> - { - MessageAttributeValue.Builder builder = MessageAttributeValue.builder(); - - builder - .dataType(messageAttribute.getDataType()) - .stringValue(messageAttribute.getStringValue()); - - if (null != messageAttribute.getBinaryValue()) { - builder.binaryValue(SdkBytes.fromByteBuffer(messageAttribute.getBinaryValue())); - } - - messageAttributesMap.put(s, builder.build()); - }); - - return SendMessageBatchRequestEntry.builder() - .messageBody(sqsMessage.getBody()) - .id(sqsMessage.getMessageId()) - .messageAttributes(messageAttributesMap) - .build(); - }) - .collect(toList()); - - List sendMessageBatchResponses = batchRequest(dlqMessages, 10, entriesToSend -> - { - - SendMessageBatchResponse sendMessageBatchResponse = - client.sendMessageBatch(SendMessageBatchRequest.builder() - .entries(entriesToSend) - .queueUrl(dlqUrl.get()) - .build()); - - - LOG.debug("Response from send batch message to DLQ request {}", sendMessageBatchResponse); - - return sendMessageBatchResponse; - }); - - return sendMessageBatchResponses.stream() - .filter(response -> null != response && response.hasFailed()) - .peek(sendMessageBatchResponse -> LOG.error( - "Failed sending message to the DLQ. Entire batch will be re processed. Check if needed permissions are configured for the function. Response: {}", - sendMessageBatchResponse)) - .count() == 0; - } - - - private Optional fetchDlqUrl(Map nonRetryableMessageToException) { - return nonRetryableMessageToException.keySet().stream() - .findFirst() - .map(sqsMessage -> QUEUE_ARN_TO_DLQ_URL_MAPPING.computeIfAbsent(sqsMessage.getEventSourceArn(), - sourceArn -> - { - String queueUrl = url(sourceArn); - - GetQueueAttributesResponse queueAttributes = - client.getQueueAttributes(GetQueueAttributesRequest.builder() - .attributeNames(QueueAttributeName.REDRIVE_POLICY) - .queueUrl(queueUrl) - .build()); - - return ofNullable(queueAttributes.attributes().get(QueueAttributeName.REDRIVE_POLICY)) - .map(policy -> - { - try { - return SqsUtils.objectMapper().readTree(policy); - } catch (JsonProcessingException e) { - LOG.debug( - "Unable to parse Re drive policy for queue {}. Even if DLQ exists, failed messages will be send back to main queue.", - queueUrl, e); - return null; - } - }) - .map(node -> node.get("deadLetterTargetArn")) - .map(JsonNode::asText) - .map(this::url) - .orElse(null); - })); - } - - private boolean hasFailures() { - return !messageToException.isEmpty(); - } - - private void deleteMessagesFromQueue(final List messages) { - if (!messages.isEmpty()) { - - List entries = - messages.stream().map(m -> DeleteMessageBatchRequestEntry.builder() - .id(m.getMessageId()) - .receiptHandle(m.getReceiptHandle()) - .build()).collect(toList()); - - batchRequest(entries, 10, entriesToDelete -> - { - DeleteMessageBatchRequest request = DeleteMessageBatchRequest.builder() - .queueUrl(url(messages.get(0).getEventSourceArn())) - .entries(entriesToDelete) - .build(); - - DeleteMessageBatchResponse deleteMessageBatchResponse = client.deleteMessageBatch(request); - - LOG.debug("Response from delete request {}", deleteMessageBatchResponse); - - return deleteMessageBatchResponse; - }); - } - } - - private List batchRequest(final List listOFEntries, - final int size, - final Function, R> batchLogic) { - - return IntStream.range(0, listOFEntries.size()) - .filter(index -> index % size == 0) - .mapToObj(index -> listOFEntries.subList(index, Math.min(index + size, listOFEntries.size()))) - .map(batchLogic) - .collect(Collectors.toList()); - } - - private String url(String queueArn) { - String[] arnArray = queueArn.split(":"); - return String.format("https://sqs.%s.amazonaws.com/%s/%s", arnArray[3], arnArray[4], arnArray[5]); - } -} diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/SqsLargeMessageAspect.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/SqsLargeMessageAspect.java deleted file mode 100644 index e5176e13a..000000000 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/SqsLargeMessageAspect.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs.internal; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static java.lang.String.format; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isHandlerMethod; -import static software.amazon.lambda.powertools.sqs.SqsUtils.s3Client; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.function.Function; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Pointcut; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import software.amazon.awssdk.core.ResponseInputStream; -import software.amazon.awssdk.core.exception.SdkClientException; -import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; -import software.amazon.awssdk.services.s3.model.GetObjectRequest; -import software.amazon.awssdk.services.s3.model.GetObjectResponse; -import software.amazon.awssdk.services.s3.model.S3Exception; -import software.amazon.awssdk.utils.IoUtils; -import software.amazon.lambda.powertools.sqs.SqsLargeMessage; -import software.amazon.payloadoffloading.PayloadS3Pointer; - -@Aspect -public class SqsLargeMessageAspect { - - private static final Logger LOG = LoggerFactory.getLogger(SqsLargeMessageAspect.class); - - public static List processMessages(final List records) { - List s3Pointers = new ArrayList<>(); - for (SQSMessage sqsMessage : records) { - if (isBodyLargeMessagePointer(sqsMessage.getBody())) { - - PayloadS3Pointer s3Pointer = Optional.ofNullable(PayloadS3Pointer.fromJson(sqsMessage.getBody())) - .orElseThrow(() -> new FailedProcessingLargePayloadException( - format("Failed processing SQS body to extract S3 details. [ %s ].", - sqsMessage.getBody()))); - - ResponseInputStream s3Object = callS3Gracefully(s3Pointer, pointer -> - { - ResponseInputStream response = - s3Client().getObject(GetObjectRequest.builder() - .bucket(pointer.getS3BucketName()) - .key(pointer.getS3Key()) - .build()); - - LOG.debug("Object downloaded with key: " + s3Pointer.getS3Key()); - return response; - }); - - sqsMessage.setBody(readStringFromS3Object(s3Object, s3Pointer)); - s3Pointers.add(s3Pointer); - } - } - - return s3Pointers; - } - - private static boolean isBodyLargeMessagePointer(String record) { - return record.startsWith("[\"software.amazon.payloadoffloading.PayloadS3Pointer\""); - } - - private static String readStringFromS3Object(ResponseInputStream response, - PayloadS3Pointer s3Pointer) { - try (ResponseInputStream content = response) { - return IoUtils.toUtf8String(content); - } catch (IOException e) { - LOG.error("Error converting S3 object to String", e); - throw new FailedProcessingLargePayloadException( - format("Failed processing S3 record with [Bucket Name: %s Bucket Key: %s]", - s3Pointer.getS3BucketName(), s3Pointer.getS3Key()), e); - } - } - - public static void deleteMessage(PayloadS3Pointer s3Pointer) { - callS3Gracefully(s3Pointer, pointer -> - { - s3Client().deleteObject(DeleteObjectRequest.builder() - .bucket(pointer.getS3BucketName()) - .key(pointer.getS3Key()) - .build()); - LOG.info("Message deleted from S3: " + s3Pointer.toJson()); - return null; - }); - } - - private static R callS3Gracefully(final PayloadS3Pointer pointer, - final Function function) { - try { - return function.apply(pointer); - } catch (S3Exception e) { - LOG.error("A service exception", e); - throw new FailedProcessingLargePayloadException( - format("Failed processing S3 record with [Bucket Name: %s Bucket Key: %s]", - pointer.getS3BucketName(), pointer.getS3Key()), e); - } catch (SdkClientException e) { - LOG.error("Some sort of client exception", e); - throw new FailedProcessingLargePayloadException( - format("Failed processing S3 record with [Bucket Name: %s Bucket Key: %s]", - pointer.getS3BucketName(), pointer.getS3Key()), e); - } - } - - public static boolean placedOnSqsEventRequestHandler(ProceedingJoinPoint pjp) { - return pjp.getArgs().length == 2 - && pjp.getArgs()[0] instanceof SQSEvent - && pjp.getArgs()[1] instanceof Context; - } - - @SuppressWarnings({"EmptyMethod"}) - @Pointcut("@annotation(sqsLargeMessage)") - public void callAt(SqsLargeMessage sqsLargeMessage) { - } - - @Around(value = "callAt(sqsLargeMessage) && execution(@SqsLargeMessage * *.*(..))", argNames = "pjp,sqsLargeMessage") - public Object around(ProceedingJoinPoint pjp, - SqsLargeMessage sqsLargeMessage) throws Throwable { - Object[] proceedArgs = pjp.getArgs(); - - if (isHandlerMethod(pjp) - && placedOnSqsEventRequestHandler(pjp)) { - List pointersToDelete = rewriteMessages((SQSEvent) proceedArgs[0]); - - Object proceed = pjp.proceed(proceedArgs); - - if (sqsLargeMessage.deletePayloads()) { - pointersToDelete.forEach(SqsLargeMessageAspect::deleteMessage); - } - return proceed; - } - - return pjp.proceed(proceedArgs); - } - - private List rewriteMessages(SQSEvent sqsEvent) { - List records = sqsEvent.getRecords(); - return processMessages(records); - } - - public static class FailedProcessingLargePayloadException extends RuntimeException { - public FailedProcessingLargePayloadException(String message, Throwable cause) { - super(message, cause); - } - - public FailedProcessingLargePayloadException(String message) { - super(message); - } - } -} diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/SqsMessageBatchProcessorAspect.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/SqsMessageBatchProcessorAspect.java deleted file mode 100644 index ff0b5b014..000000000 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/SqsMessageBatchProcessorAspect.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs.internal; - -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isHandlerMethod; -import static software.amazon.lambda.powertools.sqs.SqsUtils.batchProcessor; -import static software.amazon.lambda.powertools.sqs.internal.SqsLargeMessageAspect.placedOnSqsEventRequestHandler; - -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Pointcut; -import software.amazon.lambda.powertools.sqs.SqsBatch; - -@Aspect -public class SqsMessageBatchProcessorAspect { - - @SuppressWarnings({"EmptyMethod"}) - @Pointcut("@annotation(sqsBatch)") - public void callAt(SqsBatch sqsBatch) { - } - - @Around(value = "callAt(sqsBatch) && execution(@SqsBatch * *.*(..))", argNames = "pjp,sqsBatch") - public Object around(ProceedingJoinPoint pjp, - SqsBatch sqsBatch) throws Throwable { - Object[] proceedArgs = pjp.getArgs(); - - if (isHandlerMethod(pjp) - && placedOnSqsEventRequestHandler(pjp)) { - - SQSEvent sqsEvent = (SQSEvent) proceedArgs[0]; - - batchProcessor(sqsEvent, - sqsBatch.suppressException(), - sqsBatch.value(), - sqsBatch.deleteNonRetryableMessageFromQueue(), - sqsBatch.nonRetryableExceptions()); - } - - return pjp.proceed(proceedArgs); - } -} diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SampleSqsHandler.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SampleSqsHandler.java deleted file mode 100644 index 557aa214d..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SampleSqsHandler.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs; - -import com.amazonaws.services.lambda.runtime.events.SQSEvent; - -public class SampleSqsHandler implements SqsMessageHandler { - private int counter; - - @Override - public String process(SQSEvent.SQSMessage message) { - return String.valueOf(counter++); - } -} diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsBatchProcessorTest.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsBatchProcessorTest.java deleted file mode 100644 index c0c334e78..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsBatchProcessorTest.java +++ /dev/null @@ -1,386 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.when; -import static software.amazon.lambda.powertools.sqs.SqsUtils.batchProcessor; -import static software.amazon.lambda.powertools.sqs.SqsUtils.overrideSqsClient; - -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.IOException; -import java.util.HashMap; -import java.util.List; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; -import org.mockito.ArgumentCaptor; -import software.amazon.awssdk.services.sqs.SqsClient; -import software.amazon.awssdk.services.sqs.model.DeleteMessageBatchRequest; -import software.amazon.awssdk.services.sqs.model.GetQueueAttributesRequest; -import software.amazon.awssdk.services.sqs.model.GetQueueAttributesResponse; -import software.amazon.awssdk.services.sqs.model.QueueAttributeName; -import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequest; - -class SqsUtilsBatchProcessorTest { - - private static final SqsClient sqsClient = mock(SqsClient.class); - private static final SqsClient interactionClient = mock(SqsClient.class); - private static final ObjectMapper MAPPER = new ObjectMapper(); - private SQSEvent event; - - @BeforeEach - void setUp() throws IOException { - reset(sqsClient, interactionClient); - event = MAPPER.readValue(this.getClass().getResource("/sampleSqsBatchEvent.json"), SQSEvent.class); - overrideSqsClient(sqsClient); - } - - @Test - void shouldBatchProcessAndNotDeleteMessagesWhenAllSuccess() { - List returnValues = batchProcessor(event, false, (message) -> - { - interactionClient.listQueues(); - return "Success"; - }); - - assertThat(returnValues) - .hasSize(2) - .containsExactly("Success", "Success"); - - verify(interactionClient, times(2)).listQueues(); - verifyNoInteractions(sqsClient); - } - - @ParameterizedTest - @ValueSource(classes = {SampleInnerSqsHandler.class, SampleSqsHandler.class}) - void shouldBatchProcessViaClassAndNotDeleteMessagesWhenAllSuccess( - Class> handler) { - List returnValues = batchProcessor(event, handler); - - assertThat(returnValues) - .hasSize(2) - .containsExactly("0", "1"); - - verifyNoInteractions(sqsClient); - } - - @Test - void shouldBatchProcessAndDeleteSuccessMessageOnPartialFailures() { - String failedId = "2e1424d4-f796-459a-8184-9c92662be6da"; - - SqsMessageHandler failedHandler = (message) -> - { - if (failedId.equals(message.getMessageId())) { - throw new RuntimeException("Failed processing"); - } - - interactionClient.listQueues(); - return "Success"; - }; - - assertThatExceptionOfType(SQSBatchProcessingException.class) - .isThrownBy(() -> batchProcessor(event, failedHandler)) - .satisfies(e -> - { - - assertThat(e.successMessageReturnValues()) - .hasSize(1) - .contains("Success"); - - assertThat(e.getFailures()) - .hasSize(1) - .extracting("messageId") - .contains(failedId); - - assertThat(e.getExceptions()) - .hasSize(1) - .extracting("detailMessage") - .contains("Failed processing"); - }); - - verify(interactionClient).listQueues(); - - ArgumentCaptor captor = ArgumentCaptor.forClass(DeleteMessageBatchRequest.class); - verify(sqsClient).deleteMessageBatch(captor.capture()); - - assertThat(captor.getValue()) - .hasFieldOrPropertyWithValue("queueUrl", "https://sqs.us-east-2.amazonaws.com/123456789012/my-queue"); - } - - @Test - void shouldBatchProcessAndFullFailuresInBatch() { - SqsMessageHandler failedHandler = (message) -> - { - throw new RuntimeException(message.getMessageId()); - }; - - assertThatExceptionOfType(SQSBatchProcessingException.class) - .isThrownBy(() -> batchProcessor(event, failedHandler)) - .satisfies(e -> - { - - assertThat(e.successMessageReturnValues()) - .isEmpty(); - - assertThat(e.getFailures()) - .hasSize(2) - .extracting("messageId") - .containsExactly("059f36b4-87a3-44ab-83d2-661975830a7d", - "2e1424d4-f796-459a-8184-9c92662be6da"); - - assertThat(e.getExceptions()) - .hasSize(2) - .extracting("detailMessage") - .containsExactly("059f36b4-87a3-44ab-83d2-661975830a7d", - "2e1424d4-f796-459a-8184-9c92662be6da"); - }); - - verifyNoInteractions(sqsClient); - } - - @Test - void shouldBatchProcessViaClassAndDeleteSuccessMessageOnPartialFailures() { - assertThatExceptionOfType(SQSBatchProcessingException.class) - .isThrownBy(() -> batchProcessor(event, FailureSampleInnerSqsHandler.class)) - .satisfies(e -> - { - - assertThat(e.successMessageReturnValues()) - .hasSize(1) - .contains("Success"); - - assertThat(e.getFailures()) - .hasSize(1) - .extracting("messageId") - .contains("2e1424d4-f796-459a-8184-9c92662be6da"); - - assertThat(e.getExceptions()) - .hasSize(1) - .extracting("detailMessage") - .contains("Failed processing"); - }); - - verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); - } - - - @Test - void shouldBatchProcessAndSuppressExceptions() { - String failedId = "2e1424d4-f796-459a-8184-9c92662be6da"; - - SqsMessageHandler failedHandler = (message) -> - { - if (failedId.equals(message.getMessageId())) { - throw new RuntimeException("Failed processing"); - } - - interactionClient.listQueues(); - return "Success"; - }; - - List returnValues = batchProcessor(event, true, failedHandler); - - assertThat(returnValues) - .hasSize(1) - .contains("Success"); - - verify(interactionClient).listQueues(); - verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); - } - - @Test - void shouldBatchProcessViaClassAndSuppressExceptions() { - List returnValues = batchProcessor(event, true, FailureSampleInnerSqsHandler.class); - - assertThat(returnValues) - .hasSize(1) - .contains("Success"); - - verify(interactionClient).listQueues(); - verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); - } - - @Test - void shouldBatchProcessAndMoveNonRetryableExceptionToDlq() { - String failedId = "2e1424d4-f796-459a-8184-9c92662be6da"; - HashMap attributes = new HashMap<>(); - - attributes.put(QueueAttributeName.REDRIVE_POLICY, "{\n" + - " \"deadLetterTargetArn\": \"arn:aws:sqs:us-east-2:123456789012:retry-queue\",\n" + - " \"maxReceiveCount\": 2\n" + - "}"); - - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn( - GetQueueAttributesResponse.builder() - .attributes(attributes) - .build()); - - List batchProcessor = batchProcessor(event, (message) -> - { - if (failedId.equals(message.getMessageId())) { - throw new IllegalStateException("Failed processing"); - } - - interactionClient.listQueues(); - return "Success"; - }, IllegalStateException.class, IllegalArgumentException.class); - - assertThat(batchProcessor) - .hasSize(1); - - verify(sqsClient).sendMessageBatch(any(SendMessageBatchRequest.class)); - } - - @Test - void shouldBatchProcessAndDeleteNonRetryableException() { - String failedId = "2e1424d4-f796-459a-8184-9c92662be6da"; - HashMap attributes = new HashMap<>(); - - attributes.put(QueueAttributeName.REDRIVE_POLICY, "{\n" + - " \"deadLetterTargetArn\": \"arn:aws:sqs:us-east-2:123456789012:retry-queue\",\n" + - " \"maxReceiveCount\": 2\n" + - "}"); - - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn( - GetQueueAttributesResponse.builder() - .attributes(attributes) - .build()); - - List batchProcessor = batchProcessor(event, false, (message) -> - { - if (failedId.equals(message.getMessageId())) { - throw new IllegalStateException("Failed processing"); - } - - interactionClient.listQueues(); - return "Success"; - }, true, IllegalStateException.class, IllegalArgumentException.class); - - assertThat(batchProcessor) - .hasSize(1); - - verify(sqsClient, times(0)).sendMessageBatch(any(SendMessageBatchRequest.class)); - verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); - } - - @Test - void shouldDeleteSuccessfulMessageInBatchesOfT10orLess() throws IOException { - SQSEvent batch25Message = - MAPPER.readValue(this.getClass().getResource("/sampleSqsBatchEventBatchSize25.json"), SQSEvent.class); - - assertThatExceptionOfType(SQSBatchProcessingException.class) - .isThrownBy(() -> batchProcessor(batch25Message, FailureSampleInnerSqsHandler.class)) - .satisfies(e -> - { - - assertThat(e.successMessageReturnValues()) - .hasSize(24) - .contains("Success"); - - assertThat(e.getFailures()) - .hasSize(1) - .extracting("messageId") - .contains("2e1424d4-f796-459a-8184-9c92662be6da"); - - assertThat(e.getExceptions()) - .hasSize(1) - .extracting("detailMessage") - .contains("Failed processing"); - }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(DeleteMessageBatchRequest.class); - - verify(sqsClient, times(3)).deleteMessageBatch(captor.capture()); - - assertThat(captor.getAllValues()) - .hasSize(3) - .flatMap(DeleteMessageBatchRequest::entries) - .hasSize(24); - } - - @Test - void shouldBatchProcessAndMoveNonRetryableExceptionToDlqInBatchesOfT10orLess() throws IOException { - SQSEvent batch25Message = - MAPPER.readValue(this.getClass().getResource("/sampleSqsBatchEventBatchSize25.json"), SQSEvent.class); - - HashMap attributes = new HashMap<>(); - - attributes.put(QueueAttributeName.REDRIVE_POLICY, "{\n" + - " \"deadLetterTargetArn\": \"arn:aws:sqs:us-east-2:123456789012:retry-queue\",\n" + - " \"maxReceiveCount\": 2\n" + - "}"); - - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn( - GetQueueAttributesResponse.builder() - .attributes(attributes) - .build()); - - List batchProcessor = batchProcessor(batch25Message, (message) -> - { - if ("2e1424d4-f796-459a-8184-9c92662be6da".equals(message.getMessageId())) { - interactionClient.listQueues(); - return "Success"; - } - - throw new IllegalStateException("Failed processing"); - }, IllegalStateException.class, IllegalArgumentException.class); - - assertThat(batchProcessor) - .hasSize(1); - - ArgumentCaptor captor = ArgumentCaptor.forClass(SendMessageBatchRequest.class); - - - verify(sqsClient, times(3)).sendMessageBatch(captor.capture()); - - assertThat(captor.getAllValues()) - .hasSize(3) - .flatMap(SendMessageBatchRequest::entries) - .hasSize(24); - } - - public class SampleInnerSqsHandler implements SqsMessageHandler { - private int counter; - - @Override - public String process(SQSMessage message) { - interactionClient.listQueues(); - return String.valueOf(counter++); - } - } - - public class FailureSampleInnerSqsHandler implements SqsMessageHandler { - @Override - public String process(SQSEvent.SQSMessage message) { - if ("2e1424d4-f796-459a-8184-9c92662be6da".equals(message.getMessageId())) { - throw new RuntimeException("Failed processing"); - } - - interactionClient.listQueues(); - return "Success"; - } - } -} \ No newline at end of file diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsFifoBatchProcessorTest.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsFifoBatchProcessorTest.java deleted file mode 100644 index bfc555405..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsFifoBatchProcessorTest.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static software.amazon.lambda.powertools.sqs.SqsUtils.batchProcessor; -import static software.amazon.lambda.powertools.sqs.SqsUtils.overrideSqsClient; - -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import com.amazonaws.services.lambda.runtime.tests.EventLoader; -import java.io.IOException; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoSession; -import org.mockito.quality.Strictness; -import software.amazon.awssdk.services.sqs.SqsClient; -import software.amazon.awssdk.services.sqs.model.DeleteMessageBatchRequest; -import software.amazon.awssdk.services.sqs.model.DeleteMessageBatchRequestEntry; -import software.amazon.awssdk.services.sqs.model.DeleteMessageBatchResponse; - -public class SqsUtilsFifoBatchProcessorTest { - - private static SQSEvent sqsBatchEvent; - private MockitoSession session; - - @Mock - private SqsClient sqsClient; - - @Captor - private ArgumentCaptor deleteMessageBatchCaptor; - - public SqsUtilsFifoBatchProcessorTest() throws IOException { - sqsBatchEvent = EventLoader.loadSQSEvent("SqsFifoBatchEvent.json"); - } - - @BeforeEach - public void setup() { - // Establish a strict mocking session. This means that any - // calls to a mock that have not been stubbed will throw - this.session = Mockito.mockitoSession() - .strictness(Strictness.STRICT_STUBS) - .initMocks(this) - .startMocking(); - - overrideSqsClient(sqsClient); - } - - @AfterEach - public void tearDown() { - session.finishMocking(); - } - - @Test - public void processWholeBatch() { - // Act - AtomicInteger processedCount = new AtomicInteger(); - List results = batchProcessor(sqsBatchEvent, false, (message) -> - { - processedCount.getAndIncrement(); - return true; - }); - - // Assert - assertThat(processedCount.get()).isEqualTo(3); - assertThat(results.size()).isEqualTo(3); - } - - /** - * Check that a failure in the middle of the batch: - * - deletes the successful message explicitly from SQS - * - marks the failed and subsequent message as failed - * - does not delete the failed or subsequent message - */ - @Test - public void singleFailureInMiddleOfBatch() { - // Arrange - Mockito.when(sqsClient.deleteMessageBatch(deleteMessageBatchCaptor.capture())) - .thenReturn(DeleteMessageBatchResponse - .builder().build()); - - // Act - AtomicInteger processedCount = new AtomicInteger(); - assertThatExceptionOfType(SQSBatchProcessingException.class) - .isThrownBy(() -> batchProcessor(sqsBatchEvent, false, (message) -> - { - int value = processedCount.getAndIncrement(); - if (value == 1) { - throw new RuntimeException("Whoops"); - } - return true; - })) - - // Assert - .isInstanceOf(SQSBatchProcessingException.class) - .satisfies(e -> - { - List failures = ((SQSBatchProcessingException) e).getFailures(); - assertThat(failures.size()).isEqualTo(2); - List failureIds = failures.stream() - .map(SQSEvent.SQSMessage::getMessageId) - .collect(Collectors.toList()); - assertThat(failureIds).contains(sqsBatchEvent.getRecords().get(1).getMessageId()); - assertThat(failureIds).contains(sqsBatchEvent.getRecords().get(2).getMessageId()); - }); - - DeleteMessageBatchRequest deleteRequest = deleteMessageBatchCaptor.getValue(); - List messageIds = deleteRequest.entries().stream() - .map(DeleteMessageBatchRequestEntry::id) - .collect(Collectors.toList()); - assertThat(deleteRequest.entries().size()).isEqualTo(1); - assertThat(messageIds.contains(sqsBatchEvent.getRecords().get(0).getMessageId())).isTrue(); - - } - - @Test - public void singleFailureAtEndOfBatch() { - - // Arrange - Mockito.when(sqsClient.deleteMessageBatch(deleteMessageBatchCaptor.capture())) - .thenReturn(DeleteMessageBatchResponse - .builder().build()); - - - // Act - AtomicInteger processedCount = new AtomicInteger(); - assertThatExceptionOfType(SQSBatchProcessingException.class) - .isThrownBy(() -> batchProcessor(sqsBatchEvent, false, (message) -> - { - int value = processedCount.getAndIncrement(); - if (value == 2) { - throw new RuntimeException("Whoops"); - } - return true; - })); - - // Assert - DeleteMessageBatchRequest deleteRequest = deleteMessageBatchCaptor.getValue(); - List messageIds = deleteRequest.entries().stream() - .map(DeleteMessageBatchRequestEntry::id) - .collect(Collectors.toList()); - assertThat(deleteRequest.entries().size()).isEqualTo(2); - assertThat(messageIds.contains(sqsBatchEvent.getRecords().get(0).getMessageId())).isTrue(); - assertThat(messageIds.contains(sqsBatchEvent.getRecords().get(1).getMessageId())).isTrue(); - assertThat(messageIds.contains(sqsBatchEvent.getRecords().get(2).getMessageId())).isFalse(); - } - - @Test - public void messageFailureStopsGroupProcessing() { - String groupToFail = sqsBatchEvent.getRecords().get(0).getAttributes().get("MessageGroupId"); - - assertThatExceptionOfType(SQSBatchProcessingException.class) - .isThrownBy(() -> batchProcessor(sqsBatchEvent, (message) -> - { - String groupId = message.getAttributes().get("MessageGroupId"); - if (groupId.equals(groupToFail)) { - throw new RuntimeException("Failed processing"); - } - return groupId; - })) - .satisfies(e -> - { - assertThat(e.successMessageReturnValues().size()).isEqualTo(0); - assertThat(e.successMessageReturnValues().contains(groupToFail)).isFalse(); - }); - } - -} diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsLargeMessageTest.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsLargeMessageTest.java deleted file mode 100644 index afc426976..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsLargeMessageTest.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; - -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Consumer; -import java.util.stream.Stream; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; -import org.junit.jupiter.params.provider.ValueSource; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import software.amazon.awssdk.core.ResponseInputStream; -import software.amazon.awssdk.core.exception.SdkClientException; -import software.amazon.awssdk.http.AbortableInputStream; -import software.amazon.awssdk.services.s3.S3Client; -import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; -import software.amazon.awssdk.services.s3.model.GetObjectRequest; -import software.amazon.awssdk.services.s3.model.GetObjectResponse; -import software.amazon.awssdk.services.s3.model.S3Exception; -import software.amazon.awssdk.utils.StringInputStream; -import software.amazon.lambda.powertools.sqs.internal.SqsLargeMessageAspect; - -class SqsUtilsLargeMessageTest { - - private static final String BUCKET_NAME = "ms-extended-sqs-client"; - private static final String BUCKET_KEY = "c71eb2ae-37e0-4265-8909-32f4153faddf"; - @Mock - private S3Client s3Client; - - private static Stream exception() { - return Stream.of(Arguments.of(S3Exception.builder() - .message("Service Exception") - .build()), - Arguments.of(SdkClientException.builder() - .message("Client Exception") - .build())); - } - - @BeforeEach - void setUp() { - openMocks(this); - SqsUtils.overrideS3Client(s3Client); - } - - @Test - public void testLargeMessage() { - ResponseInputStream s3Response = - new ResponseInputStream<>(GetObjectResponse.builder().build(), - AbortableInputStream.create(new ByteArrayInputStream("A big message".getBytes()))); - when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3Response); - - SQSEvent sqsEvent = messageWithBody( - "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + - "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"); - - Map sqsMessage = SqsUtils.enrichedMessageFromS3(sqsEvent, sqsMessages -> - { - Map someBusinessLogic = new HashMap<>(); - someBusinessLogic.put("Message", sqsMessages.get(0).getBody()); - return someBusinessLogic; - }); - - assertThat(sqsMessage) - .hasSize(1) - .containsEntry("Message", "A big message"); - - ArgumentCaptor delete = ArgumentCaptor.forClass(DeleteObjectRequest.class); - - verify(s3Client).deleteObject(delete.capture()); - - Assertions.assertThat(delete.getValue()) - .satisfies((Consumer) deleteObjectRequest -> - { - assertThat(deleteObjectRequest.bucket()) - .isEqualTo(BUCKET_NAME); - - assertThat(deleteObjectRequest.key()) - .isEqualTo(BUCKET_KEY); - }); - } - - @ParameterizedTest - @ValueSource(booleans = {true, false}) - public void testLargeMessageDeleteFromS3Toggle(boolean deleteS3Payload) { - ResponseInputStream s3Response = - new ResponseInputStream<>(GetObjectResponse.builder().build(), - AbortableInputStream.create(new ByteArrayInputStream("A big message".getBytes()))); - - when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3Response); - - SQSEvent sqsEvent = messageWithBody( - "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + - "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"); - - Map sqsMessage = SqsUtils.enrichedMessageFromS3(sqsEvent, deleteS3Payload, sqsMessages -> - { - Map someBusinessLogic = new HashMap<>(); - someBusinessLogic.put("Message", sqsMessages.get(0).getBody()); - return someBusinessLogic; - }); - - assertThat(sqsMessage) - .hasSize(1) - .containsEntry("Message", "A big message"); - if (deleteS3Payload) { - ArgumentCaptor delete = ArgumentCaptor.forClass(DeleteObjectRequest.class); - - verify(s3Client).deleteObject(delete.capture()); - - Assertions.assertThat(delete.getValue()) - .satisfies((Consumer) deleteObjectRequest -> - { - assertThat(deleteObjectRequest.bucket()) - .isEqualTo(BUCKET_NAME); - - assertThat(deleteObjectRequest.key()) - .isEqualTo(BUCKET_KEY); - }); - } else { - verify(s3Client, never()).deleteObject(any(DeleteObjectRequest.class)); - } - } - - @Test - public void shouldNotProcessSmallMessageBody() { - ResponseInputStream s3Response = - new ResponseInputStream<>(GetObjectResponse.builder().build(), - AbortableInputStream.create(new ByteArrayInputStream("A big message".getBytes()))); - - when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3Response); - - SQSEvent sqsEvent = messageWithBody("This is small message"); - - Map sqsMessage = SqsUtils.enrichedMessageFromS3(sqsEvent, sqsMessages -> - { - Map someBusinessLogic = new HashMap<>(); - someBusinessLogic.put("Message", sqsMessages.get(0).getBody()); - return someBusinessLogic; - }); - - assertThat(sqsMessage) - .containsEntry("Message", "This is small message"); - - verifyNoInteractions(s3Client); - } - - @ParameterizedTest - @MethodSource("exception") - public void shouldFailEntireBatchIfFailedDownloadingFromS3(RuntimeException exception) { - when(s3Client.getObject(any(GetObjectRequest.class))).thenThrow(exception); - - String messageBody = - "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + - "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"; - SQSEvent sqsEvent = messageWithBody(messageBody); - - assertThatExceptionOfType(SqsLargeMessageAspect.FailedProcessingLargePayloadException.class) - .isThrownBy(() -> SqsUtils.enrichedMessageFromS3(sqsEvent, sqsMessages -> sqsMessages.get(0).getBody())) - .withCause(exception); - - verify(s3Client, never()).deleteObject(any(DeleteObjectRequest.class)); - } - - @Test - public void shouldFailEntireBatchIfFailedProcessingDownloadMessageFromS3() { - ResponseInputStream s3Response = - new ResponseInputStream<>(GetObjectResponse.builder().build(), - AbortableInputStream.create(new StringInputStream("test") { - @Override - public void close() throws IOException { - throw new IOException("Failed"); - } - })); - - when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3Response); - - String messageBody = - "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + - "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"; - SQSEvent sqsEvent = messageWithBody(messageBody); - - assertThatExceptionOfType(SqsLargeMessageAspect.FailedProcessingLargePayloadException.class) - .isThrownBy(() -> SqsUtils.enrichedMessageFromS3(sqsEvent, sqsMessages -> sqsMessages.get(0).getBody())) - .withCauseInstanceOf(IOException.class); - - verify(s3Client, never()).deleteObject(any(DeleteObjectRequest.class)); - } - - private SQSEvent messageWithBody(String messageBody) { - SQSMessage sqsMessage = new SQSMessage(); - sqsMessage.setBody(messageBody); - SQSEvent sqsEvent = new SQSEvent(); - sqsEvent.setRecords(singletonList(sqsMessage)); - return sqsEvent; - } -} \ No newline at end of file diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/LambdaHandlerApiGateway.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/LambdaHandlerApiGateway.java deleted file mode 100644 index 3bad9644f..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/LambdaHandlerApiGateway.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs.handlers; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; -import software.amazon.lambda.powertools.sqs.SampleSqsHandler; -import software.amazon.lambda.powertools.sqs.SqsBatch; -import software.amazon.lambda.powertools.sqs.SqsLargeMessage; - -public class LambdaHandlerApiGateway implements RequestHandler { - - @Override - @SqsLargeMessage - @SqsBatch(value = SampleSqsHandler.class) - public String handleRequest(APIGatewayProxyRequestEvent sqsEvent, Context context) { - return sqsEvent.getBody(); - } -} diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchFailureSuppressedHandler.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchFailureSuppressedHandler.java deleted file mode 100644 index 172179057..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchFailureSuppressedHandler.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs.handlers; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static software.amazon.lambda.powertools.sqs.internal.SqsMessageBatchProcessorAspectTest.interactionClient; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import software.amazon.lambda.powertools.sqs.SqsBatch; -import software.amazon.lambda.powertools.sqs.SqsMessageHandler; - -public class PartialBatchFailureSuppressedHandler implements RequestHandler { - @Override - @SqsBatch(value = InnerMessageHandler.class, suppressException = true) - public String handleRequest(final SQSEvent sqsEvent, - final Context context) { - return "Success"; - } - - private class InnerMessageHandler implements SqsMessageHandler { - - @Override - public String process(SQSMessage message) { - if ("2e1424d4-f796-459a-8184-9c92662be6da".equals(message.getMessageId())) { - throw new RuntimeException("2e1424d4-f796-459a-8184-9c92662be6da"); - } - - interactionClient.listQueues(); - return "Success"; - } - } -} diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchPartialFailureHandler.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchPartialFailureHandler.java deleted file mode 100644 index 6e3971269..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchPartialFailureHandler.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs.handlers; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static software.amazon.lambda.powertools.sqs.internal.SqsMessageBatchProcessorAspectTest.interactionClient; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import software.amazon.lambda.powertools.sqs.SqsBatch; -import software.amazon.lambda.powertools.sqs.SqsMessageHandler; - -public class PartialBatchPartialFailureHandler implements RequestHandler { - @Override - @SqsBatch(InnerMessageHandler.class) - public String handleRequest(final SQSEvent sqsEvent, - final Context context) { - return "Success"; - } - - private class InnerMessageHandler implements SqsMessageHandler { - - @Override - public String process(SQSMessage message) { - if ("2e1424d4-f796-459a-8184-9c92662be6da".equals(message.getMessageId())) { - throw new RuntimeException("2e1424d4-f796-459a-8184-9c92662be6da"); - } - - interactionClient.listQueues(); - return "Success"; - } - } -} diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchSuccessHandler.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchSuccessHandler.java deleted file mode 100644 index acfcd7109..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchSuccessHandler.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs.handlers; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static software.amazon.lambda.powertools.sqs.internal.SqsMessageBatchProcessorAspectTest.interactionClient; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import software.amazon.lambda.powertools.sqs.SqsBatch; -import software.amazon.lambda.powertools.sqs.SqsMessageHandler; - -public class PartialBatchSuccessHandler implements RequestHandler { - @Override - @SqsBatch(InnerMessageHandler.class) - public String handleRequest(final SQSEvent sqsEvent, - final Context context) { - return "Success"; - } - - private class InnerMessageHandler implements SqsMessageHandler { - - @Override - public String process(SQSMessage message) { - interactionClient.listQueues(); - return "Success"; - } - } -} diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandler.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandler.java deleted file mode 100644 index de096679f..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandler.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs.handlers; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import software.amazon.lambda.powertools.sqs.SqsLargeMessage; - -public class SqsMessageHandler implements RequestHandler { - - @Override - @SqsLargeMessage - public String handleRequest(SQSEvent sqsEvent, Context context) { - return sqsEvent.getRecords().get(0).getBody(); - } -} diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandlerWithNonRetryableHandler.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandlerWithNonRetryableHandler.java deleted file mode 100644 index 74ff02e2c..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandlerWithNonRetryableHandler.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs.handlers; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static software.amazon.lambda.powertools.sqs.internal.SqsMessageBatchProcessorAspectTest.interactionClient; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import software.amazon.lambda.powertools.sqs.SqsBatch; -import software.amazon.lambda.powertools.sqs.SqsMessageHandler; - -public class SqsMessageHandlerWithNonRetryableHandler implements RequestHandler { - - @Override - @SqsBatch(value = InnerMessageHandler.class, nonRetryableExceptions = {IllegalStateException.class, - IllegalArgumentException.class}) - public String handleRequest(final SQSEvent sqsEvent, - final Context context) { - return "Success"; - } - - private class InnerMessageHandler implements SqsMessageHandler { - - @Override - public String process(SQSMessage message) { - if (message.getMessageId().isEmpty()) { - throw new IllegalArgumentException("Invalid message and was moved to DLQ"); - } - - if ("2e1424d4-f796-459a-9696-9c92662ba5da".equals(message.getMessageId())) { - throw new RuntimeException("Invalid message and should be reprocessed"); - } - - interactionClient.listQueues(); - return "Success"; - } - } -} diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandlerWithNonRetryableHandlerWithDelete.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandlerWithNonRetryableHandlerWithDelete.java deleted file mode 100644 index 5b341880e..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandlerWithNonRetryableHandlerWithDelete.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs.handlers; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static software.amazon.lambda.powertools.sqs.internal.SqsMessageBatchProcessorAspectTest.interactionClient; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import software.amazon.lambda.powertools.sqs.SqsBatch; -import software.amazon.lambda.powertools.sqs.SqsMessageHandler; - -public class SqsMessageHandlerWithNonRetryableHandlerWithDelete implements RequestHandler { - - @Override - @SqsBatch(value = InnerMessageHandler.class, - nonRetryableExceptions = {IllegalStateException.class, IllegalArgumentException.class}, - deleteNonRetryableMessageFromQueue = true) - public String handleRequest(final SQSEvent sqsEvent, - final Context context) { - return "Success"; - } - - private class InnerMessageHandler implements SqsMessageHandler { - - @Override - public String process(SQSMessage message) { - if (message.getMessageId().isEmpty()) { - throw new IllegalArgumentException("Invalid message and was moved to DLQ"); - } - - if ("2e1424d4-f796-459a-9696-9c92662ba5da".equals(message.getMessageId())) { - throw new RuntimeException("Invalid message and should be reprocessed"); - } - - interactionClient.listQueues(); - return "Success"; - } - } -} diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsNoDeleteMessageHandler.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsNoDeleteMessageHandler.java deleted file mode 100644 index e96dc5581..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsNoDeleteMessageHandler.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs.handlers; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import software.amazon.lambda.powertools.sqs.SqsLargeMessage; - -public class SqsNoDeleteMessageHandler implements RequestHandler { - - @Override - @SqsLargeMessage(deletePayloads = false) - public String handleRequest(SQSEvent sqsEvent, Context context) { - return sqsEvent.getRecords().get(0).getBody(); - } -} \ No newline at end of file diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/internal/SqsLargeMessageAspectTest.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/internal/SqsLargeMessageAspectTest.java deleted file mode 100644 index 535da11c7..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/internal/SqsLargeMessageAspectTest.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs.internal; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; -import static software.amazon.lambda.powertools.sqs.internal.SqsLargeMessageAspect.FailedProcessingLargePayloadException; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.function.Consumer; -import java.util.stream.Stream; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import software.amazon.awssdk.core.ResponseInputStream; -import software.amazon.awssdk.core.exception.SdkClientException; -import software.amazon.awssdk.http.AbortableInputStream; -import software.amazon.awssdk.services.s3.S3Client; -import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; -import software.amazon.awssdk.services.s3.model.GetObjectRequest; -import software.amazon.awssdk.services.s3.model.GetObjectResponse; -import software.amazon.awssdk.services.s3.model.S3Exception; -import software.amazon.awssdk.utils.StringInputStream; -import software.amazon.lambda.powertools.sqs.SqsUtils; -import software.amazon.lambda.powertools.sqs.handlers.LambdaHandlerApiGateway; -import software.amazon.lambda.powertools.sqs.handlers.SqsMessageHandler; -import software.amazon.lambda.powertools.sqs.handlers.SqsNoDeleteMessageHandler; - -public class SqsLargeMessageAspectTest { - - private static final String BUCKET_NAME = "bucketname"; - private static final String BUCKET_KEY = "c71eb2ae-37e0-4265-8909-32f4153faddf"; - private RequestHandler requestHandler; - @Mock - private Context context; - @Mock - private S3Client s3Client; - - private static Stream exception() { - return Stream.of(Arguments.of(S3Exception.builder() - .message("Service Exception") - .build()), - Arguments.of(SdkClientException.builder() - .message("Client Exception") - .build())); - } - - @BeforeEach - void setUp() { - openMocks(this); - setupContext(); - SqsUtils.overrideS3Client(s3Client); - requestHandler = new SqsMessageHandler(); - } - - @Test - public void testLargeMessage() { - when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); - SQSEvent sqsEvent = messageWithBody( - "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + - "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"); - - String response = requestHandler.handleRequest(sqsEvent, context); - - assertThat(response) - .isEqualTo("A big message"); - - ArgumentCaptor delete = ArgumentCaptor.forClass(DeleteObjectRequest.class); - - verify(s3Client).deleteObject(delete.capture()); - - Assertions.assertThat(delete.getValue()) - .satisfies((Consumer) deleteObjectRequest -> - { - assertThat(deleteObjectRequest.bucket()) - .isEqualTo(BUCKET_NAME); - - assertThat(deleteObjectRequest.key()) - .isEqualTo(BUCKET_KEY); - }); - } - - @Test - public void shouldNotProcessSmallMessageBody() { - when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); - - SQSEvent sqsEvent = messageWithBody("This is small message"); - - String response = requestHandler.handleRequest(sqsEvent, context); - - assertThat(response) - .isEqualTo("This is small message"); - - verifyNoInteractions(s3Client); - } - - @ParameterizedTest - @MethodSource("exception") - public void shouldFailEntireBatchIfFailedDownloadingFromS3(RuntimeException exception) { - when(s3Client.getObject(any(GetObjectRequest.class))).thenThrow(exception); - - String messageBody = - "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + - "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"; - SQSEvent sqsEvent = messageWithBody(messageBody); - - assertThatExceptionOfType(FailedProcessingLargePayloadException.class) - .isThrownBy(() -> requestHandler.handleRequest(sqsEvent, context)) - .withCause(exception); - - verify(s3Client, never()).deleteObject(any(DeleteObjectRequest.class)); - } - - @Test - public void testLargeMessageWithDeletionOff() { - requestHandler = new SqsNoDeleteMessageHandler(); - - when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); - SQSEvent sqsEvent = messageWithBody( - "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + - "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"); - - String response = requestHandler.handleRequest(sqsEvent, context); - - assertThat(response).isEqualTo("A big message"); - - verify(s3Client, never()).deleteObject(any(DeleteObjectRequest.class)); - } - - @Test - public void shouldFailEntireBatchIfFailedProcessingDownloadMessageFromS3() { - ResponseInputStream s3Response = - new ResponseInputStream<>(GetObjectResponse.builder().build(), - AbortableInputStream.create(new StringInputStream("test") { - @Override - public void close() throws IOException { - throw new IOException("Failed"); - } - })); - - when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3Response); - - String messageBody = - "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + - "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"; - SQSEvent sqsEvent = messageWithBody(messageBody); - - assertThatExceptionOfType(FailedProcessingLargePayloadException.class) - .isThrownBy(() -> requestHandler.handleRequest(sqsEvent, context)) - .withCauseInstanceOf(IOException.class); - - verify(s3Client, never()).deleteObject(any(DeleteObjectRequest.class)); - } - - @Test - public void shouldNotDoAnyProcessingWhenNotSqsEvent() { - LambdaHandlerApiGateway handler = new LambdaHandlerApiGateway(); - - String messageBody = - "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + - "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"; - - APIGatewayProxyRequestEvent event = new APIGatewayProxyRequestEvent(); - event.setBody(messageBody); - String response = handler.handleRequest(event, context); - - assertThat(response) - .isEqualTo(messageBody); - - verifyNoInteractions(s3Client); - } - - private ResponseInputStream s3ObjectWithLargeMessage() { - return new ResponseInputStream<>(GetObjectResponse.builder().build(), - AbortableInputStream.create(new ByteArrayInputStream("A big message".getBytes()))); - } - - private SQSEvent messageWithBody(String messageBody) { - SQSMessage sqsMessage = new SQSMessage(); - sqsMessage.setBody(messageBody); - SQSEvent sqsEvent = new SQSEvent(); - sqsEvent.setRecords(singletonList(sqsMessage)); - return sqsEvent; - } - - private void setupContext() { - when(context.getFunctionName()).thenReturn("testFunction"); - when(context.getInvokedFunctionArn()).thenReturn("testArn"); - when(context.getFunctionVersion()).thenReturn("1"); - when(context.getMemoryLimitInMB()).thenReturn(10); - } -} \ No newline at end of file diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/internal/SqsMessageBatchProcessorAspectTest.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/internal/SqsMessageBatchProcessorAspectTest.java deleted file mode 100644 index c0211cb83..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/internal/SqsMessageBatchProcessorAspectTest.java +++ /dev/null @@ -1,331 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.sqs.internal; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.when; -import static software.amazon.lambda.powertools.sqs.SqsUtils.overrideSqsClient; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.IOException; -import java.util.HashMap; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; -import software.amazon.awssdk.services.sqs.SqsClient; -import software.amazon.awssdk.services.sqs.model.BatchResultErrorEntry; -import software.amazon.awssdk.services.sqs.model.DeleteMessageBatchRequest; -import software.amazon.awssdk.services.sqs.model.GetQueueAttributesRequest; -import software.amazon.awssdk.services.sqs.model.GetQueueAttributesResponse; -import software.amazon.awssdk.services.sqs.model.QueueAttributeName; -import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequest; -import software.amazon.awssdk.services.sqs.model.SendMessageBatchResponse; -import software.amazon.lambda.powertools.sqs.SQSBatchProcessingException; -import software.amazon.lambda.powertools.sqs.handlers.LambdaHandlerApiGateway; -import software.amazon.lambda.powertools.sqs.handlers.PartialBatchFailureSuppressedHandler; -import software.amazon.lambda.powertools.sqs.handlers.PartialBatchPartialFailureHandler; -import software.amazon.lambda.powertools.sqs.handlers.PartialBatchSuccessHandler; -import software.amazon.lambda.powertools.sqs.handlers.SqsMessageHandlerWithNonRetryableHandler; -import software.amazon.lambda.powertools.sqs.handlers.SqsMessageHandlerWithNonRetryableHandlerWithDelete; - -public class SqsMessageBatchProcessorAspectTest { - public static final SqsClient interactionClient = mock(SqsClient.class); - private static final SqsClient sqsClient = mock(SqsClient.class); - private static final ObjectMapper MAPPER = new ObjectMapper(); - private final Context context = mock(Context.class); - private SQSEvent event; - private RequestHandler requestHandler; - - @BeforeEach - void setUp() throws IOException { - overrideSqsClient(sqsClient); - reset(interactionClient); - reset(sqsClient); - setupContext(); - event = MAPPER.readValue(this.getClass().getResource("/sampleSqsBatchEvent.json"), SQSEvent.class); - requestHandler = new PartialBatchSuccessHandler(); - } - - @Test - void shouldBatchProcessAllMessageSuccessfullyAndNotDeleteFromSQS() { - requestHandler.handleRequest(event, context); - - verify(interactionClient, times(2)).listQueues(); - verify(sqsClient, times(0)).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); - } - - @Test - void shouldBatchProcessMessageWithSuccessDeletedOnFailureInBatchFromSQS() { - requestHandler = new PartialBatchPartialFailureHandler(); - - assertThatExceptionOfType(SQSBatchProcessingException.class) - .isThrownBy(() -> requestHandler.handleRequest(event, context)) - .satisfies(e -> - { - assertThat(e.getExceptions()) - .hasSize(1) - .extracting("message") - .containsExactly("2e1424d4-f796-459a-8184-9c92662be6da"); - - assertThat(e.getFailures()) - .hasSize(1) - .extracting("messageId") - .containsExactly("2e1424d4-f796-459a-8184-9c92662be6da"); - - assertThat(e.successMessageReturnValues()) - .hasSize(1) - .contains("Success"); - }); - - verify(interactionClient).listQueues(); - verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); - } - - @Test - void shouldBatchProcessMessageWithSuccessDeletedOnFailureWithSuppressionInBatchFromSQS() { - requestHandler = new PartialBatchFailureSuppressedHandler(); - - requestHandler.handleRequest(event, context); - - verify(interactionClient).listQueues(); - verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); - } - - @Test - void shouldNotTakeEffectOnNonSqsEventHandler() { - LambdaHandlerApiGateway handlerApiGateway = new LambdaHandlerApiGateway(); - - handlerApiGateway.handleRequest(mock(APIGatewayProxyRequestEvent.class), context); - - verifyNoInteractions(sqsClient); - } - - @Test - void shouldBatchProcessAndMoveNonRetryableExceptionToDlq() { - requestHandler = new SqsMessageHandlerWithNonRetryableHandler(); - event.getRecords().get(0).setMessageId(""); - - HashMap attributes = new HashMap<>(); - - attributes.put(QueueAttributeName.REDRIVE_POLICY, "{\n" + - " \"deadLetterTargetArn\": \"arn:aws:sqs:us-east-2:123456789012:retry-queue\",\n" + - " \"maxReceiveCount\": 2\n" + - "}"); - - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn( - GetQueueAttributesResponse.builder() - .attributes(attributes) - .build()); - - requestHandler.handleRequest(event, context); - - verify(interactionClient).listQueues(); - verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); - verify(sqsClient).sendMessageBatch(any(SendMessageBatchRequest.class)); - } - - @Test - void shouldBatchProcessAndThrowExceptionForNonRetryableExceptionWhenMoveToDlqReturnFailedResponse() { - requestHandler = new SqsMessageHandlerWithNonRetryableHandler(); - event.getRecords().get(0).setMessageId(""); - - when(sqsClient.sendMessageBatch(any(SendMessageBatchRequest.class))).thenReturn( - SendMessageBatchResponse.builder() - .failed(BatchResultErrorEntry.builder() - .message("Permission Error") - .code("KMS.AccessDeniedException") - .senderFault(true) - .build()) - .build()); - - HashMap attributes = new HashMap<>(); - - attributes.put(QueueAttributeName.REDRIVE_POLICY, "{\n" + - " \"deadLetterTargetArn\": \"arn:aws:sqs:us-east-2:123456789012:retry-queue\",\n" + - " \"maxReceiveCount\": 2\n" + - "}"); - - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn( - GetQueueAttributesResponse.builder() - .attributes(attributes) - .build()); - - Assertions.assertThatExceptionOfType(SQSBatchProcessingException.class). - isThrownBy(() -> requestHandler.handleRequest(event, context)); - - verify(interactionClient).listQueues(); - verify(sqsClient).sendMessageBatch(any(SendMessageBatchRequest.class)); - verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); - } - - @Test - void shouldBatchProcessAndDeleteNonRetryableExceptionMessage() { - requestHandler = new SqsMessageHandlerWithNonRetryableHandlerWithDelete(); - event.getRecords().get(0).setMessageId(""); - - requestHandler.handleRequest(event, context); - - verify(interactionClient).listQueues(); - ArgumentCaptor captor = ArgumentCaptor.forClass(DeleteMessageBatchRequest.class); - verify(sqsClient).deleteMessageBatch(captor.capture()); - verify(sqsClient, never()).sendMessageBatch(any(SendMessageBatchRequest.class)); - verify(sqsClient, never()).getQueueAttributes(any(GetQueueAttributesRequest.class)); - - assertThat(captor.getValue()) - .satisfies(deleteMessageBatchRequest -> assertThat(deleteMessageBatchRequest.entries()) - .hasSize(2) - .extracting("id") - .contains("", "2e1424d4-f796-459a-8184-9c92662be6da")); - } - - @Test - void shouldBatchProcessAndFailWithExceptionForNonRetryableExceptionAndNoDlq() { - requestHandler = new SqsMessageHandlerWithNonRetryableHandler(); - - event.getRecords().get(0).setMessageId(""); - event.getRecords() - .forEach(sqsMessage -> sqsMessage.setEventSourceArn(sqsMessage.getEventSourceArn() + "-temp")); - - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn( - GetQueueAttributesResponse.builder() - .build()); - - assertThatExceptionOfType(SQSBatchProcessingException.class) - .isThrownBy(() -> requestHandler.handleRequest(event, context)) - .satisfies(e -> - { - assertThat(e.getExceptions()) - .hasSize(1) - .extracting("message") - .containsExactly("Invalid message and was moved to DLQ"); - - assertThat(e.getFailures()) - .hasSize(1) - .extracting("messageId") - .containsExactly(""); - - assertThat(e.successMessageReturnValues()) - .hasSize(1) - .contains("Success"); - }); - - verify(interactionClient).listQueues(); - verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); - verify(sqsClient, never()).sendMessageBatch(any(SendMessageBatchRequest.class)); - } - - @Test - void shouldBatchProcessAndFailWithExceptionForNonRetryableExceptionWhenFailedParsingPolicy() { - requestHandler = new SqsMessageHandlerWithNonRetryableHandler(); - event.getRecords().get(0).setMessageId(""); - event.getRecords() - .forEach(sqsMessage -> sqsMessage.setEventSourceArn(sqsMessage.getEventSourceArn() + "-temp-queue")); - - HashMap attributes = new HashMap<>(); - - attributes.put(QueueAttributeName.REDRIVE_POLICY, "MalFormedRedrivePolicy"); - - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn( - GetQueueAttributesResponse.builder() - .attributes(attributes) - .build()); - - assertThatExceptionOfType(SQSBatchProcessingException.class) - .isThrownBy(() -> requestHandler.handleRequest(event, context)) - .satisfies(e -> - { - assertThat(e.getExceptions()) - .hasSize(1) - .extracting("message") - .containsExactly("Invalid message and was moved to DLQ"); - - assertThat(e.getFailures()) - .hasSize(1) - .extracting("messageId") - .containsExactly(""); - - assertThat(e.successMessageReturnValues()) - .hasSize(1) - .contains("Success"); - }); - - verify(interactionClient).listQueues(); - verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); - verify(sqsClient, never()).sendMessageBatch(any(SendMessageBatchRequest.class)); - } - - @Test - void shouldBatchProcessAndMoveNonRetryableExceptionToDlqAndThrowException() throws IOException { - requestHandler = new SqsMessageHandlerWithNonRetryableHandler(); - event = MAPPER.readValue(this.getClass().getResource("/threeMessageSqsBatchEvent.json"), SQSEvent.class); - - event.getRecords().get(1).setMessageId(""); - - HashMap attributes = new HashMap<>(); - - attributes.put(QueueAttributeName.REDRIVE_POLICY, "{\n" + - " \"deadLetterTargetArn\": \"arn:aws:sqs:us-east-2:123456789012:retry-queue\",\n" + - " \"maxReceiveCount\": 2\n" + - "}"); - - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn( - GetQueueAttributesResponse.builder() - .attributes(attributes) - .build()); - - assertThatExceptionOfType(SQSBatchProcessingException.class) - .isThrownBy(() -> requestHandler.handleRequest(event, context)) - .satisfies(e -> - { - assertThat(e.getExceptions()) - .hasSize(1) - .extracting("message") - .containsExactly("Invalid message and should be reprocessed"); - - assertThat(e.getFailures()) - .hasSize(1) - .extracting("messageId") - .containsExactly("2e1424d4-f796-459a-9696-9c92662ba5da"); - - assertThat(e.successMessageReturnValues()) - .hasSize(1) - .contains("Success"); - }); - - verify(interactionClient).listQueues(); - verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); - verify(sqsClient).sendMessageBatch(any(SendMessageBatchRequest.class)); - } - - private void setupContext() { - when(context.getFunctionName()).thenReturn("testFunction"); - when(context.getInvokedFunctionArn()).thenReturn("testArn"); - when(context.getFunctionVersion()).thenReturn("1"); - when(context.getMemoryLimitInMB()).thenReturn(10); - } -} \ No newline at end of file diff --git a/powertools-sqs/src/test/resources/SqsFifoBatchEvent.json b/powertools-sqs/src/test/resources/SqsFifoBatchEvent.json deleted file mode 100644 index 726e45ea1..000000000 --- a/powertools-sqs/src/test/resources/SqsFifoBatchEvent.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "Records": [ - { - "messageId": "059f36b4-87a3-44ab-83d2-661975830a7d", - "receiptHandle": "AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082649183", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082649185", - "MessageGroupId": "Group1" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceARN": "arn:aws:sqs:us-east-1:906126917321:sqs-lambda-MySqsQueue-4DYWWJIE97N5", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6da", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649", - "MessageGroupId": "Group2" - }, - "messageAttributes": { - "Attribute3" : { - "binaryValue" : "MTEwMA==", - "dataType" : "Binary" - }, - "Attribute2" : { - "stringValue" : "123", - "dataType" : "Number" - }, - "Attribute1" : { - "stringValue" : "AttributeValue1", - "dataType" : "String" - } - }, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceARN": "arn:aws:sqs:us-east-1:906126917321:sqs-lambda-MySqsQueue-4DYWWJIE97N5", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-9696-9c92662ba5da", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649", - "MessageGroupId": "Group1" - }, - "messageAttributes": { - "Attribute2" : { - "stringValue" : "123", - "dataType" : "Number" - } - }, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceARN": "arn:aws:sqs:us-east-1:906126917321:sqs-lambda-MySqsQueue-4DYWWJIE97N5", - "awsRegion": "us-east-2" - } - ] -} \ No newline at end of file diff --git a/powertools-sqs/src/test/resources/sampleSqsBatchEvent.json b/powertools-sqs/src/test/resources/sampleSqsBatchEvent.json deleted file mode 100644 index 8a5fbf309..000000000 --- a/powertools-sqs/src/test/resources/sampleSqsBatchEvent.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "records": [ - { - "messageId": "059f36b4-87a3-44ab-83d2-661975830a7d", - "receiptHandle": "AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082649183", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082649185" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6da", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - } - ] -} \ No newline at end of file diff --git a/powertools-sqs/src/test/resources/sampleSqsBatchEventBatchSize25.json b/powertools-sqs/src/test/resources/sampleSqsBatchEventBatchSize25.json deleted file mode 100644 index 31b8862ad..000000000 --- a/powertools-sqs/src/test/resources/sampleSqsBatchEventBatchSize25.json +++ /dev/null @@ -1,404 +0,0 @@ -{ - "records": [ - { - "messageId": "059f36b4-87a3-44ab-83d2-661975830a7d", - "receiptHandle": "AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082649183", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082649185" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6da", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d3", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d4", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b5", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d6", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b7", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d8", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d9", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d10", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d11", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d12", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d13", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d14", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d15", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d16", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d17", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d18", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d19", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d20", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d21", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d22", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d23", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d24", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d25", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d26", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6d27", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue", - "awsRegion": "us-east-2" - } - ] -} \ No newline at end of file diff --git a/powertools-sqs/src/test/resources/threeMessageSqsBatchEvent.json b/powertools-sqs/src/test/resources/threeMessageSqsBatchEvent.json deleted file mode 100644 index b3b61da3b..000000000 --- a/powertools-sqs/src/test/resources/threeMessageSqsBatchEvent.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "records": [ - { - "messageId": "059f36b4-87a3-44ab-83d2-661975830a7d", - "receiptHandle": "AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082649183", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082649185" - }, - "messageAttributes": {}, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-1:906126917321:sqs-lambda-MySqsQueue-4DYWWJIE97N5", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-8184-9c92662be6da", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": { - "Attribute3" : { - "binaryValue" : "MTEwMA==", - "dataType" : "Binary" - }, - "Attribute2" : { - "stringValue" : "123", - "dataType" : "Number" - }, - "Attribute1" : { - "stringValue" : "AttributeValue1", - "dataType" : "String" - } - }, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-1:906126917321:sqs-lambda-MySqsQueue-4DYWWJIE97N5", - "awsRegion": "us-east-2" - }, - { - "messageId": "2e1424d4-f796-459a-9696-9c92662ba5da", - "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...", - "body": "Test message.", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1545082650636", - "SenderId": "AIDAIENQZJOLO23YVJ4VO", - "ApproximateFirstReceiveTimestamp": "1545082650649" - }, - "messageAttributes": { - "Attribute2" : { - "stringValue" : "123", - "dataType" : "Number" - } - }, - "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", - "eventSource": "aws:sqs", - "eventSourceArn": "arn:aws:sqs:us-east-1:906126917321:sqs-lambda-MySqsQueue-4DYWWJIE97N5", - "awsRegion": "us-east-2" - } - ] -} \ No newline at end of file diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml deleted file mode 100644 index 4a79d2987..000000000 --- a/powertools-test-suite/pom.xml +++ /dev/null @@ -1,164 +0,0 @@ - - - - - 4.0.0 - - powertools-test-suite - jar - - - powertools-parent - software.amazon.lambda - 1.17.0-SNAPSHOT - - - Powertools for AWS Lambda (Java) library Test Suite - - A suite of tests for interactions between the various Powertools for AWS Lambda (Java) modules. - - https://aws.amazon.com/lambda/ - - GitHub Issues - https://github.com/aws-powertools/powertools-lambda-java/issues - - - https://github.com/aws-powertools/powertools-lambda-java.git - - - - Powertools for AWS Lambda team - Amazon Web Services - https://aws.amazon.com/ - - - - - true - - - - - ossrh - https://aws.oss.sonatype.org/content/repositories/snapshots - - - - - - software.amazon.lambda - powertools-core - - - org.apache.logging.log4j - log4j-jcl - - - com.amazonaws - aws-lambda-java-core - - - com.amazonaws - aws-lambda-java-events - - - software.amazon.lambda - powertools-logging - - - software.amazon.lambda - powertools-tracing - - - software.amazon.lambda - powertools-sqs - - - - - org.junit.jupiter - junit-jupiter-api - test - - - org.junit.jupiter - junit-jupiter-engine - test - - - org.apache.commons - commons-lang3 - test - - - org.mockito - mockito-core - test - - - org.aspectj - aspectjweaver - test - - - org.assertj - assertj-core - test - - - org.skyscreamer - jsonassert - test - - - - - - - dev.aspectj - aspectj-maven-plugin - 1.13.1 - - ${maven.compiler.source} - ${maven.compiler.target} - ${maven.compiler.target} - - - software.amazon.lambda - powertools-logging - - - software.amazon.lambda - powertools-tracing - - - software.amazon.lambda - powertools-sqs - - - - - - - compile - - - - - - - \ No newline at end of file diff --git a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/LoggingOrderTest.java b/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/LoggingOrderTest.java deleted file mode 100644 index 9e9c464a6..000000000 --- a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/LoggingOrderTest.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.testsuite; - - -import static java.util.Collections.emptyMap; -import static java.util.Collections.singletonList; -import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification; -import com.amazonaws.xray.AWSXRay; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.nio.channels.FileChannel; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; -import java.util.Map; -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.ThreadContext; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import software.amazon.awssdk.core.ResponseInputStream; -import software.amazon.awssdk.http.AbortableInputStream; -import software.amazon.awssdk.services.s3.S3Client; -import software.amazon.awssdk.services.s3.model.GetObjectRequest; -import software.amazon.awssdk.services.s3.model.GetObjectResponse; -import software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor; -import software.amazon.lambda.powertools.logging.internal.LambdaLoggingAspect; -import software.amazon.lambda.powertools.sqs.SqsUtils; -import software.amazon.lambda.powertools.testsuite.handler.LoggingOrderMessageHandler; -import software.amazon.lambda.powertools.testsuite.handler.TracingLoggingStreamMessageHandler; - -public class LoggingOrderTest { - - private static final String BUCKET_NAME = "ms-extended-sqs-client"; - private static final String BUCKET_KEY = "c71eb2ae-37e0-4265-8909-32f4153faddf"; - - @Mock - private Context context; - - @Mock - private S3Client s3Client; - - @BeforeEach - void setUp() throws IllegalAccessException, IOException, NoSuchMethodException, InvocationTargetException { - openMocks(this); - SqsUtils.overrideS3Client(s3Client); - ThreadContext.clearAll(); - writeStaticField(LambdaHandlerProcessor.class, "IS_COLD_START", null, true); - setupContext(); - //Make sure file is cleaned up before running full stack logging regression - FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); - resetLogLevel(Level.INFO); - AWSXRay.beginSegment(LoggingOrderTest.class.getName()); - } - - @AfterEach - void tearDown() { - AWSXRay.endSegment(); - } - - /** - * The SQSEvent payload will be altered by the @SqsLargeMessage annotation. Logging of the event should happen - * after the event has been altered - */ - @Test - public void testThatLoggingAnnotationActsLast() throws IOException { - ResponseInputStream s3Response = - new ResponseInputStream<>(GetObjectResponse.builder().build(), - AbortableInputStream.create(new ByteArrayInputStream("A big message".getBytes()))); - - when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3Response); - SQSEvent sqsEvent = messageWithBody( - "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + - "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"); - - LoggingOrderMessageHandler requestHandler = new LoggingOrderMessageHandler(); - requestHandler.handleRequest(sqsEvent, context); - - assertThat(Files.lines(Paths.get("target/logfile.json"))) - .hasSize(2) - .satisfies(line -> - { - Map actual = parseToMap(line.get(0)); - - String message = actual.get("message").toString(); - - assertThat(message) - .contains("A big message"); - }); - } - - @Test - public void testLoggingAnnotationActsAfterTracingForStreamingHandler() throws IOException { - - ByteArrayOutputStream output = new ByteArrayOutputStream(); - S3EventNotification s3EventNotification = s3EventNotification(); - - TracingLoggingStreamMessageHandler handler = new TracingLoggingStreamMessageHandler(); - handler.handleRequest(new ByteArrayInputStream(new ObjectMapper().writeValueAsBytes(s3EventNotification)), - output, context); - - assertThat(new String(output.toByteArray(), StandardCharsets.UTF_8)) - .isNotEmpty(); - } - - private void setupContext() { - when(context.getFunctionName()).thenReturn("testFunction"); - when(context.getInvokedFunctionArn()).thenReturn("testArn"); - when(context.getFunctionVersion()).thenReturn("1"); - when(context.getMemoryLimitInMB()).thenReturn(10); - when(context.getAwsRequestId()).thenReturn("RequestId"); - } - - private void resetLogLevel(Level level) - throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { - Method resetLogLevels = LambdaLoggingAspect.class.getDeclaredMethod("resetLogLevels", Level.class); - resetLogLevels.setAccessible(true); - resetLogLevels.invoke(null, level); - writeStaticField(LambdaLoggingAspect.class, "LEVEL_AT_INITIALISATION", level, true); - } - - private Map parseToMap(String stringAsJson) { - try { - return new ObjectMapper().readValue(stringAsJson, Map.class); - } catch (JsonProcessingException e) { - fail("Failed parsing logger line " + stringAsJson); - return emptyMap(); - } - } - - private S3EventNotification s3EventNotification() { - S3EventNotification.S3EventNotificationRecord record = - new S3EventNotification.S3EventNotificationRecord("us-west-2", - "ObjectCreated:Put", - "aws:s3", - null, - "2.1", - new S3EventNotification.RequestParametersEntity("127.0.0.1"), - new S3EventNotification.ResponseElementsEntity("C3D13FE58DE4C810", - "FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD"), - new S3EventNotification.S3Entity("testConfigRule", - new S3EventNotification.S3BucketEntity("mybucket", - new S3EventNotification.UserIdentityEntity("A3NL1KOZZKExample"), - "arn:aws:s3:::mybucket"), - new S3EventNotification.S3ObjectEntity("HappyFace.jpg", - 1024L, - "d41d8cd98f00b204e9800998ecf8427e", - "096fKKXTRTtl3on89fVO.nfljtsv6qko", - "0055AED6DCD90281E5"), - "1.0"), - new S3EventNotification.UserIdentityEntity("AIDAJDPLRKLG7UEXAMPLE") - ); - - return new S3EventNotification(singletonList(record)); - } - - private SQSEvent messageWithBody(String messageBody) { - SQSEvent.SQSMessage sqsMessage = new SQSEvent.SQSMessage(); - sqsMessage.setBody(messageBody); - SQSEvent sqsEvent = new SQSEvent(); - sqsEvent.setRecords(singletonList(sqsMessage)); - return sqsEvent; - } -} \ No newline at end of file diff --git a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/LoggingOrderMessageHandler.java b/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/LoggingOrderMessageHandler.java deleted file mode 100644 index 5592b1fd3..000000000 --- a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/LoggingOrderMessageHandler.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.testsuite.handler; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.sqs.SqsLargeMessage; - -public class LoggingOrderMessageHandler implements RequestHandler { - - @Override - @SqsLargeMessage - @Logging(logEvent = true) - public String handleRequest(SQSEvent sqsEvent, Context context) { - return sqsEvent.getRecords().get(0).getBody(); - } -} diff --git a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/TracingLoggingStreamMessageHandler.java b/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/TracingLoggingStreamMessageHandler.java deleted file mode 100644 index 4a60d0949..000000000 --- a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/TracingLoggingStreamMessageHandler.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package software.amazon.lambda.powertools.testsuite.handler; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestStreamHandler; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Map; -import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.tracing.Tracing; - -public class TracingLoggingStreamMessageHandler implements RequestStreamHandler { - - @Logging(logEvent = true) - @Tracing - @Override - public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { - ObjectMapper mapper = new ObjectMapper(); - mapper.writeValue(output, mapper.readValue(input, Map.class)); - } -} diff --git a/powertools-test-suite/src/test/resources/log4j2.xml b/powertools-test-suite/src/test/resources/log4j2.xml deleted file mode 100644 index 8ac9b34ce..000000000 --- a/powertools-test-suite/src/test/resources/log4j2.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file