From bdb0f185e00807ce9b7e6beff47915e2dbc658ba Mon Sep 17 00:00:00 2001 From: Garrett Beatty Date: Fri, 7 Mar 2025 08:31:11 -0600 Subject: [PATCH 1/6] Update error message for when subscribermapping is null (#181) --- .../01370ae5-3537-4723-80b3-ee82e79e80b9.json | 11 +++++ .../Serialization/EnvelopeSerializer.cs | 10 ++++- .../FifoSubscriberTests.cs | 2 +- .../SubscriberTests.cs | 2 +- .../EnvelopeSerializerTests.cs | 45 +++++++++++++++++++ 5 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 .autover/changes/01370ae5-3537-4723-80b3-ee82e79e80b9.json 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..650908a1 --- /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/src/AWS.Messaging/Serialization/EnvelopeSerializer.cs b/src/AWS.Messaging/Serialization/EnvelopeSerializer.cs index ddc304d2..871c4288 100644 --- a/src/AWS.Messaging/Serialization/EnvelopeSerializer.cs +++ b/src/AWS.Messaging/Serialization/EnvelopeSerializer.cs @@ -144,8 +144,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 254d9f6a..7393e139 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; @@ -564,6 +565,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 From 519a1b4dd53f4a4b0faa2804cca4684a00a33b7d Mon Sep 17 00:00:00 2001 From: Phil Asmar Date: Wed, 12 Mar 2025 16:24:57 -0400 Subject: [PATCH 2/6] chore: fix doc site --- .github/workflows/doc-site.yml | 44 ++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 21 deletions(-) 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 + + From 8bab0ed77dd09af39ece597aeacd338a8b96415f Mon Sep 17 00:00:00 2001 From: aws-sdk-dotnet-automation Date: Wed, 12 Mar 2025 20:51:27 +0000 Subject: [PATCH 3/6] release_2025-03-12 --- src/AWS.Messaging/AWS.Messaging.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AWS.Messaging/AWS.Messaging.csproj b/src/AWS.Messaging/AWS.Messaging.csproj index 12b007c9..532a5a1e 100644 --- a/src/AWS.Messaging/AWS.Messaging.csproj +++ b/src/AWS.Messaging/AWS.Messaging.csproj @@ -17,7 +17,7 @@ CA1727 true ..\..\public.snk - 0.9.4 + 0.9.5 true true true From 1abdcc55e3248ce0096550e6145adc19b6078673 Mon Sep 17 00:00:00 2001 From: aws-sdk-dotnet-automation Date: Wed, 12 Mar 2025 20:51:27 +0000 Subject: [PATCH 4/6] Updated changelog --- .../changes/01370ae5-3537-4723-80b3-ee82e79e80b9.json | 11 ----------- CHANGELOG.md | 5 +++++ 2 files changed, 5 insertions(+), 11 deletions(-) delete mode 100644 .autover/changes/01370ae5-3537-4723-80b3-ee82e79e80b9.json diff --git a/.autover/changes/01370ae5-3537-4723-80b3-ee82e79e80b9.json b/.autover/changes/01370ae5-3537-4723-80b3-ee82e79e80b9.json deleted file mode 100644 index 650908a1..00000000 --- a/.autover/changes/01370ae5-3537-4723-80b3-ee82e79e80b9.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "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/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) From b3b009ff7932018daf5970ab350602bc018c107e Mon Sep 17 00:00:00 2001 From: Garrett Beatty Date: Mon, 24 Mar 2025 13:51:31 -0400 Subject: [PATCH 5/6] Update sample apps readme (#196) --- .../LambdaMessaging/HandlerSampleRequest.json | 20 ++ .../LambdaMessaging/LambdaMessaging.csproj | 9 +- .../Properties/launchSettings.json | 11 +- sampleapps/LambdaMessaging/README.md | 79 +++++++ sampleapps/LambdaMessaging/Startup.cs | 18 +- .../LambdaMessaging/serverless.template | 4 +- .../PollyIntegration/PollyIntegration.csproj | 6 +- sampleapps/PollyIntegration/README.md | 149 +++++++++++++ sampleapps/PublisherAPI/PublisherAPI.csproj | 8 +- sampleapps/PublisherAPI/README.md | 209 ++++++++++++++++++ sampleapps/README.md | 34 +++ sampleapps/SubscriberService/README.md | 150 +++++++++++++ .../SubscriberService.csproj | 12 +- src/AWS.Messaging.Lambda/README.md | 8 +- 14 files changed, 683 insertions(+), 34 deletions(-) create mode 100644 sampleapps/LambdaMessaging/HandlerSampleRequest.json create mode 100644 sampleapps/LambdaMessaging/README.md create mode 100644 sampleapps/PollyIntegration/README.md create mode 100644 sampleapps/PublisherAPI/README.md create mode 100644 sampleapps/README.md create mode 100644 sampleapps/SubscriberService/README.md 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 d1a946f0..e735d8ab 100644 --- a/sampleapps/LambdaMessaging/LambdaMessaging.csproj +++ b/sampleapps/LambdaMessaging/LambdaMessaging.csproj @@ -1,22 +1,19 @@ - net6.0 + net8.0 enable enable true Lambda - true true - + - - - + diff --git a/sampleapps/LambdaMessaging/Properties/launchSettings.json b/sampleapps/LambdaMessaging/Properties/launchSettings.json index a82d4042..1712041f 100644 --- a/sampleapps/LambdaMessaging/Properties/launchSettings.json +++ b/sampleapps/LambdaMessaging/Properties/launchSettings.json @@ -1,10 +1,13 @@ { "profiles": { - "Mock Lambda Test Tool": { + "Default": { + "workingDirectory": ".\\bin\\$(Configuration)\\net8.0", "commandName": "Executable", - "commandLineArgs": "--port 5050", - "workingDirectory": ".\\bin\\$(Configuration)\\net6.0", - "executablePath": "%USERPROFILE%\\.dotnet\\tools\\dotnet-lambda-test-tool-6.0.exe" + "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/LambdaMessaging/serverless.template b/sampleapps/LambdaMessaging/serverless.template index 1fdee3f4..8881b851 100644 --- a/sampleapps/LambdaMessaging/serverless.template +++ b/sampleapps/LambdaMessaging/serverless.template @@ -1,7 +1,7 @@ { "AWSTemplateFormatVersion": "2010-09-09", "Transform": "AWS::Serverless-2016-10-31", - "Description": "This template is partially managed by Amazon.Lambda.Annotations (v1.0.0.0).", + "Description": "This template is partially managed by Amazon.Lambda.Annotations (v1.7.0.0).", "Resources": { "ChatQueue": { "Type": "AWS::SQS::Queue", @@ -13,7 +13,7 @@ "Tool": "Amazon.Lambda.Annotations" }, "Properties": { - "Runtime": "dotnet6", + "Runtime": "dotnet8", "CodeUri": ".", "MemorySize": 256, "Timeout": 30, diff --git a/sampleapps/PollyIntegration/PollyIntegration.csproj b/sampleapps/PollyIntegration/PollyIntegration.csproj index 11b14630..7e356cdc 100644 --- a/sampleapps/PollyIntegration/PollyIntegration.csproj +++ b/sampleapps/PollyIntegration/PollyIntegration.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 enable enable @@ -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 9d9f42c5..91a45c77 100644 --- a/sampleapps/PublisherAPI/PublisherAPI.csproj +++ b/sampleapps/PublisherAPI/PublisherAPI.csproj @@ -1,15 +1,15 @@ - net6.0 + net8.0 enable enable - - - + + + 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 de382e3b..99f06212 100644 --- a/sampleapps/SubscriberService/SubscriberService.csproj +++ b/sampleapps/SubscriberService/SubscriberService.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 enable enable @@ -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; } } ``` From e590fe2a6ec00c0522b71687eb2a2aeacaa4c2d4 Mon Sep 17 00:00:00 2001 From: Garrett Beatty Date: Tue, 25 Mar 2025 17:10:36 -0400 Subject: [PATCH 6/6] add change file --- .../changes/01370ae5-3537-4723-80b3-ee82e79e80b9.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .autover/changes/01370ae5-3537-4723-80b3-ee82e79e80b9.json 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