diff --git a/.autover/changes/01370ae5-3537-4723-80b3-ee82e79e80b9.json b/.autover/changes/01370ae5-3537-4723-80b3-ee82e79e80b9.json
new file mode 100644
index 00000000..db6529c7
--- /dev/null
+++ b/.autover/changes/01370ae5-3537-4723-80b3-ee82e79e80b9.json
@@ -0,0 +1,11 @@
+{
+ "Projects": [
+ {
+ "Name": "AWS.Messaging",
+ "Type": "Patch",
+ "ChangelogMessages": [
+ "Update error message for handling scenario where subscriber mapping is not valid."
+ ]
+ }
+ ]
+ }
\ No newline at end of file
diff --git a/.github/workflows/doc-site.yml b/.github/workflows/doc-site.yml
index f655b124..2ba1c4e6 100644
--- a/.github/workflows/doc-site.yml
+++ b/.github/workflows/doc-site.yml
@@ -17,7 +17,7 @@ permissions:
concurrency:
group: "pages"
cancel-in-progress: false
-
+
jobs:
publish-docs:
environment:
@@ -25,23 +25,25 @@ jobs:
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- - name: Checkout
- uses: actions/checkout@v3
- - name: Dotnet Setup
- uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # pinning V4
- with:
- dotnet-version: 8.x
-
- - run: dotnet tool update -g docfx --version 2.74.0
- - run: docfx docs/docfx.json
-
- - name: Setup Pages
- uses: actions/configure-pages@v3
- - name: Upload artifact
- uses: actions/upload-pages-artifact@v2
- with:
- # Upload entire repository
- path: 'docs/_site'
- - name: Deploy to GitHub Pages
- id: deployment
- uses: actions/deploy-pages@v2
+ - name: Checkout
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pinning V4
+ - name: Dotnet Setup
+ uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # pinning V4
+ with:
+ dotnet-version: 8.x
+
+ - run: dotnet tool update -g docfx --version 2.78.3
+ - run: docfx docs/docfx.json
+
+ - name: Setup Pages
+ uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # pinning V5
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # pinning V3
+ with:
+ # Upload entire repository
+ path: "docs/_site"
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # pinning V4
+
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4505e70c..1a885918 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+## Release 2025-03-12
+
+### AWS.Messaging (0.9.5)
+* Update error message for handling scenario where subscriber mapping is not valid.
+
## Release 2025-02-20
### AWS.Messaging (0.9.4)
diff --git a/sampleapps/LambdaMessaging/HandlerSampleRequest.json b/sampleapps/LambdaMessaging/HandlerSampleRequest.json
new file mode 100644
index 00000000..91602565
--- /dev/null
+++ b/sampleapps/LambdaMessaging/HandlerSampleRequest.json
@@ -0,0 +1,20 @@
+{
+ "Records": [
+ {
+ "messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb78",
+ "receiptHandle": "MessageReceiptHandle",
+ "body": "{\"id\":\"d9b4bfc7-9398-44aa-8049-85c07490fb35\",\"source\":\"/AWSLambda/FunctionName\",\"specversion\":\"1.0\",\"type\":\"chatMessage\",\"time\":\"2024-03-22T21:01:03.5484607+00:00\",\"data\":\"{\\u0022MessageDescription\\u0022:\\u0022Testing!!!\\u0022}\"}",
+ "attributes": {
+ "ApproximateReceiveCount": "1",
+ "SentTimestamp": "1523232000000",
+ "SenderId": "123456789012",
+ "ApproximateFirstReceiveTimestamp": "1523232000001"
+ },
+ "messageAttributes": {},
+ "md5OfBody": "7b270e59b47ff90a553787216d55d91d",
+ "eventSource": "aws:sqs",
+ "eventSourceARN": "arn:{partition}:sqs:{region}:123456789012:MyQueue",
+ "awsRegion": "{region}"
+ }
+ ]
+}
diff --git a/sampleapps/LambdaMessaging/LambdaMessaging.csproj b/sampleapps/LambdaMessaging/LambdaMessaging.csproj
index ebe1be70..e735d8ab 100644
--- a/sampleapps/LambdaMessaging/LambdaMessaging.csproj
+++ b/sampleapps/LambdaMessaging/LambdaMessaging.csproj
@@ -5,18 +5,15 @@
enable
true
Lambda
-
true
true
-
-
-
-
-
+
+
+
diff --git a/sampleapps/LambdaMessaging/Properties/launchSettings.json b/sampleapps/LambdaMessaging/Properties/launchSettings.json
index c1fabf8f..3e52fac7 100644
--- a/sampleapps/LambdaMessaging/Properties/launchSettings.json
+++ b/sampleapps/LambdaMessaging/Properties/launchSettings.json
@@ -1,10 +1,13 @@
{
"profiles": {
- "Mock Lambda Test Tool": {
- "commandName": "Executable",
- "commandLineArgs": "--port 5050",
+ "Default": {
"workingDirectory": ".\\bin\\$(Configuration)\\net8.0",
- "executablePath": "%USERPROFILE%\\.dotnet\\tools\\dotnet-lambda-test-tool-8.0.exe"
+ "commandName": "Executable",
+ "commandLineArgs": "exec --depsfile ./LambdaMessaging.deps.json --runtimeconfig ./LambdaMessaging.runtimeconfig.json %USERPROFILE%/.dotnet/tools/.store/amazon.lambda.testtool/${VERSION}/amazon.lambda.testtool/${VERSION}/content/Amazon.Lambda.RuntimeSupport/net8.0/Amazon.Lambda.RuntimeSupport.dll LambdaMessaging::LambdaMessaging.Function_FunctionHandler_Generated::FunctionHandler",
+ "executablePath": "dotnet",
+ "environmentVariables": {
+ "AWS_LAMBDA_RUNTIME_API": "localhost:5050/MyFunction"
+ }
}
}
-}
\ No newline at end of file
+}
diff --git a/sampleapps/LambdaMessaging/README.md b/sampleapps/LambdaMessaging/README.md
new file mode 100644
index 00000000..e6fe17aa
--- /dev/null
+++ b/sampleapps/LambdaMessaging/README.md
@@ -0,0 +1,79 @@
+# Lambda Messaging Sample Application
+
+This sample application demonstrates how to use AWS Lambda with the AWS Message Processing Framework for .NET to process messages from SQS queues.
+
+## Overview
+
+This sample shows how to:
+- Configure a Lambda function to process messages from SQS
+- Use dependency injection with Lambda Annotations
+- Handle message batch processing
+- Implement partial batch failure responses
+- Set up message handlers for specific message types
+
+## Prerequisites
+
+- .NET 8.0 or later
+
+
+## Project Structure
+
+```
+LambdaMessaging/
+├── Function.cs # Lambda function handler
+├── Startup.cs # DI and service configuration
+├── ChatMessage.cs # Message type definition
+├── ChatMessageHandler.cs # Message handler implementation
+├── LambdaMessaging.csproj # Project file
+```
+
+
+## Getting Started
+
+In order to test the lambda function locally with the messaging processing framework, it requires installing the Lambda Test Tool https://github.com/aws/aws-lambda-dotnet/blob/master/Tools/LambdaTestTool-v2 first.
+
+1. Build the project
+
+```
+dotnet build
+```
+
+2Install the AWS Lambda Test Tool:
+```bash
+dotnet tool install -g amazon.lambda.testtool
+```
+
+3. Start the Lambda Test Tool:
+
+```bash
+dotnet lambda-test-tool start --lambda-emulator-port 5050
+```
+
+4. Get the test tool version:
+
+```
+dotnet lambda-test-tool info
+```
+
+5. Run the `LambdaMessaging` project.
+
+There are 2 ways to run it
+1. Visual studio (easiest way).
+ 1a. Update `Properties/launchSettings.json` and replace `${VERSIOMN} with the actual test tool version.
+2. Via command line
+
+```
+cd bin\Debug\net8.0
+$env:AWS_LAMBDA_RUNTIME_API = "localhost:5050/MyFunction"
+$env:VERSION = "0.9.1" // Use the version returned from dotnet lambda-test-tool info
+
+dotnet exec --depsfile ./LambdaMessaging.deps.json --runtimeconfig ./LambdaMessaging.runtimeconfig.json "$env:USERPROFILE\.dotnet\tools\.store\amazon.lambda.testtool\$env:VERSION\amazon.lambda.testtool\$env:VERSION\content\Amazon.Lambda.RuntimeSupport\net8.0\Amazon.Lambda.RuntimeSupport.dll" LambdaMessaging::LambdaMessaging.Function_FunctionHandler_Generated::FunctionHandler
+
+
+```
+
+6. You should now see the `MyFunction` appear in the test tools function list drop down in the top right corner. Select `MyFunction`.
+
+7. We have provided a `HandlerSampleRequest.json` file to be used to test this function. Copy and paste this json into the test tools input window and then hit the "invoke button".
+
+8. You should see in the console window that the `ChatMessageHandler` successfully processed the message. There should be a log statement saying `Message Description: Testing!!!`.
diff --git a/sampleapps/LambdaMessaging/Startup.cs b/sampleapps/LambdaMessaging/Startup.cs
index 3958e72d..85bb937e 100644
--- a/sampleapps/LambdaMessaging/Startup.cs
+++ b/sampleapps/LambdaMessaging/Startup.cs
@@ -1,5 +1,6 @@
using Amazon.Lambda.Annotations;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace LambdaMessaging;
@@ -7,21 +8,24 @@ namespace LambdaMessaging;
[LambdaStartup]
public class Startup
{
- public void ConfigureServices(IServiceCollection services)
+ public HostApplicationBuilder ConfigureHostBuilder()
{
- services.AddLogging(builder =>
+ var builder = new HostApplicationBuilder();
+ builder.Services.AddLogging(b =>
{
- builder.SetMinimumLevel(LogLevel.Trace);
- builder.AddLambdaLogger();
+ b.SetMinimumLevel(LogLevel.Trace);
+ b.AddLambdaLogger();
});
- services.AddAWSMessageBus(builder =>
+ builder.Services.AddAWSMessageBus(b =>
{
- builder.AddMessageHandler("chatMessage");
+ b.AddMessageHandler("chatMessage");
- builder.AddLambdaMessageProcessor(options =>
+ b.AddLambdaMessageProcessor(options =>
{
options.MaxNumberOfConcurrentMessages = 2;
});
});
+
+ return builder;
}
}
diff --git a/sampleapps/PollyIntegration/PollyIntegration.csproj b/sampleapps/PollyIntegration/PollyIntegration.csproj
index 4a8fd540..7e356cdc 100644
--- a/sampleapps/PollyIntegration/PollyIntegration.csproj
+++ b/sampleapps/PollyIntegration/PollyIntegration.csproj
@@ -18,8 +18,8 @@
-
-
+
+
diff --git a/sampleapps/PollyIntegration/README.md b/sampleapps/PollyIntegration/README.md
new file mode 100644
index 00000000..acfbb96f
--- /dev/null
+++ b/sampleapps/PollyIntegration/README.md
@@ -0,0 +1,149 @@
+# AWS Message Processing Framework with Polly Integration
+
+This sample application demonstrates how to use the AWS Message Processing Framework for .NET with Polly for resilient message processing. It showcases integration between SQS message processing and Polly's retry policies.
+
+## Overview
+
+This sample demonstrates:
+- Integration of AWS Message Processing Framework with Polly for resilient messaging
+- Custom backoff handling for message processing
+- SQS message processing with typed handlers
+- Both code-based and configuration-based setup options
+
+## Prerequisites
+
+- .NET 8.0 or later
+- AWS Account with appropriate permissions
+- Basic understanding of Amazon SQS and AWS Message Processing Framework
+
+## Setup
+
+### 1. Create an SQS Queue
+
+1. Open the AWS Management Console
+2. Navigate to Amazon SQS
+3. Click "Create Queue"
+4. Choose "Standard Queue"
+5. Enter a queue name (e.g., "MPF")
+6. Keep default settings for this demo
+7. Click "Create Queue"
+8. Copy the Queue URL - you'll need this later
+
+### 2. Configure Message Processing
+
+You can choose either configuration approach:
+
+#### Option A: Code-based Configuration
+
+In `Program.cs`, update the queue URL and keep these lines uncommented:
+```csharp
+builder.AddSQSPoller("https://sqs.us-west-2.amazonaws.com/012345678910/MPF"); // Replace with your Queue URL
+builder.AddMessageHandler("chatMessage");
+```
+And keep this line commented:
+```
+// builder.LoadConfigurationFromSettings(context.Configuration);
+```
+
+#### Option B: Configuration-based (appsettings.json)
+1. Comment out the code-based configuration in Program.cs:
+
+```
+// builder.AddSQSPoller("https://sqs.us-west-2.amazonaws.com/012345678910/MPF");
+// builder.AddMessageHandler("chatMessage");
+```
+2. Uncomment the configuration loading:
+
+```
+builder.LoadConfigurationFromSettings(context.Configuration);
+```
+3. Update appsettings.json:
+
+```
+{
+ "AWS.Messaging": {
+ "SQSPollers": [
+ {
+ "QueueUrl": "https://sqs.us-west-2.amazonaws.com/012345678910/MPF" // Replace with your Queue URL
+ }
+ ],
+ "MessageHandlers": [
+ {
+ "HandlerType": "PollyIntegration.MessageHandlers.ChatMessageHandler",
+ "MessageType": "PollyIntegration.Models.ChatMessage",
+ "MessageTypeIdentifier": "chatMessage"
+ }
+ ]
+ }
+}
+
+```
+
+Choose Option A if you:
+
+- Want configuration close to the code
+
+- Need dynamic runtime configuration
+
+- Are prototyping or testing
+
+
+Choose Option B if you:
+
+- Need configuration changes without recompiling
+
+- Want environment-specific settings
+
+- Prefer separation of configuration from code
+
+- Need to manage multiple configurations
+
+
+### 3. Configure AWS Credentials
+
+Ensure you have AWS credentials configured either through:
+
+- AWS CLI
+
+- Environment variables
+
+- AWS credentials file
+
+- IAM role (if running on AWS)
+
+## Project Structure
+```
+PollyIntegration/
+├── MessageHandlers/
+│ └── ChatMessageHandler.cs # Sample message handler
+├── Models/
+│ └── ChatMessage.cs # Message type definition
+├── Program.cs # Application entry point
+├── PollyBackoffHandler.cs # Custom Polly integration
+└── appsettings.json # Application configuration
+```
+
+## Running the Application
+1. Build the project
+```
+dotnet build
+```
+2. Run the application
+```
+dotnet run
+```
+## Testing
+
+### Send a Test Message
+
+You can send a test message to your SQS queue using the AWS Console or AWS CLI:
+
+Using AWS CLI:
+```
+
+$messageBody = "{""""type"""":""""chatMessage"""",""""id"""":""""123"""",""""source"""":""""test"""",""""specversion"""":""""1.0"""",""""time"""":""""2024-01-01T00:00:00Z"""",""""data"""":""""{\\""""messageDescription\\"""":\\""""Test message\\""""}""""}"
+
+aws sqs send-message --queue-url YOUR_QUEUE_URL --message-body $messageBody
+
+```
+Replace YOUR_QUEUE_URL with your actual SQS queue URL.
diff --git a/sampleapps/PublisherAPI/PublisherAPI.csproj b/sampleapps/PublisherAPI/PublisherAPI.csproj
index bb1b0844..91a45c77 100644
--- a/sampleapps/PublisherAPI/PublisherAPI.csproj
+++ b/sampleapps/PublisherAPI/PublisherAPI.csproj
@@ -7,9 +7,9 @@
-
-
-
+
+
+
diff --git a/sampleapps/PublisherAPI/README.md b/sampleapps/PublisherAPI/README.md
new file mode 100644
index 00000000..5cd789ce
--- /dev/null
+++ b/sampleapps/PublisherAPI/README.md
@@ -0,0 +1,209 @@
+# AWS Message Processing Framework Publisher API Sample
+
+This sample application demonstrates how to use the AWS Message Processing Framework for .NET to publish messages to different AWS messaging services (SQS, SNS, and EventBridge).
+
+## Overview
+
+This sample demonstrates:
+- Publishing messages to SQS queues (standard and FIFO)
+- Publishing messages to SNS topics (standard and FIFO)
+- Publishing messages to EventBridge
+- Configuration-based and code-based setup options
+- Handling service-specific message options
+
+## Prerequisites
+
+- .NET 8.0 or later
+- AWS Account with appropriate permissions
+- Basic understanding of AWS messaging services (SQS, SNS, EventBridge)
+
+## Setup
+
+### 1. Create AWS Resources
+
+#### SQS Queues
+1. Create a standard SQS queue named "MPF"
+2. Create a FIFO SQS queue named "MPF.fifo". When creating the queue be sure to enable `Content-based deduplication`.
+3. Note the queue URLs
+
+#### SNS Topics
+1. Create a standard SNS topic named "MPF"
+2. Create a FIFO SNS topic named "MPF.fifo". When creating the topic be sure to enable `Content-based deduplication`.
+3. Note the topic ARNs
+
+#### EventBridge
+1. Note your default event bus ARN or create a custom event bus
+
+### 2. Configure Message Publishing
+
+You can choose either configuration approach:
+
+#### Option A: Code-based Configuration
+
+In `Program.cs`, update the endpoints and keep these lines uncommented:
+```csharp
+bus.AddSQSPublisher("https://sqs.us-west-2.amazonaws.com/012345678910/MPF", "chatMessage");
+bus.AddSNSPublisher("arn:aws:sns:us-west-2:012345678910:MPF", "orderInfo");
+bus.AddEventBridgePublisher("arn:aws:events:us-west-2:012345678910:event-bus/default", "foodItem");
+
+// FIFO endpoints
+bus.AddSQSPublisher("https://sqs.us-west-2.amazonaws.com/012345678910/MPF.fifo", "transactionInfo");
+bus.AddSNSPublisher("arn:aws:sns:us-west-2:012345678910:MPF.fifo", "bidInfo");
+```
+And keep this line commented:
+
+```
+// bus.LoadConfigurationFromSettings(builder.Configuration);
+```
+#### Option B: Configuration-based (appsettings.json)
+
+1. Comment out the code-based configuration in Program.cs
+
+2. Uncomment the configuration loading:
+```
+bus.LoadConfigurationFromSettings(builder.Configuration);
+```
+3. Update appsettings.json:
+
+```
+{
+ "AWS.Messaging": {
+ "SQSPublishers": [
+ {
+ "MessageType": "PublisherAPI.Models.ChatMessage",
+ "QueueUrl": "https://sqs.us-west-2.amazonaws.com/012345678910/MPF",
+ "MessageTypeIdentifier": "chatMessage"
+ },
+ {
+ "MessageType": "PublisherAPI.Models.TransactionInfo",
+ "QueueUrl": "https://sqs.us-west-2.amazonaws.com/012345678910/MPF.fifo",
+ "MessageTypeIdentifier": "transactionInfo"
+ }
+ ],
+ "SNSPublishers": [
+ {
+ "MessageType": "PublisherAPI.Models.OrderInfo",
+ "TopicUrl": "arn:aws:sns:us-west-2:012345678910:MPF",
+ "MessageTypeIdentifier": "orderInfo"
+ },
+ {
+ "MessageType": "PublisherAPI.Models.BidInfo",
+ "TopicUrl": "arn:aws:sns:us-west-2:012345678910:MPF.fifo",
+ "MessageTypeIdentifier": "bidInfo"
+ }
+ ],
+ "EventBridgePublishers": [
+ {
+ "MessageType": "PublisherAPI.Models.FoodItem",
+ "EventBusName": "arn:aws:events:us-west-2:012345678910:event-bus/default",
+ "MessageTypeIdentifier": "foodItem"
+ }
+ ]
+ }
+}
+
+```
+### 3. Configure AWS Credentials
+
+Ensure you have AWS credentials configured either through:
+
+- AWS CLI
+
+- Environment variables
+
+- AWS credentials file
+
+- IAM role (if running on AWS)
+
+## Project Structure
+
+```
+PublisherAPI/
+├── Controllers/
+│ └── PublisherController.cs # API endpoints for publishing
+├── Models/
+│ ├── ChatMessage.cs # Standard queue message
+│ ├── TransactionInfo.cs # FIFO queue message
+│ ├── OrderInfo.cs # Standard topic message
+│ ├── BidInfo.cs # FIFO topic message
+│ └── FoodItem.cs # EventBridge message
+├── Program.cs # Application entry point
+└── appsettings.json # Application configuration
+
+```
+
+## Running the Application
+
+1. Build the project:
+
+
+```bash
+dotnet build
+```
+
+2. Run the application:
+
+
+```bash
+dotnet run
+```
+
+
+## Testing
+The API includes Swagger UI for testing. Access it at:
+
+```
+https://localhost:7204/swagger
+```
+### Example API Requests
+
+#### Send Chat Message (Standard SQS):
+```
+POST /Publisher/chatmessage
+Content-Type: application/json
+
+{
+ "messageDescription": "Hello World!"
+}
+```
+
+#### Send Transaction (FIFO SQS):
+```
+POST /Publisher/transactioninfo
+Content-Type: application/json
+
+{
+ "transactionId": "123"
+}
+```
+#### Send Order (Standard SNS):
+```
+POST /Publisher/order
+Content-Type: application/json
+
+{
+ "orderId": "456",
+ "userId": "user123"
+}
+```
+#### Send Bid (FIFO SNS):
+```
+POST /Publisher/bidinfo
+Content-Type: application/json
+
+{
+ "bidId": "789"
+}
+
+```
+
+#### Send Food Item (EventBridge):
+```
+POST /Publisher/fooditem
+Content-Type: application/json
+
+{
+ "id": 1,
+ "name": "Pizza"
+}
+```
diff --git a/sampleapps/README.md b/sampleapps/README.md
new file mode 100644
index 00000000..7a549a7d
--- /dev/null
+++ b/sampleapps/README.md
@@ -0,0 +1,34 @@
+# AWS Message Processing Framework Sample Applications
+
+This directory contains sample applications demonstrating different aspects and use cases of the AWS Message Processing Framework for .NET.
+
+## Sample Projects
+
+### PublisherAPI
+A REST API demonstrating how to publish messages to various AWS messaging services:
+- Publishing to SQS (standard and FIFO queues)
+- Publishing to SNS (standard and FIFO topics)
+- Publishing to EventBridge
+- Configuration-based and code-based setup options
+
+### SubscriberService
+A service application showing how to process messages from SQS queues:
+- Configurable SQS message polling
+- Message handler implementation
+- Backoff policies
+- Configuration options for concurrent processing
+
+### PollyIntegration
+Demonstrates integration with the Polly resilience library:
+- Custom backoff handling
+- Retry policies
+- Circuit breaker patterns
+- Integration with message processing
+
+### LambdaMessaging
+Shows how to use the framework with AWS Lambda:
+- Lambda function configuration
+- SQS event processing
+- Batch message handling
+- Partial batch failures
+- Dependency injection with Lambda Annotations
diff --git a/sampleapps/SubscriberService/README.md b/sampleapps/SubscriberService/README.md
new file mode 100644
index 00000000..b66ff68c
--- /dev/null
+++ b/sampleapps/SubscriberService/README.md
@@ -0,0 +1,150 @@
+# AWS Message Processing Framework Subscriber Service Sample
+
+This sample application demonstrates how to use the AWS Message Processing Framework for .NET to process messages from SQS queues with configurable polling and message handling.
+
+## Overview
+
+This sample demonstrates:
+- Configuring and using SQS message pollers
+- Implementing typed message handlers
+- Configuration-based and code-based setup options
+- Configurable backoff policies for message processing
+- Proper message handling patterns
+
+## Prerequisites
+
+- .NET 8.0 or later
+- AWS Account with appropriate permissions
+- Basic understanding of Amazon SQS and message processing
+
+## Setup
+
+### 1. Create AWS Resources
+
+#### SQS Queue
+1. Open the AWS Management Console
+2. Navigate to Amazon SQS
+3. Click "Create Queue"
+4. Choose "Standard Queue"
+5. Enter a queue name (e.g., "MPF")
+6. Keep default settings for this demo
+7. Click "Create Queue"
+8. Copy the Queue URL - you'll need this later
+
+### 2. Configure Message Processing
+
+You can choose either configuration approach:
+
+#### Option A: Code-based Configuration
+
+In `Program.cs`, update the queue URL and keep these lines uncommented:
+```csharp
+builder.AddSQSPoller("https://sqs.us-west-2.amazonaws.com/012345678910/MPF");
+builder.AddMessageHandler("chatMessage");
+```
+And keep this line commented:
+
+```
+// builder.LoadConfigurationFromSettings(context.Configuration);
+```
+#### Option B: Configuration-based (appsettings.json)
+
+1. Comment out the code-based configuration in Program.cs
+2. Uncomment the configuration loading:
+```
+builder.LoadConfigurationFromSettings(context.Configuration);
+```
+3. Update appsettings.json:
+```
+{
+ "AWS.Messaging": {
+ "MessageHandlers": [
+ {
+ "HandlerType": "SubscriberService.MessageHandlers.ChatMessageHandler",
+ "MessageType": "SubscriberService.Models.ChatMessage",
+ "MessageTypeIdentifier": "chatMessage"
+ }
+ ],
+ "SQSPollers": [
+ {
+ "QueueUrl": "https://sqs.us-west-2.amazonaws.com/012345678910/MPF",
+ "Options": {
+ "MaxNumberOfConcurrentMessages": 10,
+ "VisibilityTimeout": 20,
+ "WaitTimeSeconds": 20,
+ "VisibilityTimeoutExtensionHeartbeatInterval": 1,
+ "VisibilityTimeoutExtensionThreshold": 5
+ }
+ }
+ ],
+ "BackoffPolicy": "CappedExponential"
+ }
+}
+```
+
+### 3. Configure AWS Credentials
+
+Ensure you have AWS credentials configured either through:
+
+- AWS CLI
+
+- Environment variables
+
+- AWS credentials file
+
+- IAM role (if running on AWS)
+
+
+## Project Structure
+```
+SubscriberService/
+├── MessageHandlers/
+│ └── ChatMessageHandler.cs # Message handler implementation
+├── Models/
+│ └── ChatMessage.cs # Message type definition
+├── Program.cs # Application entry point
+└── appsettings.json # Application configuration
+```
+
+## Running the Application
+
+1. Build the project:
+```
+dotnet build
+```
+2. Run the application:
+```
+dotnet run
+```
+
+## Testing
+
+You can test the service by sending messages to the SQS queue using the AWS Console, AWS CLI, or the companion PublisherAPI project.
+
+### Using AWS CLI:
+```
+$messageBody = "{""""type"""":""""chatMessage"""",""""id"""":""""123"""",""""source"""":""""test"""",""""specversion"""":""""1.0"""",""""time"""":""""2024-01-01T00:00:00Z"""",""""data"""":""""{\\""""messageDescription\\"""":\\""""Test message\\""""}""""}"
+
+aws sqs send-message --queue-url YOUR_QUEUE_URL --message-body $messageBody
+```
+
+## Configuration Options
+```
+{
+ "MaxNumberOfConcurrentMessages": 10, // Maximum number of messages to process concurrently
+ "VisibilityTimeout": 20, // Time (in seconds) that a message is invisible after being received
+ "WaitTimeSeconds": 20, // Long polling duration
+ "VisibilityTimeoutExtensionHeartbeatInterval": 1, // How often to extend visibility timeout
+ "VisibilityTimeoutExtensionThreshold": 5 // When to extend visibility timeout
+}
+```
+### Backoff Policy Options
+```
+{
+ "BackoffPolicy": "CappedExponential", // Available options: Linear, Exponential, CappedExponential
+ "CappedExponentialBackoffOptions": {
+ "CapBackoffTime": 60 // Maximum backoff time in seconds
+ }
+}
+```
+
diff --git a/sampleapps/SubscriberService/SubscriberService.csproj b/sampleapps/SubscriberService/SubscriberService.csproj
index 0c125372..99f06212 100644
--- a/sampleapps/SubscriberService/SubscriberService.csproj
+++ b/sampleapps/SubscriberService/SubscriberService.csproj
@@ -16,12 +16,12 @@
PreserveNewest
-
+
-
-
-
-
+
+
+
+
diff --git a/src/AWS.Messaging.Lambda/README.md b/src/AWS.Messaging.Lambda/README.md
index c5bb8461..757caff7 100644
--- a/src/AWS.Messaging.Lambda/README.md
+++ b/src/AWS.Messaging.Lambda/README.md
@@ -22,9 +22,11 @@ Publishers can also be configured if you expect the Lambda function to publish m
[LambdaStartup]
public class Startup
{
- public void ConfigureServices(IServiceCollection services)
+ public HostApplicationBuilder ConfigureHostBuilder()
{
- services.AddAWSMessageBus(builder =>
+ var hostBuilder = new HostApplicationBuilder();
+
+ hostBuilder.Services.AddAWSMessageBus(builder =>
{
builder.AddMessageHandler();
@@ -33,6 +35,8 @@ public class Startup
options.MaxNumberOfConcurrentMessages = 4;
});
});
+
+ return hostBuilder;
}
}
```
diff --git a/src/AWS.Messaging/Serialization/EnvelopeSerializer.cs b/src/AWS.Messaging/Serialization/EnvelopeSerializer.cs
index 0ebb9d70..cac5e738 100644
--- a/src/AWS.Messaging/Serialization/EnvelopeSerializer.cs
+++ b/src/AWS.Messaging/Serialization/EnvelopeSerializer.cs
@@ -146,8 +146,14 @@ public async ValueTask ConvertToEnvelopeAsync(Message s
var subscriberMapping = _messageConfiguration.GetSubscriberMapping(messageTypeIdentifier);
if (subscriberMapping is null)
{
- _logger.LogError("{MessageConfiguration} does not have a valid subscriber mapping for message ID '{MessageTypeIdentifier}'", nameof(_messageConfiguration), messageTypeIdentifier);
- throw new InvalidDataException($"{nameof(_messageConfiguration)} does not have a valid subscriber mapping for {nameof(messageTypeIdentifier)} '{messageTypeIdentifier}'");
+ var availableMappings = string.Join(", ", _messageConfiguration.SubscriberMappings.Select(m => m.MessageTypeIdentifier));
+ _logger.LogError("'{MessageTypeIdentifier}' is not a valid subscriber mapping. Available mappings: {AvailableMappings}",
+ messageTypeIdentifier,
+ string.IsNullOrEmpty(availableMappings) ? "none" : availableMappings);
+
+ throw new InvalidDataException(
+ $"'{messageTypeIdentifier}' is not a valid subscriber mapping. " +
+ $"Available mappings: {(string.IsNullOrEmpty(availableMappings) ? "none" : availableMappings)}");
}
var messageType = subscriberMapping.MessageType;
diff --git a/test/AWS.Messaging.IntegrationTests/FifoSubscriberTests.cs b/test/AWS.Messaging.IntegrationTests/FifoSubscriberTests.cs
index 0351bec9..cfa4f4fc 100644
--- a/test/AWS.Messaging.IntegrationTests/FifoSubscriberTests.cs
+++ b/test/AWS.Messaging.IntegrationTests/FifoSubscriberTests.cs
@@ -162,7 +162,7 @@ public async Task MessagesWithoutHandlers(int numberOfMessages)
var timeElapsed = DateTime.UtcNow - processStartTime;
var inMemoryLogger = serviceProvider.GetRequiredService();
- var errorMessages = inMemoryLogger.Logs.Where(x => x.Message.Equals("_messageConfiguration does not have a valid subscriber mapping for message ID 'AWS.Messaging.Tests.Common.Models.TransactionInfo'"));
+ var errorMessages = inMemoryLogger.Logs.Where(x => x.Message.StartsWith("'") && x.Message.Contains("is not a valid subscriber mapping"));
Assert.NotEmpty(errorMessages);
Assert.True(errorMessages.Count() >= numberOfMessages);
Assert.True(timeElapsed.TotalSeconds > 29);
diff --git a/test/AWS.Messaging.IntegrationTests/SubscriberTests.cs b/test/AWS.Messaging.IntegrationTests/SubscriberTests.cs
index f62bc670..2982c048 100644
--- a/test/AWS.Messaging.IntegrationTests/SubscriberTests.cs
+++ b/test/AWS.Messaging.IntegrationTests/SubscriberTests.cs
@@ -316,7 +316,7 @@ await publisher.PublishAsync(new ChatMessage
var timeElapsed = DateTime.UtcNow - processStartTime;
var inMemoryLogger = serviceProvider.GetRequiredService();
- var errorMessages = inMemoryLogger.Logs.Where(x => x.Message.Equals("_messageConfiguration does not have a valid subscriber mapping for message ID 'AWS.Messaging.IntegrationTests.Models.ChatMessage'"));
+ var errorMessages = inMemoryLogger.Logs.Where(x => x.Message.StartsWith("'") && x.Message.Contains("is not a valid subscriber mapping"));
Assert.NotEmpty(errorMessages);
Assert.True(errorMessages.Count() >= numberOfMessages);
Assert.True(timeElapsed.TotalSeconds > 59);
diff --git a/test/AWS.Messaging.UnitTests/SerializationTests/EnvelopeSerializerTests.cs b/test/AWS.Messaging.UnitTests/SerializationTests/EnvelopeSerializerTests.cs
index b1462474..bf231999 100644
--- a/test/AWS.Messaging.UnitTests/SerializationTests/EnvelopeSerializerTests.cs
+++ b/test/AWS.Messaging.UnitTests/SerializationTests/EnvelopeSerializerTests.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
@@ -566,6 +567,50 @@ public async Task ConvertToEnvelopeAsync_DataMessageLogging_WithError(bool dataM
Assert.Null(exception.InnerException);
}
}
+
+ [Fact]
+ public async Task ConvertToEnvelope_NullSubscriberMapping_ThrowsException()
+ {
+ // ARRANGE
+ var serviceProvider = _serviceCollection.BuildServiceProvider();
+ var envelopeSerializer = serviceProvider.GetRequiredService();
+ var messageEnvelope = new MessageEnvelope
+ {
+ Id = "66659d05-e4ff-462f-81c4-09e560e66a5c",
+ Source = new Uri("/aws/messaging", UriKind.Relative),
+ Version = "1.0",
+ MessageTypeIdentifier = "unknownMessageType", // Using an unknown message type
+ TimeStamp = _testdate,
+ Message = new AddressInfo
+ {
+ Street = "Prince St",
+ Unit = 123,
+ ZipCode = "00001"
+ }
+ };
+
+ var sqsMessage = new Message
+ {
+ Body = await envelopeSerializer.SerializeAsync(messageEnvelope),
+ ReceiptHandle = "receipt-handle"
+ };
+
+ // ACT & ASSERT
+ var exception = await Assert.ThrowsAsync(
+ async () => await envelopeSerializer.ConvertToEnvelopeAsync(sqsMessage)
+ );
+
+ // Verify the exception message
+ Assert.Equal("Failed to create MessageEnvelope", exception.Message);
+
+ // Verify the inner exception type and message
+ Assert.IsType(exception.InnerException);
+ var innerException = exception.InnerException as InvalidDataException;
+ Assert.Contains("'unknownMessageType' is not a valid subscriber mapping.", innerException.Message);
+ Assert.Contains("Available mappings:", innerException.Message);
+ Assert.Contains("addressInfo", innerException.Message);
+ }
+
}
public class MockSerializationCallback : ISerializationCallback