Skip to content

Commit 865b539

Browse files
benspethbspeth-afk
andauthored
Updated IMessageMetadataAccessor interface to allow to be decorated (#112)
Co-authored-by: Benjamin SPETH <bspeth@ecovadis.com>
1 parent 85d9173 commit 865b539

6 files changed

Lines changed: 191 additions & 5 deletions

File tree

docs/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## 5.7.1
8+
- Changed
9+
- Updated IMessageMetadataAccessor interface to allow to be decorated
10+
711
## 5.7.0
812
- Changed
913
- Updated IMessagePublisher and Dispatch contract to allow for setting more properties of the underlying ServiceBusMessage

src/Ev.ServiceBus.Abstractions/MessageReception/IMessageMetadataAccessor.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22

33
public interface IMessageMetadataAccessor
44
{
5-
public IMessageMetadata? Metadata { get; }
5+
IMessageMetadata? Metadata { get; }
6+
void SetData(MessageContext context);
67
}

src/Ev.ServiceBus/Management/MessageMetadataAccessor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ public class MessageMetadataAccessor : IMessageMetadataAccessor
77
{
88
public IMessageMetadata? Metadata { get; private set; }
99

10-
internal void SetData(MessageContext context)
10+
public void SetData(MessageContext context)
1111
{
1212
if (context.SessionArgs != null)
1313
{

src/Ev.ServiceBus/Reception/MessageReceptionHandler.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
using Ev.ServiceBus.Diagnostics;
1010
using Ev.ServiceBus.Exceptions;
1111
using Ev.ServiceBus.Isolation;
12-
using Ev.ServiceBus.Management;
1312
using Microsoft.Extensions.DependencyInjection;
1413
using Microsoft.Extensions.Logging;
1514

@@ -20,7 +19,7 @@ public class MessageReceptionHandler
2019
private readonly MethodInfo _callHandlerInfo;
2120
private readonly IMessagePayloadSerializer _messagePayloadSerializer;
2221
private readonly ILogger<LoggingExtensions.MessageProcessing> _logger;
23-
private readonly MessageMetadataAccessor _messageMetadataAccessor;
22+
private readonly IMessageMetadataAccessor _messageMetadataAccessor;
2423
private readonly IEnumerable<IServiceBusEventListener> _eventListeners;
2524
private readonly IServiceProvider _provider;
2625
private readonly IsolationService _isolationService;
@@ -36,7 +35,7 @@ public MessageReceptionHandler(
3635
_provider = provider;
3736
_messagePayloadSerializer = messagePayloadSerializer;
3837
_logger = logger;
39-
_messageMetadataAccessor = (MessageMetadataAccessor)messageMetadataAccessor;
38+
_messageMetadataAccessor = messageMetadataAccessor;
4039
_eventListeners = eventListeners;
4140
_callHandlerInfo = GetType().GetMethod(nameof(CallHandler), BindingFlags.NonPublic | BindingFlags.Instance)!;
4241
_isolationService = isolationService;

tests/Ev.ServiceBus.UnitTests/Ev.ServiceBus.UnitTests.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
<Nullable>enable</Nullable>
88
</PropertyGroup>
99

10+
<ItemGroup>
11+
<PackageReference Include="Scrutor" Version="4.2.2" />
12+
</ItemGroup>
13+
1014
<ItemGroup>
1115
<ProjectReference Include="..\..\src\Ev.ServiceBus.Abstractions\Ev.ServiceBus.Abstractions.csproj" />
1216
<ProjectReference Include="..\..\src\Ev.ServiceBus\Ev.ServiceBus.csproj" />
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
using System.Collections.Generic;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
using Azure.Messaging.ServiceBus;
5+
using Ev.ServiceBus.Abstractions;
6+
using Ev.ServiceBus.Abstractions.MessageReception;
7+
using Ev.ServiceBus.Reception;
8+
using Ev.ServiceBus.TestHelpers;
9+
using Ev.ServiceBus.UnitTests.Helpers;
10+
using FluentAssertions;
11+
using Microsoft.Extensions.DependencyInjection;
12+
using Xunit;
13+
14+
namespace Ev.ServiceBus.UnitTests;
15+
16+
public class MessageMetadataAccessorDecoratorTests
17+
{
18+
[Fact]
19+
public async Task DecoratorSetDataIsCalledDuringReception()
20+
{
21+
var composer = new Composer();
22+
23+
composer.WithAdditionalServices(services =>
24+
{
25+
services.AddSingleton<DecoratorCallTracker>();
26+
services.Decorate<IMessageMetadataAccessor, TrackingMetadataAccessorDecorator>();
27+
services.RegisterServiceBusReception().FromQueue("testQueue", builder =>
28+
{
29+
builder.RegisterReception<Payload, NoopHandler>();
30+
});
31+
});
32+
33+
var provider = await composer.Compose();
34+
var clientMock = provider.GetProcessorMock("testQueue");
35+
36+
await TriggerReception(clientMock);
37+
38+
var tracker = provider.GetRequiredService<DecoratorCallTracker>();
39+
tracker.SetDataCallCount.Should().Be(1);
40+
}
41+
42+
[Fact]
43+
public async Task DecoratorMetadataIsAccessibleFromHandler()
44+
{
45+
var composer = new Composer();
46+
47+
composer.WithAdditionalServices(services =>
48+
{
49+
services.AddSingleton<DecoratorCallTracker>();
50+
services.AddSingleton<List<IMessageMetadata?>>();
51+
services.Decorate<IMessageMetadataAccessor, TrackingMetadataAccessorDecorator>();
52+
services.RegisterServiceBusReception().FromQueue("testQueue", builder =>
53+
{
54+
builder.RegisterReception<Payload, MetadataCaptureHandler>();
55+
});
56+
});
57+
58+
var provider = await composer.Compose();
59+
var clientMock = provider.GetProcessorMock("testQueue");
60+
61+
await TriggerReception(clientMock);
62+
63+
var captured = provider.GetRequiredService<List<IMessageMetadata?>>();
64+
captured.Count.Should().Be(1);
65+
captured[0].Should().NotBeNull();
66+
captured[0]!.Subject.Should().Be("test subject");
67+
}
68+
69+
[Fact]
70+
public async Task DecoratorSetDataIsCalledDuringSessionReception()
71+
{
72+
var composer = new Composer();
73+
74+
composer.WithAdditionalServices(services =>
75+
{
76+
services.AddSingleton<DecoratorCallTracker>();
77+
services.Decorate<IMessageMetadataAccessor, TrackingMetadataAccessorDecorator>();
78+
services.RegisterServiceBusReception().FromQueue("testQueue", builder =>
79+
{
80+
builder.EnableSessionHandling(options => { });
81+
builder.RegisterReception<Payload, NoopHandler>();
82+
});
83+
});
84+
85+
var provider = await composer.Compose();
86+
var clientMock = provider.GetSessionProcessorMock("testQueue");
87+
88+
await TriggerSessionReception(clientMock);
89+
90+
var tracker = provider.GetRequiredService<DecoratorCallTracker>();
91+
tracker.SetDataCallCount.Should().Be(1);
92+
}
93+
94+
private static async Task TriggerReception(ProcessorMock client, CancellationToken cancellationToken = default)
95+
{
96+
var serializer = new TextJsonPayloadSerializer();
97+
var body = serializer.SerializeBody(new { });
98+
var message = new ServiceBusMessage(body.Body)
99+
{
100+
ContentType = body.ContentType,
101+
Subject = "test subject",
102+
ApplicationProperties =
103+
{
104+
{ UserProperties.MessageTypeProperty, "IntegrationEvent" },
105+
{ UserProperties.PayloadTypeIdProperty, "Payload" }
106+
}
107+
};
108+
await client.TriggerMessageReception(message, cancellationToken);
109+
}
110+
111+
private static async Task TriggerSessionReception(SessionProcessorMock client, CancellationToken cancellationToken = default)
112+
{
113+
var serializer = new TextJsonPayloadSerializer();
114+
var body = serializer.SerializeBody(new { });
115+
var message = new ServiceBusMessage(body.Body)
116+
{
117+
ContentType = body.ContentType,
118+
Subject = "test subject",
119+
ApplicationProperties =
120+
{
121+
{ UserProperties.MessageTypeProperty, "IntegrationEvent" },
122+
{ UserProperties.PayloadTypeIdProperty, "Payload" }
123+
}
124+
};
125+
await client.TriggerMessageReception(message, cancellationToken);
126+
}
127+
128+
internal class Payload { }
129+
130+
private class NoopHandler : IMessageReceptionHandler<Payload>
131+
{
132+
public Task Handle(Payload @event, CancellationToken cancellationToken) => Task.CompletedTask;
133+
}
134+
135+
private class MetadataCaptureHandler : IMessageReceptionHandler<Payload>
136+
{
137+
private readonly IMessageMetadataAccessor _accessor;
138+
private readonly List<IMessageMetadata?> _captured;
139+
140+
public MetadataCaptureHandler(IMessageMetadataAccessor accessor, List<IMessageMetadata?> captured)
141+
{
142+
_accessor = accessor;
143+
_captured = captured;
144+
}
145+
146+
public Task Handle(Payload @event, CancellationToken cancellationToken)
147+
{
148+
_captured.Add(_accessor.Metadata);
149+
return Task.CompletedTask;
150+
}
151+
}
152+
153+
internal class DecoratorCallTracker
154+
{
155+
public int SetDataCallCount { get; private set; }
156+
public void RecordSetData() => SetDataCallCount++;
157+
}
158+
159+
private class TrackingMetadataAccessorDecorator : IMessageMetadataAccessor
160+
{
161+
private readonly IMessageMetadataAccessor _inner;
162+
private readonly DecoratorCallTracker _tracker;
163+
164+
public TrackingMetadataAccessorDecorator(IMessageMetadataAccessor inner, DecoratorCallTracker tracker)
165+
{
166+
_inner = inner;
167+
_tracker = tracker;
168+
}
169+
170+
public IMessageMetadata? Metadata => _inner.Metadata;
171+
172+
public void SetData(MessageContext context)
173+
{
174+
_tracker.RecordSetData();
175+
_inner.SetData(context);
176+
}
177+
}
178+
}

0 commit comments

Comments
 (0)