< Summary

Information
Class: ClientManager.API.Services.EventForwarder
Assembly: ClientManager.API
File(s): /home/runner/work/ClientManagerDemo/ClientManagerDemo/src/ClientManager/ClientManager.API/Services/EventForwarder.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 50
Coverable lines: 50
Total lines: 74
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 10
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
ExecuteAsync()0%620%
SubscribeAsync()100%210%
<SubscribeAsync()100%210%
DiscoverEventTypes()0%2040%
DiscoverResponseInterfaces()0%2040%

File(s)

/home/runner/work/ClientManagerDemo/ClientManagerDemo/src/ClientManager/ClientManager.API/Services/EventForwarder.cs

#LineLine coverage
 1using System.Text;
 2using System.Text.Json;
 3using ClientManager.Shared.Contracts.Events;
 4using ClientManager.Shared.Messaging;
 5using Microsoft.AspNetCore.SignalR;
 6using RabbitMQ.Client;
 7using RabbitMQ.Client.Events;
 8
 9namespace ClientManager.API.Services;
 10
 011public class EventForwarder(IMessageBrokerFactory messageBrokerFactory, IHubContext<NotificationHub> hub, ILogger<EventF
 12{
 013    readonly IMessageBrokerFactory _messageBrokerFactory = messageBrokerFactory;
 014    readonly IHubContext<NotificationHub> _hub = hub;
 015    readonly ILogger<EventForwarder> _logger = logger;
 016    readonly IReadOnlyDictionary<string, Type> _eventTypesCache = DiscoverEventTypes().ToDictionary(t => t.Name, t => t)
 017    readonly IReadOnlyDictionary<string, Type> _interfaceCache = DiscoverResponseInterfaces().ToDictionary(t => t.Name, 
 18
 19    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
 20    {
 021        var eventTypes = new[] { "ClientCreated" };
 022        foreach (var eventType in eventTypes)
 023            await SubscribeAsync(eventType, stoppingToken);
 024    }
 25
 26    async Task SubscribeAsync(string queueName, CancellationToken cancellationToken)
 27    {
 028        var channel = await _messageBrokerFactory.GetConsumeChannelAsync(queueName);
 029        var consumer = new AsyncEventingBasicConsumer(channel);
 30
 031        consumer.ReceivedAsync += async (_, ea) =>
 032        {
 033            var body = Encoding.UTF8.GetString(ea.Body.ToArray());
 034            var eventType = _eventTypesCache[queueName];
 035
 036            var envelope = JsonSerializer.Deserialize<MessageEnvelope<object>>(body);
 037            ArgumentNullException.ThrowIfNull(envelope);
 038
 039            var jsonElement = (JsonElement)envelope.Message;
 040            var messageEvent = jsonElement.Deserialize(eventType);
 041
 042            var eventInterface = _interfaceCache[queueName];
 043            var responseDto = eventInterface.GetMethod("ToResponse")!.Invoke(messageEvent, null);
 044            _logger.LogInformation(
 045                "Forwarding event\n\t {@Event}\n\t [correlationId: {correlationId}, causationId: {causationId}]",
 046                responseDto,
 047                envelope.CorrelationId,
 048                envelope.CausationId
 049            );
 050            await _hub.Clients.All.SendAsync(responseDto!.GetType().Name, responseDto, cancellationToken);
 051            await channel.BasicAckAsync(ea.DeliveryTag, false, cancellationToken);
 052        };
 53
 054        await channel.BasicConsumeAsync(queueName, autoAck: false, consumer);
 055    }
 56
 57    static IEnumerable<Type> DiscoverEventTypes() =>
 058        AppDomain
 059            .CurrentDomain.GetAssemblies()
 060            .SelectMany(assembly => assembly.GetTypes())
 061            .Where(type => typeof(IEvent).IsAssignableFrom(type) && !type.IsAbstract && !type.IsInterface)
 062            .ToList();
 63
 64    static IEnumerable<Type> DiscoverResponseInterfaces() =>
 065        AppDomain
 066            .CurrentDomain.GetAssemblies()
 067            .SelectMany(assembly => assembly.GetTypes())
 068            .Where(type =>
 069                type.GetInterfaces().Any(iface => iface.IsGenericType && iface.GetGenericTypeDefinition() == typeof(IEve
 070                && !type.IsAbstract
 071                && !type.IsInterface
 072            )
 073            .ToList();
 74}