diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index cbd016bd0..c1f5391bb 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -165,7 +165,8 @@ const config: UserConfig = { {text: 'Dead Letter Queues', link:'/guide/messaging/transports/sqs/deadletterqueues'}, {text: 'Configuring Queues', link:'/guide/messaging/transports/sqs/queues'}, {text: 'Conventional Routing', link:'/guide/messaging/transports/sqs/conventional-routing'}, - {text: 'Interoperability', link:'/guide/messaging/transports/sqs/interoperability'} + {text: 'Interoperability', link:'/guide/messaging/transports/sqs/interoperability'}, + {text: 'MessageAttributes', link:'/guide/messaging/transports/sqs/message-attributes'} ]}, {text: 'Amazon SNS', link: '/guide/messaging/transports/sns'}, {text: 'TCP', link: '/guide/messaging/transports/tcp'}, diff --git a/docs/guide/messaging/transports/sqs/interoperability.md b/docs/guide/messaging/transports/sqs/interoperability.md index bd8d8c3aa..7ba05474b 100644 --- a/docs/guide/messaging/transports/sqs/interoperability.md +++ b/docs/guide/messaging/transports/sqs/interoperability.md @@ -90,7 +90,7 @@ public class CustomSqsMapper : ISqsEnvelopeMapper } } ``` -snippet source | anchor +snippet source | anchor And apply this to any or all of your SQS endpoints with the configuration fluent interface as shown in this sample: diff --git a/docs/guide/messaging/transports/sqs/message-attributes.md b/docs/guide/messaging/transports/sqs/message-attributes.md new file mode 100644 index 000000000..8593764ab --- /dev/null +++ b/docs/guide/messaging/transports/sqs/message-attributes.md @@ -0,0 +1,68 @@ +# Receiving SQS Message Attributes + +Here’s the deal: Amazon SQS won’t just give you the user-defined message attributes for free you have to explicitly ask for them in the receive request. Up until now, Wolverine never set that field, which meant any custom attributes upstream were effectively invisible. + +As of now, you can opt in to request those attributes. This is **interop-only**: Wolverine will ask SQS for the attributes if you configure it, but it’s still up to your own `ISqsEnvelopeMapper` to decide what to do with them. + +::: tip +Built-in mappers (`DefaultSqsEnvelopeMapper`, `RawJsonSqsEnvelopeMapper`) don’t touch message attributes. If you need them, you’ll need your own mapper. +::: + +## Opting in + +You can request *all* user-defined attributes: + + + +```cs +using var host = await Host.CreateDefaultBuilder() + .UseWolverine(opts => + { + opts.UseAmazonSqsTransport() + .ConfigureSenders(s => s.InteropWith(new CustomSqsMapper())); + + opts.ListenToSqsQueue("incoming", queue => + { + // Ask SQS for all user-defined attributes + queue.MessageAttributeNames = new List { "All" }; + }); + }).StartAsync(); +``` +snippet source | anchor + + +Or just the attributes you actually care about: + + + +```cs +using var host = await Host.CreateDefaultBuilder() + .UseWolverine(opts => + { + opts.UseAmazonSqsTransport() + .ConfigureSenders(s => s.InteropWith(new CustomSqsMapper())); + + opts.ListenToSqsQueue("incoming", queue => + { + // Ask only for specific attributes + queue.MessageAttributeNames = new List { "wolverineId", "jasperId" }; + }); + }).StartAsync(); +``` +snippet source | anchor + + + +Once you’ve opted in, those attributes are available in the dictionary passed to `ISqsEnvelopeMapper.ReadEnvelopeData`. From there, you can stash them in `Envelope.Headers`, set correlation IDs, or just ignore them. + +## Things to know + +* If `MessageAttributeNames` is `null` or empty, nothing changes (this is the default). +* `"All"` asks SQS for every user-defined attribute. +* Pulling in lots of attributes increases your payload size. Use this only when you need it. +* This affects **receiving only**. Sending attributes is still a job for your custom mapper. +* System attributes (`MessageSystemAttributeNames`) are a different story and are not part of this feature. + +::: info +That’s it. If you’ve already got a custom mapper, you can now wire in SQS attributes directly without having to bend over backwards with the AWS SDK. +::: \ No newline at end of file diff --git a/src/Transports/AWS/Wolverine.AmazonSqs.Tests/Samples/Bootstrapping.cs b/src/Transports/AWS/Wolverine.AmazonSqs.Tests/Samples/Bootstrapping.cs index 946fa2d94..ac22f3682 100644 --- a/src/Transports/AWS/Wolverine.AmazonSqs.Tests/Samples/Bootstrapping.cs +++ b/src/Transports/AWS/Wolverine.AmazonSqs.Tests/Samples/Bootstrapping.cs @@ -339,6 +339,46 @@ public async Task customize_mappers() #endregion } + + public async Task customize_mappers_with_all_message_attributes() + { + #region sample_receive_all_message_attributes + + using var host = await Host.CreateDefaultBuilder() + .UseWolverine(opts => + { + opts.UseAmazonSqsTransport() + .ConfigureSenders(s => s.InteropWith(new CustomSqsMapper())); + + opts.ListenToSqsQueue("incoming", queue => + { + // Ask SQS for all user-defined attributes + queue.MessageAttributeNames = new List { "All" }; + }); + }).StartAsync(); + + #endregion + } + + public async Task customize_mappers_with_specific_message_attributes() + { + #region sample_receive_specific_message_attributes + + using var host = await Host.CreateDefaultBuilder() + .UseWolverine(opts => + { + opts.UseAmazonSqsTransport() + .ConfigureSenders(s => s.InteropWith(new CustomSqsMapper())); + + opts.ListenToSqsQueue("incoming", queue => + { + // Ask only for specific attributes + queue.MessageAttributeNames = new List { "wolverineId", "jasperId" }; + }); + }).StartAsync(); + + #endregion + } } #region sample_custom_sqs_mapper