Skip to content

Commit 89b2d75

Browse files
Update logic to make DefaultMessageManager fail on InvalidMessageHandlerSignatureException (#169)
1 parent 8a2270b commit 89b2d75

File tree

10 files changed

+152
-4
lines changed

10 files changed

+152
-4
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"Projects": [
3+
{
4+
"Name": "AWS.Messaging",
5+
"Type": "Patch",
6+
"ChangelogMessages": [
7+
"Update logic to make DefaultMessageManager fail on InvalidMessageHandlerSignatureException"
8+
]
9+
}
10+
]
11+
}

src/AWS.Messaging/Services/DefaultMessageManager.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@ private async Task<bool> InvokeHandler(MessageEnvelope messageEnvelope, Subscrib
175175
{
176176
await handlerTask;
177177
}
178+
catch (InvalidMessageHandlerSignatureException)
179+
{
180+
throw;
181+
}
178182
catch (AWSMessagingException)
179183
{
180184
// Swallow exceptions thrown by the framework, and rely on the thrower to log

src/AWS.Messaging/Services/HandlerInvoker.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,16 @@ public async Task<MessageProcessStatus> InvokeAsync(MessageEnvelope messageEnvel
5656

5757
using (var scope = _serviceProvider.CreateScope())
5858
{
59-
var handler = scope.ServiceProvider.GetService(subscriberMapping.HandlerType);
60-
61-
if (handler == null)
59+
object handler;
60+
try
61+
{
62+
handler = scope.ServiceProvider.GetRequiredService(subscriberMapping.HandlerType);
63+
}
64+
catch (Exception e)
6265
{
6366
_logger.LogError("Unable to resolve a handler for {HandlerType} while handling message ID {MessageEnvelopeId}.", subscriberMapping.HandlerType, messageEnvelope.Id);
6467
throw new InvalidMessageHandlerSignatureException($"Unable to resolve a handler for {subscriberMapping.HandlerType} " +
65-
$"while handling message ID {messageEnvelope.Id}.");
68+
$"while handling message ID {messageEnvelope.Id}.", e);
6669
}
6770

6871
var method = _handlerMethods.GetOrAdd(subscriberMapping.MessageType, x =>

test/AWS.Messaging.UnitTests/DefaultMessageManagerTests.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,40 @@ public async Task DefaultMessageManager_MessageGroupIsSkipped()
343343
Assert.Equal("2", transactionsInGroupA[1].Id);
344344
}
345345

346+
[Fact]
347+
public async Task DefaultMessageManager_RethrowsInvalidMessageHandlerSignatureException()
348+
{
349+
var mockSQSMessageCommunication = CreateMockSQSMessageCommunication();
350+
var mockHandlerInvoker = new Mock<IHandlerInvoker>();
351+
mockHandlerInvoker
352+
.Setup(x => x.InvokeAsync(It.IsAny<MessageEnvelope>(), It.IsAny<SubscriberMapping>(), It.IsAny<CancellationToken>()))
353+
.ThrowsAsync(new InvalidMessageHandlerSignatureException("Test exception"));
354+
355+
var manager = new DefaultMessageManager(
356+
mockSQSMessageCommunication.Object,
357+
mockHandlerInvoker.Object,
358+
new NullLogger<DefaultMessageManager>(),
359+
new MessageManagerConfiguration());
360+
361+
var messageEnvelope = new MessageEnvelope<ChatMessage> { Id = "1" };
362+
var subscriberMapping = new SubscriberMapping(typeof(ChatMessageHandler), typeof(ChatMessage));
363+
364+
await Assert.ThrowsAsync<InvalidMessageHandlerSignatureException>(() =>
365+
manager.ProcessMessageAsync(messageEnvelope, subscriberMapping));
366+
367+
// Verify that the handler was invoked
368+
mockHandlerInvoker.Verify(x => x.InvokeAsync(
369+
It.Is<MessageEnvelope>(m => m == messageEnvelope),
370+
It.Is<SubscriberMapping>(s => s == subscriberMapping),
371+
It.IsAny<CancellationToken>()),
372+
Times.Once);
373+
374+
mockSQSMessageCommunication.Verify(x => x.DeleteMessagesAsync(It.IsAny<IEnumerable<MessageEnvelope>>(), It.IsAny<CancellationToken>()), Times.Never);
375+
mockSQSMessageCommunication.Verify(x => x.ReportMessageFailureAsync(It.IsAny<MessageEnvelope>(), It.IsAny<CancellationToken>()), Times.Never);
376+
377+
Assert.Equal(0, manager.ActiveMessageCount);
378+
}
379+
346380
private MessageEnvelope<TransactionInfo> CreateTransactionEnvelope(string id, string userId, bool shouldFail)
347381
{
348382
return new MessageEnvelope<TransactionInfo>

test/AWS.Messaging.UnitTests/HandlerInvokerTests.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

4+
using System;
45
using System.Collections.Generic;
56
using System.Threading.Tasks;
67
using AWS.Messaging.Configuration;
@@ -154,4 +155,32 @@ public async Task HandlerInvoker_VerifyHandlersAreRetrievedAsScopedDependencies(
154155
}
155156
Assert.Equal(3, messageStorage2.Count);
156157
}
158+
159+
[Fact]
160+
public async Task HandlerInvoker_VerifyHandlersFatalErrorWhenDIFails()
161+
{
162+
var serviceCollection = new ServiceCollection()
163+
.AddAWSMessageBus(builder =>
164+
{
165+
builder.AddMessageHandler<ChatMessageHandlerWithDependencies, ChatMessage>();
166+
}).AddSingleton<IDependentThing>(x =>
167+
{
168+
var thingDoer = x.GetRequiredService<IThingDoer>();
169+
throw new InvalidOperationException("Blah blah"); // intentionally make the DI fail.
170+
});
171+
172+
var serviceProvider = serviceCollection.BuildServiceProvider();
173+
174+
var handlerInvoker = new HandlerInvoker(
175+
serviceProvider,
176+
new NullLogger<HandlerInvoker>(),
177+
new DefaultTelemetryFactory(serviceProvider));
178+
179+
var envelope = new MessageEnvelope<ChatMessage>();
180+
var subscriberMapping = new SubscriberMapping(typeof(ChatMessageHandlerWithDependencies), typeof(ChatMessage));
181+
await Assert.ThrowsAsync<InvalidMessageHandlerSignatureException>(async () =>
182+
{
183+
await handlerInvoker.InvokeAsync(envelope, subscriberMapping);
184+
});
185+
}
157186
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
namespace AWS.Messaging.UnitTests.MessageHandlers;
5+
6+
public class DependentThing : IDependentThing
7+
{
8+
private readonly IThingDoer _ThingDoer;
9+
10+
public DependentThing(IThingDoer thingDoer)
11+
{
12+
_ThingDoer = thingDoer;
13+
}
14+
15+
16+
public void DoThingWithThing()
17+
{
18+
_ThingDoer.DoThing();
19+
}
20+
}

test/AWS.Messaging.UnitTests/MessageHandlers/Handlers.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Collections.Generic;
66
using System.Threading;
77
using System.Threading.Tasks;
8+
using Amazon.Lambda.Core;
89
using AWS.Messaging.UnitTests.Models;
910
using Microsoft.Extensions.DependencyInjection;
1011
using Xunit;
@@ -27,6 +28,22 @@ public Task<MessageProcessStatus> HandleAsync(MessageEnvelope<AddressInfo> messa
2728
}
2829
}
2930

31+
public class ChatMessageHandlerWithDependencies : IMessageHandler<ChatMessage>
32+
{
33+
private readonly IDependentThing _thingDoer;
34+
35+
public ChatMessageHandlerWithDependencies(IDependentThing thingDoer)
36+
{
37+
_thingDoer = thingDoer;
38+
}
39+
40+
public Task<MessageProcessStatus> HandleAsync(MessageEnvelope<ChatMessage> messageEnvelope, CancellationToken token = default)
41+
{
42+
_thingDoer.DoThingWithThing();
43+
return Task.FromResult(MessageProcessStatus.Success());
44+
}
45+
}
46+
3047
/// <summary>
3148
/// Implements handling for mutiple message types
3249
/// </summary>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
namespace AWS.Messaging.UnitTests.MessageHandlers;
5+
6+
public interface IDependentThing
7+
{
8+
void DoThingWithThing();
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
namespace AWS.Messaging.UnitTests.MessageHandlers;
5+
6+
public interface IThingDoer
7+
{
8+
string DoThing();
9+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
namespace AWS.Messaging.UnitTests.MessageHandlers;
5+
6+
public class ThingDoer : IThingDoer
7+
{
8+
public string DoThing()
9+
{
10+
return "I did a thing!";
11+
}
12+
}

0 commit comments

Comments
 (0)