Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## 5.7.1
- Changed
- Updated IMessageMetadataAccessor interface to allow to be decorated

## 5.7.0
- Changed
- Updated IMessagePublisher and Dispatch contract to allow for setting more properties of the underlying ServiceBusMessage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@

public interface IMessageMetadataAccessor
{
public IMessageMetadata? Metadata { get; }
IMessageMetadata? Metadata { get; }
void SetData(MessageContext context);
}
2 changes: 1 addition & 1 deletion src/Ev.ServiceBus/Management/MessageMetadataAccessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public class MessageMetadataAccessor : IMessageMetadataAccessor
{
public IMessageMetadata? Metadata { get; private set; }

internal void SetData(MessageContext context)
public void SetData(MessageContext context)
{
if (context.SessionArgs != null)
{
Expand Down
5 changes: 2 additions & 3 deletions src/Ev.ServiceBus/Reception/MessageReceptionHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
using Ev.ServiceBus.Diagnostics;
using Ev.ServiceBus.Exceptions;
using Ev.ServiceBus.Isolation;
using Ev.ServiceBus.Management;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

Expand All @@ -20,7 +19,7 @@
private readonly MethodInfo _callHandlerInfo;
private readonly IMessagePayloadSerializer _messagePayloadSerializer;
private readonly ILogger<LoggingExtensions.MessageProcessing> _logger;
private readonly MessageMetadataAccessor _messageMetadataAccessor;
private readonly IMessageMetadataAccessor _messageMetadataAccessor;
private readonly IEnumerable<IServiceBusEventListener> _eventListeners;
private readonly IServiceProvider _provider;
private readonly IsolationService _isolationService;
Expand All @@ -36,7 +35,7 @@
_provider = provider;
_messagePayloadSerializer = messagePayloadSerializer;
_logger = logger;
_messageMetadataAccessor = (MessageMetadataAccessor)messageMetadataAccessor;
_messageMetadataAccessor = messageMetadataAccessor;
_eventListeners = eventListeners;
_callHandlerInfo = GetType().GetMethod(nameof(CallHandler), BindingFlags.NonPublic | BindingFlags.Instance)!;
_isolationService = isolationService;
Expand Down Expand Up @@ -79,8 +78,8 @@

ServiceBusMeter.RecordDeliveryCount(
context.Message.DeliveryCount,
executionContext.ClientType,

Check warning on line 81 in src/Ev.ServiceBus/Reception/MessageReceptionHandler.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'clientType' in 'void ServiceBusMeter.RecordDeliveryCount(int value, string clientType, string resourceId, string? payloadTypeId)'.
executionContext.ResourceId,

Check warning on line 82 in src/Ev.ServiceBus/Reception/MessageReceptionHandler.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'resourceId' in 'void ServiceBusMeter.RecordDeliveryCount(int value, string clientType, string resourceId, string? payloadTypeId)'.
executionContext.PayloadTypeId);

ServiceBusMeter.RecordMessageQueueLatency(
Expand Down
4 changes: 4 additions & 0 deletions tests/Ev.ServiceBus.UnitTests/Ev.ServiceBus.UnitTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Scrutor" Version="4.2.2" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Ev.ServiceBus.Abstractions\Ev.ServiceBus.Abstractions.csproj" />
<ProjectReference Include="..\..\src\Ev.ServiceBus\Ev.ServiceBus.csproj" />
Expand Down
178 changes: 178 additions & 0 deletions tests/Ev.ServiceBus.UnitTests/MessageMetadataAccessorDecoratorTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Azure.Messaging.ServiceBus;
using Ev.ServiceBus.Abstractions;
using Ev.ServiceBus.Abstractions.MessageReception;
using Ev.ServiceBus.Reception;
using Ev.ServiceBus.TestHelpers;
using Ev.ServiceBus.UnitTests.Helpers;
using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;
using Xunit;

namespace Ev.ServiceBus.UnitTests;

public class MessageMetadataAccessorDecoratorTests
{
[Fact]
public async Task DecoratorSetDataIsCalledDuringReception()
{
var composer = new Composer();

composer.WithAdditionalServices(services =>
{
services.AddSingleton<DecoratorCallTracker>();
services.Decorate<IMessageMetadataAccessor, TrackingMetadataAccessorDecorator>();
services.RegisterServiceBusReception().FromQueue("testQueue", builder =>
{
builder.RegisterReception<Payload, NoopHandler>();
});
});

var provider = await composer.Compose();
var clientMock = provider.GetProcessorMock("testQueue");

await TriggerReception(clientMock);

var tracker = provider.GetRequiredService<DecoratorCallTracker>();
tracker.SetDataCallCount.Should().Be(1);
}

[Fact]
public async Task DecoratorMetadataIsAccessibleFromHandler()
{
var composer = new Composer();

composer.WithAdditionalServices(services =>
{
services.AddSingleton<DecoratorCallTracker>();
services.AddSingleton<List<IMessageMetadata?>>();
services.Decorate<IMessageMetadataAccessor, TrackingMetadataAccessorDecorator>();
services.RegisterServiceBusReception().FromQueue("testQueue", builder =>
{
builder.RegisterReception<Payload, MetadataCaptureHandler>();
});
});

var provider = await composer.Compose();
var clientMock = provider.GetProcessorMock("testQueue");

await TriggerReception(clientMock);

var captured = provider.GetRequiredService<List<IMessageMetadata?>>();
captured.Count.Should().Be(1);
captured[0].Should().NotBeNull();
captured[0]!.Subject.Should().Be("test subject");
}

[Fact]
public async Task DecoratorSetDataIsCalledDuringSessionReception()
{
var composer = new Composer();

composer.WithAdditionalServices(services =>
{
services.AddSingleton<DecoratorCallTracker>();
services.Decorate<IMessageMetadataAccessor, TrackingMetadataAccessorDecorator>();
services.RegisterServiceBusReception().FromQueue("testQueue", builder =>
{
builder.EnableSessionHandling(options => { });
builder.RegisterReception<Payload, NoopHandler>();
});
});

var provider = await composer.Compose();
var clientMock = provider.GetSessionProcessorMock("testQueue");

await TriggerSessionReception(clientMock);

var tracker = provider.GetRequiredService<DecoratorCallTracker>();
tracker.SetDataCallCount.Should().Be(1);
}

private static async Task TriggerReception(ProcessorMock client, CancellationToken cancellationToken = default)
{
var serializer = new TextJsonPayloadSerializer();
var body = serializer.SerializeBody(new { });
var message = new ServiceBusMessage(body.Body)
{
ContentType = body.ContentType,
Subject = "test subject",
ApplicationProperties =
{
{ UserProperties.MessageTypeProperty, "IntegrationEvent" },
{ UserProperties.PayloadTypeIdProperty, "Payload" }
}
};
await client.TriggerMessageReception(message, cancellationToken);
}

private static async Task TriggerSessionReception(SessionProcessorMock client, CancellationToken cancellationToken = default)
{
var serializer = new TextJsonPayloadSerializer();
var body = serializer.SerializeBody(new { });
var message = new ServiceBusMessage(body.Body)
{
ContentType = body.ContentType,
Subject = "test subject",
ApplicationProperties =
{
{ UserProperties.MessageTypeProperty, "IntegrationEvent" },
{ UserProperties.PayloadTypeIdProperty, "Payload" }
}
};
await client.TriggerMessageReception(message, cancellationToken);
}

internal class Payload { }

private class NoopHandler : IMessageReceptionHandler<Payload>
{
public Task Handle(Payload @event, CancellationToken cancellationToken) => Task.CompletedTask;
}

private class MetadataCaptureHandler : IMessageReceptionHandler<Payload>
{
private readonly IMessageMetadataAccessor _accessor;
private readonly List<IMessageMetadata?> _captured;

public MetadataCaptureHandler(IMessageMetadataAccessor accessor, List<IMessageMetadata?> captured)
{
_accessor = accessor;
_captured = captured;
}

public Task Handle(Payload @event, CancellationToken cancellationToken)
{
_captured.Add(_accessor.Metadata);
return Task.CompletedTask;
}
}

internal class DecoratorCallTracker
{
public int SetDataCallCount { get; private set; }
public void RecordSetData() => SetDataCallCount++;
}

private class TrackingMetadataAccessorDecorator : IMessageMetadataAccessor
{
private readonly IMessageMetadataAccessor _inner;
private readonly DecoratorCallTracker _tracker;

public TrackingMetadataAccessorDecorator(IMessageMetadataAccessor inner, DecoratorCallTracker tracker)
{
_inner = inner;
_tracker = tracker;
}

public IMessageMetadata? Metadata => _inner.Metadata;

public void SetData(MessageContext context)
{
_tracker.RecordSetData();
_inner.SetData(context);
}
}
}
Loading