1
1
namespace SlimMessageBus . Host . AmazonSQS ;
2
- internal abstract class SqsBaseConsumer : AbstractConsumer
2
+
3
+ abstract internal class SqsBaseConsumer : AbstractConsumer
3
4
{
4
5
private readonly ISqsClientProvider _clientProvider ;
5
6
6
7
// consumer settings
7
8
private readonly int _maxMessages ;
8
9
private readonly int _visibilityTimeout ;
9
10
private readonly List < string > _messageAttributeNames ;
11
+ private readonly bool _isSubscribedToTopic ;
10
12
11
13
private Task _task ;
12
14
13
15
public SqsMessageBus MessageBus { get ; }
14
16
protected IMessageProcessor < SqsTransportMessageWithPayload > MessageProcessor { get ; }
15
17
protected ISqsHeaderSerializer < Amazon . SQS . Model . MessageAttributeValue > HeaderSerializer { get ; }
18
+ protected IMessageSerializer < string > MessageSerializer { get ; }
16
19
17
20
protected SqsBaseConsumer (
18
21
SqsMessageBus messageBus ,
19
22
ISqsClientProvider clientProvider ,
20
23
string path ,
21
24
IMessageProcessor < SqsTransportMessageWithPayload > messageProcessor ,
25
+ IMessageSerializer < string > messageSerializer ,
22
26
IEnumerable < AbstractConsumerSettings > consumerSettings ,
23
27
ILogger logger )
24
28
: base ( logger ,
@@ -30,6 +34,8 @@ protected SqsBaseConsumer(
30
34
MessageBus = messageBus ;
31
35
MessageProcessor = messageProcessor ?? throw new ArgumentNullException ( nameof ( messageProcessor ) ) ;
32
36
HeaderSerializer = messageBus . SqsHeaderSerializer ;
37
+ MessageSerializer = messageSerializer ?? throw new ArgumentNullException ( nameof ( messageSerializer ) ) ;
38
+
33
39
T GetSingleValue < T > ( Func < AbstractConsumerSettings , T > selector , string settingName , T defaultValue = default )
34
40
{
35
41
var set = consumerSettings . Select ( x => selector ( x ) ) . Where ( x => x is not null && ! x . Equals ( defaultValue ) ) . ToHashSet ( ) ;
@@ -43,6 +49,7 @@ T GetSingleValue<T>(Func<AbstractConsumerSettings, T> selector, string settingNa
43
49
_maxMessages = GetSingleValue ( x => x . GetOrDefault ( SqsProperties . MaxMessages ) , nameof ( SqsConsumerBuilderExtensions . MaxMessages ) ) ?? messageBus . ProviderSettings . MaxMessageCount ;
44
50
_visibilityTimeout = GetSingleValue ( x => x . GetOrDefault ( SqsProperties . VisibilityTimeout ) , nameof ( SqsConsumerBuilderExtensions . VisibilityTimeout ) ) ?? 30 ;
45
51
_messageAttributeNames = [ .. GetSingleValue ( x => x . GetOrDefault ( SqsProperties . MessageAttributes ) , nameof ( SqsConsumerBuilderExtensions . FetchMessageAttributes ) ) ?? [ "All" ] ] ;
52
+ _isSubscribedToTopic = consumerSettings . Any ( x => x . GetOrDefault ( SqsProperties . SubscribeToTopic ) is not null ) ;
46
53
}
47
54
48
55
private async Task < IReadOnlyCollection < Message > > ReceiveMessagesByUrl ( string queueUrl )
@@ -111,13 +118,9 @@ protected async Task Run()
111
118
var messages = await ReceiveMessagesByUrl ( queueUrl ) . ConfigureAwait ( false ) ;
112
119
foreach ( var message in messages )
113
120
{
114
- // ToDo: Check if SNS message and if so unwrap envelope and use inner body and attributes
121
+ Logger . LogDebug ( "Received message on Queue: {Queue}, MessageId: {MessageId}, Payload: {MessagePayload}" , Path , message . MessageId , message . Body ) ;
115
122
116
- var messageHeaders = message
117
- . MessageAttributes
118
- . ToDictionary ( x => x . Key , x => HeaderSerializer . Deserialize ( x . Key , x . Value ) ) ;
119
-
120
- var messagePayload = message . Body ;
123
+ GetPayloadAndHeadersFromMessage ( message , out var messagePayload , out var messageHeaders ) ;
121
124
122
125
var r = await MessageProcessor . ProcessMessage ( new ( message , messagePayload ) , messageHeaders , cancellationToken : CancellationToken ) . ConfigureAwait ( false ) ;
123
126
if ( r . Exception != null )
@@ -146,20 +149,23 @@ protected async Task Run()
146
149
}
147
150
}
148
151
}
149
- }
150
152
151
- internal class SnsEnvelope
152
- {
153
- public string Type { get ; set ; }
154
- public string MessageId { get ; set ; }
155
- public string TopicArn { get ; set ; }
156
- public string Message { get ; set ; }
157
- public string Timestamp { get ; set ; }
158
- public Dictionary < string , SnsMessageAttribute > MessageAttributes { get ; set ; }
159
- }
153
+ private void GetPayloadAndHeadersFromMessage ( Message message , out string messagePayload , out Dictionary < string , object > messageHeaders )
154
+ {
155
+ if ( _isSubscribedToTopic )
156
+ {
157
+ // Note: Messages from SNS topics are wrapped in an envelope like SnsEnvelope type. We need to get the actual message and headers from it.
158
+ var snsEnvelope = ( SnsEnvelope ) MessageSerializer . Deserialize ( typeof ( SnsEnvelope ) , message . Body ) ;
160
159
161
- internal class SnsMessageAttribute
162
- {
163
- public string Type { get ; set ; }
164
- public string Value { get ; set ; }
165
- }
160
+ messagePayload = snsEnvelope . Message ?? throw new ConsumerMessageBusException ( "Message of the SNS Envelope was null" ) ;
161
+ messageHeaders = ( snsEnvelope . MessageAttributes ?? throw new ConsumerMessageBusException ( "Message of the SNS Envelope was null" ) )
162
+ . ToDictionary ( x => x . Key , x => HeaderSerializer . Deserialize ( x . Key , new MessageAttributeValue { DataType = x . Value . Type , StringValue = x . Value . Value } ) ) ;
163
+ }
164
+ else
165
+ {
166
+ messagePayload = message . Body ;
167
+ messageHeaders = message . MessageAttributes
168
+ . ToDictionary ( x => x . Key , x => HeaderSerializer . Deserialize ( x . Key , x . Value ) ) ;
169
+ }
170
+ }
171
+ }
0 commit comments