From a84e5554fb3c8f5e3618c32a7fc4b1df02a6b939 Mon Sep 17 00:00:00 2001 From: Brant DeBow <125889545+brant-livefront@users.noreply.github.com> Date: Tue, 29 Jul 2025 11:22:21 -0400 Subject: [PATCH] [PM-17562] Refactor event integration methods / declarations in ServiceCollectionExtensions (#6118) * [PM-17562] Refactor event integration methods / declarations in ServiceCollectionExtensions * Refactored ServiceCollectionExtensions to use TryAdd and still launch unique listeneer services * Updated unit tests to match new generic format for Listeners * Fix method spacing * Update README to reflect new integration setup in ServiceCollectionExtensions * Move interfaces to I prefix; fix typo in subscription * Fix reference to IIntegrationListenerConfiguration --- .../HecListenerConfiguration.cs | 38 ++ .../IEventListenerConfiguration.cs | 8 + .../IIntegrationListenerConfiguration.cs | 18 + .../ListenerConfiguration.cs | 28 ++ .../RepositoryListenerConfiguration.cs | 17 + .../SlackListenerConfiguration.cs | 38 ++ .../WebhookListenerConfiguration.cs | 38 ++ .../AzureServiceBusEventListenerService.cs | 15 +- ...ureServiceBusIntegrationListenerService.cs | 22 +- .../EventIntegrations/README.md | 47 ++- .../RabbitMqEventListenerService.cs | 11 +- .../RabbitMqIntegrationListenerService.cs | 23 +- src/Core/Settings/GlobalSettings.cs | 3 +- .../Utilities/ServiceCollectionExtensions.cs | 385 +++++++++--------- .../TestListenerConfiguration.cs | 16 + ...zureServiceBusEventListenerServiceTests.cs | 35 +- ...rviceBusIntegrationListenerServiceTests.cs | 31 +- .../RabbitMqEventListenerServiceTests.cs | 29 +- ...RabbitMqIntegrationListenerServiceTests.cs | 24 +- 19 files changed, 512 insertions(+), 314 deletions(-) create mode 100644 src/Core/AdminConsole/Models/Data/EventIntegrations/HecListenerConfiguration.cs create mode 100644 src/Core/AdminConsole/Models/Data/EventIntegrations/IEventListenerConfiguration.cs create mode 100644 src/Core/AdminConsole/Models/Data/EventIntegrations/IIntegrationListenerConfiguration.cs create mode 100644 src/Core/AdminConsole/Models/Data/EventIntegrations/ListenerConfiguration.cs create mode 100644 src/Core/AdminConsole/Models/Data/EventIntegrations/RepositoryListenerConfiguration.cs create mode 100644 src/Core/AdminConsole/Models/Data/EventIntegrations/SlackListenerConfiguration.cs create mode 100644 src/Core/AdminConsole/Models/Data/EventIntegrations/WebhookListenerConfiguration.cs create mode 100644 test/Core.Test/AdminConsole/Models/Data/EventIntegrations/TestListenerConfiguration.cs diff --git a/src/Core/AdminConsole/Models/Data/EventIntegrations/HecListenerConfiguration.cs b/src/Core/AdminConsole/Models/Data/EventIntegrations/HecListenerConfiguration.cs new file mode 100644 index 0000000000..37a0d68beb --- /dev/null +++ b/src/Core/AdminConsole/Models/Data/EventIntegrations/HecListenerConfiguration.cs @@ -0,0 +1,38 @@ +using Bit.Core.Enums; +using Bit.Core.Settings; + +namespace Bit.Core.AdminConsole.Models.Data.EventIntegrations; + +public class HecListenerConfiguration(GlobalSettings globalSettings) + : ListenerConfiguration(globalSettings), IIntegrationListenerConfiguration +{ + public IntegrationType IntegrationType + { + get => IntegrationType.Hec; + } + + public string EventQueueName + { + get => _globalSettings.EventLogging.RabbitMq.HecEventsQueueName; + } + + public string IntegrationQueueName + { + get => _globalSettings.EventLogging.RabbitMq.HecIntegrationQueueName; + } + + public string IntegrationRetryQueueName + { + get => _globalSettings.EventLogging.RabbitMq.HecIntegrationRetryQueueName; + } + + public string EventSubscriptionName + { + get => _globalSettings.EventLogging.AzureServiceBus.HecEventSubscriptionName; + } + + public string IntegrationSubscriptionName + { + get => _globalSettings.EventLogging.AzureServiceBus.HecIntegrationSubscriptionName; + } +} diff --git a/src/Core/AdminConsole/Models/Data/EventIntegrations/IEventListenerConfiguration.cs b/src/Core/AdminConsole/Models/Data/EventIntegrations/IEventListenerConfiguration.cs new file mode 100644 index 0000000000..7b2dd1343e --- /dev/null +++ b/src/Core/AdminConsole/Models/Data/EventIntegrations/IEventListenerConfiguration.cs @@ -0,0 +1,8 @@ +namespace Bit.Core.AdminConsole.Models.Data.EventIntegrations; + +public interface IEventListenerConfiguration +{ + public string EventQueueName { get; } + public string EventSubscriptionName { get; } + public string EventTopicName { get; } +} diff --git a/src/Core/AdminConsole/Models/Data/EventIntegrations/IIntegrationListenerConfiguration.cs b/src/Core/AdminConsole/Models/Data/EventIntegrations/IIntegrationListenerConfiguration.cs new file mode 100644 index 0000000000..322a1cd952 --- /dev/null +++ b/src/Core/AdminConsole/Models/Data/EventIntegrations/IIntegrationListenerConfiguration.cs @@ -0,0 +1,18 @@ +using Bit.Core.Enums; + +namespace Bit.Core.AdminConsole.Models.Data.EventIntegrations; + +public interface IIntegrationListenerConfiguration : IEventListenerConfiguration +{ + public IntegrationType IntegrationType { get; } + public string IntegrationQueueName { get; } + public string IntegrationRetryQueueName { get; } + public string IntegrationSubscriptionName { get; } + public string IntegrationTopicName { get; } + public int MaxRetries { get; } + + public string RoutingKey + { + get => IntegrationType.ToRoutingKey(); + } +} diff --git a/src/Core/AdminConsole/Models/Data/EventIntegrations/ListenerConfiguration.cs b/src/Core/AdminConsole/Models/Data/EventIntegrations/ListenerConfiguration.cs new file mode 100644 index 0000000000..662bb8241e --- /dev/null +++ b/src/Core/AdminConsole/Models/Data/EventIntegrations/ListenerConfiguration.cs @@ -0,0 +1,28 @@ +using Bit.Core.Settings; + +namespace Bit.Core.AdminConsole.Models.Data.EventIntegrations; + +public abstract class ListenerConfiguration +{ + protected GlobalSettings _globalSettings; + + public ListenerConfiguration(GlobalSettings globalSettings) + { + _globalSettings = globalSettings; + } + + public int MaxRetries + { + get => _globalSettings.EventLogging.MaxRetries; + } + + public string EventTopicName + { + get => _globalSettings.EventLogging.AzureServiceBus.EventTopicName; + } + + public string IntegrationTopicName + { + get => _globalSettings.EventLogging.AzureServiceBus.IntegrationTopicName; + } +} diff --git a/src/Core/AdminConsole/Models/Data/EventIntegrations/RepositoryListenerConfiguration.cs b/src/Core/AdminConsole/Models/Data/EventIntegrations/RepositoryListenerConfiguration.cs new file mode 100644 index 0000000000..118b3a17fe --- /dev/null +++ b/src/Core/AdminConsole/Models/Data/EventIntegrations/RepositoryListenerConfiguration.cs @@ -0,0 +1,17 @@ +using Bit.Core.Settings; + +namespace Bit.Core.AdminConsole.Models.Data.EventIntegrations; + +public class RepositoryListenerConfiguration(GlobalSettings globalSettings) + : ListenerConfiguration(globalSettings), IEventListenerConfiguration +{ + public string EventQueueName + { + get => _globalSettings.EventLogging.RabbitMq.EventRepositoryQueueName; + } + + public string EventSubscriptionName + { + get => _globalSettings.EventLogging.AzureServiceBus.EventRepositorySubscriptionName; + } +} diff --git a/src/Core/AdminConsole/Models/Data/EventIntegrations/SlackListenerConfiguration.cs b/src/Core/AdminConsole/Models/Data/EventIntegrations/SlackListenerConfiguration.cs new file mode 100644 index 0000000000..7dd834f51e --- /dev/null +++ b/src/Core/AdminConsole/Models/Data/EventIntegrations/SlackListenerConfiguration.cs @@ -0,0 +1,38 @@ +using Bit.Core.Enums; +using Bit.Core.Settings; + +namespace Bit.Core.AdminConsole.Models.Data.EventIntegrations; + +public class SlackListenerConfiguration(GlobalSettings globalSettings) : + ListenerConfiguration(globalSettings), IIntegrationListenerConfiguration +{ + public IntegrationType IntegrationType + { + get => IntegrationType.Slack; + } + + public string EventQueueName + { + get => _globalSettings.EventLogging.RabbitMq.SlackEventsQueueName; + } + + public string IntegrationQueueName + { + get => _globalSettings.EventLogging.RabbitMq.SlackIntegrationQueueName; + } + + public string IntegrationRetryQueueName + { + get => _globalSettings.EventLogging.RabbitMq.SlackIntegrationRetryQueueName; + } + + public string EventSubscriptionName + { + get => _globalSettings.EventLogging.AzureServiceBus.SlackEventSubscriptionName; + } + + public string IntegrationSubscriptionName + { + get => _globalSettings.EventLogging.AzureServiceBus.SlackIntegrationSubscriptionName; + } +} diff --git a/src/Core/AdminConsole/Models/Data/EventIntegrations/WebhookListenerConfiguration.cs b/src/Core/AdminConsole/Models/Data/EventIntegrations/WebhookListenerConfiguration.cs new file mode 100644 index 0000000000..9d5bf811c7 --- /dev/null +++ b/src/Core/AdminConsole/Models/Data/EventIntegrations/WebhookListenerConfiguration.cs @@ -0,0 +1,38 @@ +using Bit.Core.Enums; +using Bit.Core.Settings; + +namespace Bit.Core.AdminConsole.Models.Data.EventIntegrations; + +public class WebhookListenerConfiguration(GlobalSettings globalSettings) + : ListenerConfiguration(globalSettings), IIntegrationListenerConfiguration +{ + public IntegrationType IntegrationType + { + get => IntegrationType.Webhook; + } + + public string EventQueueName + { + get => _globalSettings.EventLogging.RabbitMq.WebhookEventsQueueName; + } + + public string IntegrationQueueName + { + get => _globalSettings.EventLogging.RabbitMq.WebhookIntegrationQueueName; + } + + public string IntegrationRetryQueueName + { + get => _globalSettings.EventLogging.RabbitMq.WebhookIntegrationRetryQueueName; + } + + public string EventSubscriptionName + { + get => _globalSettings.EventLogging.AzureServiceBus.WebhookEventSubscriptionName; + } + + public string IntegrationSubscriptionName + { + get => _globalSettings.EventLogging.AzureServiceBus.WebhookIntegrationSubscriptionName; + } +} diff --git a/src/Core/AdminConsole/Services/Implementations/EventIntegrations/AzureServiceBusEventListenerService.cs b/src/Core/AdminConsole/Services/Implementations/EventIntegrations/AzureServiceBusEventListenerService.cs index ffa148fc08..a4b83b8806 100644 --- a/src/Core/AdminConsole/Services/Implementations/EventIntegrations/AzureServiceBusEventListenerService.cs +++ b/src/Core/AdminConsole/Services/Implementations/EventIntegrations/AzureServiceBusEventListenerService.cs @@ -2,27 +2,26 @@ using System.Text; using Azure.Messaging.ServiceBus; -using Bit.Core.Settings; +using Bit.Core.AdminConsole.Models.Data.EventIntegrations; using Microsoft.Extensions.Logging; namespace Bit.Core.Services; -public class AzureServiceBusEventListenerService : EventLoggingListenerService +public class AzureServiceBusEventListenerService : EventLoggingListenerService + where TConfiguration : IEventListenerConfiguration { private readonly ServiceBusProcessor _processor; public AzureServiceBusEventListenerService( + TConfiguration configuration, IEventMessageHandler handler, IAzureServiceBusService serviceBusService, - string subscriptionName, - GlobalSettings globalSettings, - ILogger logger) : base(handler, logger) + ILogger> logger) : base(handler, logger) { _processor = serviceBusService.CreateProcessor( - globalSettings.EventLogging.AzureServiceBus.EventTopicName, - subscriptionName, + topicName: configuration.EventTopicName, + subscriptionName: configuration.EventSubscriptionName, new ServiceBusProcessorOptions()); - _logger = logger; } protected override async Task ExecuteAsync(CancellationToken cancellationToken) diff --git a/src/Core/AdminConsole/Services/Implementations/EventIntegrations/AzureServiceBusIntegrationListenerService.cs b/src/Core/AdminConsole/Services/Implementations/EventIntegrations/AzureServiceBusIntegrationListenerService.cs index 55a39ec774..6db811efd9 100644 --- a/src/Core/AdminConsole/Services/Implementations/EventIntegrations/AzureServiceBusIntegrationListenerService.cs +++ b/src/Core/AdminConsole/Services/Implementations/EventIntegrations/AzureServiceBusIntegrationListenerService.cs @@ -1,32 +1,36 @@ #nullable enable using Azure.Messaging.ServiceBus; +using Bit.Core.AdminConsole.Models.Data.EventIntegrations; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace Bit.Core.Services; -public class AzureServiceBusIntegrationListenerService : BackgroundService +public class AzureServiceBusIntegrationListenerService : BackgroundService + where TConfiguration : IIntegrationListenerConfiguration { private readonly int _maxRetries; private readonly IAzureServiceBusService _serviceBusService; private readonly IIntegrationHandler _handler; private readonly ServiceBusProcessor _processor; - private readonly ILogger _logger; + private readonly ILogger> _logger; - public AzureServiceBusIntegrationListenerService(IIntegrationHandler handler, - string topicName, - string subscriptionName, - int maxRetries, + public AzureServiceBusIntegrationListenerService( + TConfiguration configuration, + IIntegrationHandler handler, IAzureServiceBusService serviceBusService, - ILogger logger) + ILogger> logger) { _handler = handler; _logger = logger; - _maxRetries = maxRetries; + _maxRetries = configuration.MaxRetries; _serviceBusService = serviceBusService; - _processor = _serviceBusService.CreateProcessor(topicName, subscriptionName, new ServiceBusProcessorOptions()); + _processor = _serviceBusService.CreateProcessor( + topicName: configuration.IntegrationTopicName, + subscriptionName: configuration.IntegrationSubscriptionName, + options: new ServiceBusProcessorOptions()); } protected override async Task ExecuteAsync(CancellationToken cancellationToken) diff --git a/src/Core/AdminConsole/Services/Implementations/EventIntegrations/README.md b/src/Core/AdminConsole/Services/Implementations/EventIntegrations/README.md index f48dee8aad..83b59cdec1 100644 --- a/src/Core/AdminConsole/Services/Implementations/EventIntegrations/README.md +++ b/src/Core/AdminConsole/Services/Implementations/EventIntegrations/README.md @@ -399,35 +399,44 @@ These names added here are what must match the values provided in the secrets or in Global Settings. This must be in place (and the local ASB emulator restarted) before you can use any code locally that accesses ASB resources. +## ListenerConfiguration + +New integrations will need their own subclass of `ListenerConfiguration` which also conforms to +`IIntegrationListenerConfiguration`. This class provides a way of accessing the previously configured +RabbitMQ queues and ASB subscriptions by referring to the values created in `GlobalSettings`. This new +listener configuration will be used to type the listener and provide the means to access the necessary +configurations for the integration. + ## ServiceCollectionExtensions + In our `ServiceCollectionExtensions`, we pull all the above pieces together to start listeners on each message -tier with handlers to process the integration. There are a number of helper methods in here to make this simple -to add a new integration - one call per platform. +tier with handlers to process the integration. -Also note that if an integration needs a custom singleton / service defined, the add listeners method is a -good place to set that up. For instance, `SlackIntegrationHandler` needs a `SlackService`, so the singleton -declaration is right above the add integration method for slack. Same thing for webhooks when it comes to -defining a custom HttpClient by name. +The core method for all event integration setup is `AddEventIntegrationServices`. This method is called by +both of the add listeners methods, which ensures that we have one common place to set up cross-messaging-platform +dependencies and integrations. For instance, `SlackIntegrationHandler` needs a `SlackService`, so +`AddEventIntegrationServices` has a call to `AddSlackService`. Same thing for webhooks when it +comes to defining a custom HttpClient by name. + +1. In `AddEventIntegrationServices` create the listener configuration: -1. In `AddRabbitMqListeners` add the integration: ``` csharp - services.AddRabbitMqIntegration( - globalSettings.EventLogging.RabbitMq.ExampleEventsQueueName, - globalSettings.EventLogging.RabbitMq.ExampleIntegrationQueueName, - globalSettings.EventLogging.RabbitMq.ExampleIntegrationRetryQueueName, - globalSettings.EventLogging.RabbitMq.MaxRetries, - IntegrationType.Example); + var exampleConfiguration = new ExampleListenerConfiguration(globalSettings); ``` -2. In `AddAzureServiceBusListeners` add the integration: +2. Add the integration to both the RabbitMQ and ASB specific declarations: + ``` csharp -services.AddAzureServiceBusIntegration( - eventSubscriptionName: globalSettings.EventLogging.AzureServiceBus.ExampleEventSubscriptionName, - integrationSubscriptionName: globalSettings.EventLogging.AzureServiceBus.ExampleIntegrationSubscriptionName, - integrationType: IntegrationType.Example, - globalSettings: globalSettings); + services.AddRabbitMqIntegration(exampleConfiguration); ``` +and + +``` csharp + services.AddAzureServiceBusIntegration(exampleConfiguration); +``` + + # Deploying a new integration ## RabbitMQ diff --git a/src/Core/AdminConsole/Services/Implementations/EventIntegrations/RabbitMqEventListenerService.cs b/src/Core/AdminConsole/Services/Implementations/EventIntegrations/RabbitMqEventListenerService.cs index bc2329930d..09ce4ce767 100644 --- a/src/Core/AdminConsole/Services/Implementations/EventIntegrations/RabbitMqEventListenerService.cs +++ b/src/Core/AdminConsole/Services/Implementations/EventIntegrations/RabbitMqEventListenerService.cs @@ -1,13 +1,15 @@ #nullable enable using System.Text; +using Bit.Core.AdminConsole.Models.Data.EventIntegrations; using Microsoft.Extensions.Logging; using RabbitMQ.Client; using RabbitMQ.Client.Events; namespace Bit.Core.Services; -public class RabbitMqEventListenerService : EventLoggingListenerService +public class RabbitMqEventListenerService : EventLoggingListenerService + where TConfiguration : IEventListenerConfiguration { private readonly Lazy> _lazyChannel; private readonly string _queueName; @@ -15,12 +17,11 @@ public class RabbitMqEventListenerService : EventLoggingListenerService public RabbitMqEventListenerService( IEventMessageHandler handler, - string queueName, + TConfiguration configuration, IRabbitMqService rabbitMqService, - ILogger logger) : base(handler, logger) + ILogger> logger) : base(handler, logger) { - _logger = logger; - _queueName = queueName; + _queueName = configuration.EventQueueName; _rabbitMqService = rabbitMqService; _lazyChannel = new Lazy>(() => _rabbitMqService.CreateChannelAsync()); } diff --git a/src/Core/AdminConsole/Services/Implementations/EventIntegrations/RabbitMqIntegrationListenerService.cs b/src/Core/AdminConsole/Services/Implementations/EventIntegrations/RabbitMqIntegrationListenerService.cs index db6a7f9510..e8f368fbe5 100644 --- a/src/Core/AdminConsole/Services/Implementations/EventIntegrations/RabbitMqIntegrationListenerService.cs +++ b/src/Core/AdminConsole/Services/Implementations/EventIntegrations/RabbitMqIntegrationListenerService.cs @@ -10,7 +10,8 @@ using RabbitMQ.Client.Events; namespace Bit.Core.Services; -public class RabbitMqIntegrationListenerService : BackgroundService +public class RabbitMqIntegrationListenerService : BackgroundService + where TConfiguration : IIntegrationListenerConfiguration { private readonly int _maxRetries; private readonly string _queueName; @@ -19,26 +20,24 @@ public class RabbitMqIntegrationListenerService : BackgroundService private readonly IIntegrationHandler _handler; private readonly Lazy> _lazyChannel; private readonly IRabbitMqService _rabbitMqService; - private readonly ILogger _logger; + private readonly ILogger> _logger; private readonly TimeProvider _timeProvider; - public RabbitMqIntegrationListenerService(IIntegrationHandler handler, - string routingKey, - string queueName, - string retryQueueName, - int maxRetries, + public RabbitMqIntegrationListenerService( + IIntegrationHandler handler, + TConfiguration configuration, IRabbitMqService rabbitMqService, - ILogger logger, + ILogger> logger, TimeProvider timeProvider) { _handler = handler; - _routingKey = routingKey; - _retryQueueName = retryQueueName; - _queueName = queueName; + _maxRetries = configuration.MaxRetries; + _routingKey = configuration.RoutingKey; + _retryQueueName = configuration.IntegrationRetryQueueName; + _queueName = configuration.IntegrationQueueName; _rabbitMqService = rabbitMqService; _logger = logger; _timeProvider = timeProvider; - _maxRetries = maxRetries; _lazyChannel = new Lazy>(() => _rabbitMqService.CreateChannelAsync()); } diff --git a/src/Core/Settings/GlobalSettings.cs b/src/Core/Settings/GlobalSettings.cs index e49ef4a7f2..2d9301e451 100644 --- a/src/Core/Settings/GlobalSettings.cs +++ b/src/Core/Settings/GlobalSettings.cs @@ -288,6 +288,7 @@ public class GlobalSettings : IGlobalSettings public AzureServiceBusSettings AzureServiceBus { get; set; } = new AzureServiceBusSettings(); public RabbitMqSettings RabbitMq { get; set; } = new RabbitMqSettings(); public int IntegrationCacheRefreshIntervalMinutes { get; set; } = 10; + public int MaxRetries { get; set; } = 3; public class AzureServiceBusSettings { @@ -295,7 +296,6 @@ public class GlobalSettings : IGlobalSettings private string _eventTopicName; private string _integrationTopicName; - public int MaxRetries { get; set; } = 3; public virtual string EventRepositorySubscriptionName { get; set; } = "events-write-subscription"; public virtual string SlackEventSubscriptionName { get; set; } = "events-slack-subscription"; public virtual string SlackIntegrationSubscriptionName { get; set; } = "integration-slack-subscription"; @@ -331,7 +331,6 @@ public class GlobalSettings : IGlobalSettings private string _eventExchangeName; private string _integrationExchangeName; - public int MaxRetries { get; set; } = 3; public int RetryTiming { get; set; } = 30000; // 30s public bool UseDelayPlugin { get; set; } = false; public virtual string EventRepositoryQueueName { get; set; } = "events-write-queue"; diff --git a/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs b/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs index 0c3f0cbca1..48d3304ab0 100644 --- a/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs +++ b/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs @@ -373,7 +373,6 @@ public static class ServiceCollectionExtensions services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); } public static IdentityBuilder AddCustomIdentityServices( @@ -550,198 +549,57 @@ public static class ServiceCollectionExtensions { if (!globalSettings.SelfHosted && CoreHelpers.SettingHasValue(globalSettings.Events.ConnectionString)) { - services.AddKeyedSingleton("storage"); + services.TryAddKeyedSingleton("storage"); if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.ConnectionString) && CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.EventTopicName)) { - services.AddSingleton(); - services.AddKeyedSingleton("broadcast"); + services.TryAddSingleton(); + services.TryAddKeyedSingleton("broadcast"); } else { - services.AddKeyedSingleton("broadcast"); + services.TryAddKeyedSingleton("broadcast"); } } else if (globalSettings.SelfHosted) { - services.AddKeyedSingleton("storage"); + services.TryAddKeyedSingleton("storage"); if (IsRabbitMqEnabled(globalSettings)) { - services.AddSingleton(); - services.AddKeyedSingleton("broadcast"); + services.TryAddSingleton(); + services.TryAddKeyedSingleton("broadcast"); } else { - services.AddKeyedSingleton("broadcast"); + services.TryAddKeyedSingleton("broadcast"); } } else { - services.AddKeyedSingleton("storage"); - services.AddKeyedSingleton("broadcast"); + services.TryAddKeyedSingleton("storage"); + services.TryAddKeyedSingleton("broadcast"); } - services.AddScoped(); - return services; - } - - private static IServiceCollection AddAzureServiceBusEventRepositoryListener(this IServiceCollection services, GlobalSettings globalSettings) - { - services.AddSingleton(); - services.AddSingleton(); - services.AddKeyedSingleton("persistent"); - services.AddSingleton(provider => - new AzureServiceBusEventListenerService( - handler: provider.GetRequiredService(), - serviceBusService: provider.GetRequiredService(), - subscriptionName: globalSettings.EventLogging.AzureServiceBus.EventRepositorySubscriptionName, - globalSettings: globalSettings, - logger: provider.GetRequiredService>() - ) - ); - - return services; - } - - private static IServiceCollection AddAzureServiceBusIntegration( - this IServiceCollection services, - string eventSubscriptionName, - string integrationSubscriptionName, - IntegrationType integrationType, - GlobalSettings globalSettings) - where TConfig : class - where THandler : class, IIntegrationHandler - { - var routingKey = integrationType.ToRoutingKey(); - - services.AddKeyedSingleton(routingKey, (provider, _) => - new EventIntegrationHandler( - integrationType, - provider.GetRequiredService(), - provider.GetRequiredService(), - provider.GetRequiredService(), - provider.GetRequiredService(), - provider.GetRequiredService(), - provider.GetRequiredService>>())); - - services.AddSingleton(provider => - new AzureServiceBusEventListenerService( - handler: provider.GetRequiredKeyedService(routingKey), - serviceBusService: provider.GetRequiredService(), - subscriptionName: eventSubscriptionName, - globalSettings: globalSettings, - logger: provider.GetRequiredService>() - ) - ); - - services.AddSingleton, THandler>(); - services.AddSingleton(provider => - new AzureServiceBusIntegrationListenerService( - handler: provider.GetRequiredService>(), - topicName: globalSettings.EventLogging.AzureServiceBus.IntegrationTopicName, - subscriptionName: integrationSubscriptionName, - maxRetries: globalSettings.EventLogging.AzureServiceBus.MaxRetries, - serviceBusService: provider.GetRequiredService(), - logger: provider.GetRequiredService>())); - + services.TryAddScoped(); return services; } public static IServiceCollection AddAzureServiceBusListeners(this IServiceCollection services, GlobalSettings globalSettings) { - if (!CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.ConnectionString) || - !CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.EventTopicName)) + if (!IsAzureServiceBusEnabled(globalSettings)) + { return services; + } - services.AddSingleton(); - services.AddSingleton(provider => - provider.GetRequiredService()); - services.AddHostedService(provider => provider.GetRequiredService()); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddAzureServiceBusEventRepositoryListener(globalSettings); + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddKeyedSingleton("persistent"); + services.TryAddSingleton(); - services.AddSlackService(globalSettings); - services.AddAzureServiceBusIntegration( - eventSubscriptionName: globalSettings.EventLogging.AzureServiceBus.SlackEventSubscriptionName, - integrationSubscriptionName: globalSettings.EventLogging.AzureServiceBus.SlackIntegrationSubscriptionName, - integrationType: IntegrationType.Slack, - globalSettings: globalSettings); - - services.TryAddSingleton(TimeProvider.System); - services.AddHttpClient(WebhookIntegrationHandler.HttpClientName); - services.AddAzureServiceBusIntegration( - eventSubscriptionName: globalSettings.EventLogging.AzureServiceBus.WebhookEventSubscriptionName, - integrationSubscriptionName: globalSettings.EventLogging.AzureServiceBus.WebhookIntegrationSubscriptionName, - integrationType: IntegrationType.Webhook, - globalSettings: globalSettings); - - services.AddAzureServiceBusIntegration( - eventSubscriptionName: globalSettings.EventLogging.AzureServiceBus.HecEventSubscriptionName, - integrationSubscriptionName: globalSettings.EventLogging.AzureServiceBus.HecIntegrationSubscriptionName, - integrationType: IntegrationType.Hec, - globalSettings: globalSettings); - - return services; - } - - private static IServiceCollection AddRabbitMqEventRepositoryListener(this IServiceCollection services, GlobalSettings globalSettings) - { - services.AddSingleton(); - services.AddKeyedSingleton("persistent"); - - services.AddSingleton(provider => - new RabbitMqEventListenerService( - provider.GetRequiredService(), - globalSettings.EventLogging.RabbitMq.EventRepositoryQueueName, - provider.GetRequiredService(), - provider.GetRequiredService>())); - - return services; - } - - private static IServiceCollection AddRabbitMqIntegration(this IServiceCollection services, - string eventQueueName, - string integrationQueueName, - string integrationRetryQueueName, - int maxRetries, - IntegrationType integrationType) - where TConfig : class - where THandler : class, IIntegrationHandler - { - var routingKey = integrationType.ToRoutingKey(); - - services.AddKeyedSingleton(routingKey, (provider, _) => - new EventIntegrationHandler( - integrationType, - provider.GetRequiredService(), - provider.GetRequiredService(), - provider.GetRequiredService(), - provider.GetRequiredService(), - provider.GetRequiredService(), - provider.GetRequiredService>>())); - - services.AddSingleton(provider => - new RabbitMqEventListenerService( - provider.GetRequiredKeyedService(routingKey), - eventQueueName, - provider.GetRequiredService(), - provider.GetRequiredService>())); - - services.AddSingleton, THandler>(); - services.AddSingleton(provider => - new RabbitMqIntegrationListenerService( - handler: provider.GetRequiredService>(), - routingKey: routingKey, - queueName: integrationQueueName, - retryQueueName: integrationRetryQueueName, - maxRetries: maxRetries, - rabbitMqService: provider.GetRequiredService(), - logger: provider.GetRequiredService>(), - timeProvider: provider.GetRequiredService())); + services.AddEventIntegrationServices(globalSettings); return services; } @@ -753,49 +611,15 @@ public static class ServiceCollectionExtensions return services; } - services.AddSingleton(); - services.AddSingleton(provider => - provider.GetRequiredService()); - services.AddHostedService(provider => provider.GetRequiredService()); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddRabbitMqEventRepositoryListener(globalSettings); + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); - services.AddSlackService(globalSettings); - services.AddRabbitMqIntegration( - globalSettings.EventLogging.RabbitMq.SlackEventsQueueName, - globalSettings.EventLogging.RabbitMq.SlackIntegrationQueueName, - globalSettings.EventLogging.RabbitMq.SlackIntegrationRetryQueueName, - globalSettings.EventLogging.RabbitMq.MaxRetries, - IntegrationType.Slack); - - services.AddHttpClient(WebhookIntegrationHandler.HttpClientName); - services.AddRabbitMqIntegration( - globalSettings.EventLogging.RabbitMq.WebhookEventsQueueName, - globalSettings.EventLogging.RabbitMq.WebhookIntegrationQueueName, - globalSettings.EventLogging.RabbitMq.WebhookIntegrationRetryQueueName, - globalSettings.EventLogging.RabbitMq.MaxRetries, - IntegrationType.Webhook); - - services.AddRabbitMqIntegration( - globalSettings.EventLogging.RabbitMq.HecEventsQueueName, - globalSettings.EventLogging.RabbitMq.HecIntegrationQueueName, - globalSettings.EventLogging.RabbitMq.HecIntegrationRetryQueueName, - globalSettings.EventLogging.RabbitMq.MaxRetries, - IntegrationType.Hec); + services.AddEventIntegrationServices(globalSettings); return services; } - private static bool IsRabbitMqEnabled(GlobalSettings settings) - { - return CoreHelpers.SettingHasValue(settings.EventLogging.RabbitMq.HostName) && - CoreHelpers.SettingHasValue(settings.EventLogging.RabbitMq.Username) && - CoreHelpers.SettingHasValue(settings.EventLogging.RabbitMq.Password) && - CoreHelpers.SettingHasValue(settings.EventLogging.RabbitMq.EventExchangeName); - } - public static IServiceCollection AddSlackService(this IServiceCollection services, GlobalSettings globalSettings) { if (CoreHelpers.SettingHasValue(globalSettings.Slack.ClientId) && @@ -803,11 +627,11 @@ public static class ServiceCollectionExtensions CoreHelpers.SettingHasValue(globalSettings.Slack.Scopes)) { services.AddHttpClient(SlackService.HttpClientName); - services.AddSingleton(); + services.TryAddSingleton(); } else { - services.AddSingleton(); + services.TryAddSingleton(); } return services; @@ -1043,4 +867,161 @@ public static class ServiceCollectionExtensions return (provider, connectionString); } + + private static IServiceCollection AddAzureServiceBusIntegration(this IServiceCollection services, + TListenerConfig listenerConfiguration) + where TConfig : class + where TListenerConfig : IIntegrationListenerConfiguration + { + services.TryAddKeyedSingleton(serviceKey: listenerConfiguration.RoutingKey, implementationFactory: (provider, _) => + new EventIntegrationHandler( + integrationType: listenerConfiguration.IntegrationType, + eventIntegrationPublisher: provider.GetRequiredService(), + integrationFilterService: provider.GetRequiredService(), + configurationCache: provider.GetRequiredService(), + userRepository: provider.GetRequiredService(), + organizationRepository: provider.GetRequiredService(), + logger: provider.GetRequiredService>>() + ) + ); + services.TryAddEnumerable(ServiceDescriptor.Singleton>(provider => + new AzureServiceBusEventListenerService( + configuration: listenerConfiguration, + handler: provider.GetRequiredKeyedService(serviceKey: listenerConfiguration.RoutingKey), + serviceBusService: provider.GetRequiredService(), + logger: provider.GetRequiredService>>() + ) + ) + ); + services.TryAddEnumerable(ServiceDescriptor.Singleton>(provider => + new AzureServiceBusIntegrationListenerService( + configuration: listenerConfiguration, + handler: provider.GetRequiredService>(), + serviceBusService: provider.GetRequiredService(), + logger: provider.GetRequiredService>>() + ) + ) + ); + + return services; + } + + private static IServiceCollection AddEventIntegrationServices(this IServiceCollection services, + GlobalSettings globalSettings) + { + // Add common services + services.TryAddSingleton(); + services.TryAddSingleton(provider => + provider.GetRequiredService()); + services.AddHostedService(provider => provider.GetRequiredService()); + services.TryAddSingleton(); + services.TryAddKeyedSingleton("persistent"); + + // Add services in support of handlers + services.AddSlackService(globalSettings); + services.TryAddSingleton(TimeProvider.System); + services.AddHttpClient(WebhookIntegrationHandler.HttpClientName); + + // Add integration handlers + services.TryAddSingleton, SlackIntegrationHandler>(); + services.TryAddSingleton, WebhookIntegrationHandler>(); + + var repositoryConfiguration = new RepositoryListenerConfiguration(globalSettings); + var slackConfiguration = new SlackListenerConfiguration(globalSettings); + var webhookConfiguration = new WebhookListenerConfiguration(globalSettings); + var hecConfiguration = new HecListenerConfiguration(globalSettings); + + if (IsRabbitMqEnabled(globalSettings)) + { + services.TryAddEnumerable(ServiceDescriptor.Singleton>(provider => + new RabbitMqEventListenerService( + handler: provider.GetRequiredService(), + configuration: repositoryConfiguration, + rabbitMqService: provider.GetRequiredService(), + logger: provider.GetRequiredService>>() + ) + ) + ); + services.AddRabbitMqIntegration(slackConfiguration); + services.AddRabbitMqIntegration(webhookConfiguration); + services.AddRabbitMqIntegration(hecConfiguration); + } + + if (IsAzureServiceBusEnabled(globalSettings)) + { + services.TryAddEnumerable(ServiceDescriptor.Singleton>(provider => + new AzureServiceBusEventListenerService( + configuration: repositoryConfiguration, + handler: provider.GetRequiredService(), + serviceBusService: provider.GetRequiredService(), + logger: provider.GetRequiredService>>() + ) + ) + ); + services.AddAzureServiceBusIntegration(slackConfiguration); + services.AddAzureServiceBusIntegration(webhookConfiguration); + services.AddAzureServiceBusIntegration(hecConfiguration); + } + + return services; + } + + private static IServiceCollection AddRabbitMqIntegration(this IServiceCollection services, + TListenerConfig listenerConfiguration) + where TConfig : class + where TListenerConfig : IIntegrationListenerConfiguration + { + services.TryAddKeyedSingleton(serviceKey: listenerConfiguration.RoutingKey, implementationFactory: (provider, _) => + new EventIntegrationHandler( + integrationType: listenerConfiguration.IntegrationType, + eventIntegrationPublisher: provider.GetRequiredService(), + integrationFilterService: provider.GetRequiredService(), + configurationCache: provider.GetRequiredService(), + userRepository: provider.GetRequiredService(), + organizationRepository: provider.GetRequiredService(), + logger: provider.GetRequiredService>>() + ) + ); + services.TryAddEnumerable(ServiceDescriptor.Singleton>(provider => + new RabbitMqEventListenerService( + handler: provider.GetRequiredKeyedService(serviceKey: listenerConfiguration.RoutingKey), + configuration: listenerConfiguration, + rabbitMqService: provider.GetRequiredService(), + logger: provider.GetRequiredService>>() + ) + ) + ); + services.TryAddEnumerable(ServiceDescriptor.Singleton>(provider => + new RabbitMqIntegrationListenerService( + handler: provider.GetRequiredService>(), + configuration: listenerConfiguration, + rabbitMqService: provider.GetRequiredService(), + logger: provider.GetRequiredService>>(), + timeProvider: provider.GetRequiredService() + ) + ) + ); + + return services; + } + + private static bool IsAzureServiceBusEnabled(GlobalSettings settings) + { + return CoreHelpers.SettingHasValue(settings.EventLogging.AzureServiceBus.ConnectionString) && + CoreHelpers.SettingHasValue(settings.EventLogging.AzureServiceBus.EventTopicName); + } + + private static bool IsRabbitMqEnabled(GlobalSettings settings) + { + return CoreHelpers.SettingHasValue(settings.EventLogging.RabbitMq.HostName) && + CoreHelpers.SettingHasValue(settings.EventLogging.RabbitMq.Username) && + CoreHelpers.SettingHasValue(settings.EventLogging.RabbitMq.Password) && + CoreHelpers.SettingHasValue(settings.EventLogging.RabbitMq.EventExchangeName); + } } diff --git a/test/Core.Test/AdminConsole/Models/Data/EventIntegrations/TestListenerConfiguration.cs b/test/Core.Test/AdminConsole/Models/Data/EventIntegrations/TestListenerConfiguration.cs new file mode 100644 index 0000000000..b676cb44b9 --- /dev/null +++ b/test/Core.Test/AdminConsole/Models/Data/EventIntegrations/TestListenerConfiguration.cs @@ -0,0 +1,16 @@ +using Bit.Core.Enums; + +namespace Bit.Core.AdminConsole.Models.Data.EventIntegrations; + +public class TestListenerConfiguration : IIntegrationListenerConfiguration +{ + public string EventQueueName => "event_queue"; + public string EventSubscriptionName => "event_subscription"; + public string EventTopicName => "event_topic"; + public IntegrationType IntegrationType => IntegrationType.Webhook; + public string IntegrationQueueName => "integration_queue"; + public string IntegrationRetryQueueName => "integration_retry_queue"; + public string IntegrationSubscriptionName => "integration_subscription"; + public string IntegrationTopicName => "integration_topic"; + public int MaxRetries => 3; +} diff --git a/test/Core.Test/AdminConsole/Services/AzureServiceBusEventListenerServiceTests.cs b/test/Core.Test/AdminConsole/Services/AzureServiceBusEventListenerServiceTests.cs index 13704817ca..fb0adc2119 100644 --- a/test/Core.Test/AdminConsole/Services/AzureServiceBusEventListenerServiceTests.cs +++ b/test/Core.Test/AdminConsole/Services/AzureServiceBusEventListenerServiceTests.cs @@ -1,5 +1,6 @@ using System.Text.Json; using Azure.Messaging.ServiceBus; +using Bit.Core.AdminConsole.Models.Data.EventIntegrations; using Bit.Core.Models.Data; using Bit.Core.Services; using Bit.Test.Common.AutoFixture; @@ -14,20 +15,28 @@ namespace Bit.Core.Test.Services; [SutProviderCustomize] public class AzureServiceBusEventListenerServiceTests { - private readonly IEventMessageHandler _handler = Substitute.For(); - private readonly ILogger _logger = - Substitute.For>(); private const string _messageId = "messageId"; + private readonly TestListenerConfiguration _config = new(); - private SutProvider GetSutProvider() + private SutProvider> GetSutProvider() { - return new SutProvider() - .SetDependency(_handler) - .SetDependency(_logger) - .SetDependency("test-subscription", "subscriptionName") + return new SutProvider>() + .SetDependency(_config) .Create(); } + [Fact] + public void Constructor_CreatesProcessor() + { + var sutProvider = GetSutProvider(); + + sutProvider.GetDependency().Received(1).CreateProcessor( + Arg.Is(_config.EventTopicName), + Arg.Is(_config.EventSubscriptionName), + Arg.Any() + ); + } + [Theory, BitAutoData] public async Task ProcessErrorAsync_LogsError(ProcessErrorEventArgs args) { @@ -35,7 +44,7 @@ public class AzureServiceBusEventListenerServiceTests await sutProvider.Sut.ProcessErrorAsync(args); - _logger.Received(1).Log( + sutProvider.GetDependency>>().Received(1).Log( LogLevel.Error, Arg.Any(), Arg.Any(), @@ -49,7 +58,7 @@ public class AzureServiceBusEventListenerServiceTests var sutProvider = GetSutProvider(); await sutProvider.Sut.ProcessReceivedMessageAsync(string.Empty, _messageId); - _logger.Received(1).Log( + sutProvider.GetDependency>>().Received(1).Log( LogLevel.Error, Arg.Any(), Arg.Any(), @@ -63,7 +72,7 @@ public class AzureServiceBusEventListenerServiceTests var sutProvider = GetSutProvider(); await sutProvider.Sut.ProcessReceivedMessageAsync("{ Inavlid JSON }", _messageId); - _logger.Received(1).Log( + sutProvider.GetDependency>>().Received(1).Log( LogLevel.Error, Arg.Any(), Arg.Is(o => o.ToString().Contains("Invalid JSON")), @@ -80,7 +89,7 @@ public class AzureServiceBusEventListenerServiceTests _messageId ); - _logger.Received(1).Log( + sutProvider.GetDependency>>().Received(1).Log( LogLevel.Error, Arg.Any(), Arg.Any(), @@ -97,7 +106,7 @@ public class AzureServiceBusEventListenerServiceTests _messageId ); - _logger.Received(1).Log( + sutProvider.GetDependency>>().Received(1).Log( LogLevel.Error, Arg.Any(), Arg.Any(), diff --git a/test/Core.Test/AdminConsole/Services/AzureServiceBusIntegrationListenerServiceTests.cs b/test/Core.Test/AdminConsole/Services/AzureServiceBusIntegrationListenerServiceTests.cs index 740baec37e..f450863ebf 100644 --- a/test/Core.Test/AdminConsole/Services/AzureServiceBusIntegrationListenerServiceTests.cs +++ b/test/Core.Test/AdminConsole/Services/AzureServiceBusIntegrationListenerServiceTests.cs @@ -14,33 +14,38 @@ namespace Bit.Core.Test.Services; [SutProviderCustomize] public class AzureServiceBusIntegrationListenerServiceTests { - private const int _maxRetries = 3; - private const string _topicName = "test_topic"; - private const string _subscriptionName = "test_subscription"; private readonly IIntegrationHandler _handler = Substitute.For(); private readonly IAzureServiceBusService _serviceBusService = Substitute.For(); - private readonly ILogger _logger = - Substitute.For>(); + private readonly TestListenerConfiguration _config = new(); - private SutProvider GetSutProvider() + private SutProvider> GetSutProvider() { - return new SutProvider() + return new SutProvider>() + .SetDependency(_config) .SetDependency(_handler) .SetDependency(_serviceBusService) - .SetDependency(_topicName, "topicName") - .SetDependency(_subscriptionName, "subscriptionName") - .SetDependency(_maxRetries, "maxRetries") - .SetDependency(_logger) .Create(); } + [Fact] + public void Constructor_CreatesProcessor() + { + var sutProvider = GetSutProvider(); + + sutProvider.GetDependency().Received(1).CreateProcessor( + Arg.Is(_config.IntegrationTopicName), + Arg.Is(_config.IntegrationSubscriptionName), + Arg.Any() + ); + } + [Theory, BitAutoData] public async Task ProcessErrorAsync_LogsError(ProcessErrorEventArgs args) { var sutProvider = GetSutProvider(); await sutProvider.Sut.ProcessErrorAsync(args); - _logger.Received(1).Log( + sutProvider.GetDependency>>().Received(1).Log( LogLevel.Error, Arg.Any(), Arg.Any(), @@ -70,7 +75,7 @@ public class AzureServiceBusIntegrationListenerServiceTests public async Task HandleMessageAsync_FailureRetryableButTooManyRetries_PublishesToDeadLetterQueue(IntegrationMessage message) { var sutProvider = GetSutProvider(); - message.RetryCount = _maxRetries; + message.RetryCount = _config.MaxRetries; var result = new IntegrationHandlerResult(false, message); result.Retryable = true; diff --git a/test/Core.Test/AdminConsole/Services/RabbitMqEventListenerServiceTests.cs b/test/Core.Test/AdminConsole/Services/RabbitMqEventListenerServiceTests.cs index 8fd7e460be..cf1d8f6a0e 100644 --- a/test/Core.Test/AdminConsole/Services/RabbitMqEventListenerServiceTests.cs +++ b/test/Core.Test/AdminConsole/Services/RabbitMqEventListenerServiceTests.cs @@ -1,4 +1,5 @@ using System.Text.Json; +using Bit.Core.AdminConsole.Models.Data.EventIntegrations; using Bit.Core.Models.Data; using Bit.Core.Services; using Bit.Test.Common.AutoFixture; @@ -15,16 +16,12 @@ namespace Bit.Core.Test.Services; [SutProviderCustomize] public class RabbitMqEventListenerServiceTests { - private const string _queueName = "test_queue"; - private readonly IRabbitMqService _rabbitMqService = Substitute.For(); - private readonly ILogger _logger = Substitute.For>(); + private readonly TestListenerConfiguration _config = new(); - private SutProvider GetSutProvider() + private SutProvider> GetSutProvider() { - return new SutProvider() - .SetDependency(_rabbitMqService) - .SetDependency(_logger) - .SetDependency(_queueName, "queueName") + return new SutProvider>() + .SetDependency(_config) .Create(); } @@ -35,8 +32,8 @@ public class RabbitMqEventListenerServiceTests var cancellationToken = CancellationToken.None; await sutProvider.Sut.StartAsync(cancellationToken); - await _rabbitMqService.Received(1).CreateEventQueueAsync( - Arg.Is(_queueName), + await sutProvider.GetDependency().Received(1).CreateEventQueueAsync( + Arg.Is(_config.EventQueueName), Arg.Is(cancellationToken) ); } @@ -52,11 +49,11 @@ public class RabbitMqEventListenerServiceTests exchange: string.Empty, routingKey: string.Empty, new BasicProperties(), - body: new byte[0]); + body: Array.Empty()); await sutProvider.Sut.ProcessReceivedMessageAsync(eventArgs); - _logger.Received(1).Log( + sutProvider.GetDependency>>().Received(1).Log( LogLevel.Error, Arg.Any(), Arg.Any(), @@ -75,11 +72,11 @@ public class RabbitMqEventListenerServiceTests exchange: string.Empty, routingKey: string.Empty, new BasicProperties(), - body: JsonSerializer.SerializeToUtf8Bytes("{ Inavlid JSON")); + body: JsonSerializer.SerializeToUtf8Bytes("{ Invalid JSON")); await sutProvider.Sut.ProcessReceivedMessageAsync(eventArgs); - _logger.Received(1).Log( + sutProvider.GetDependency>>().Received(1).Log( LogLevel.Error, Arg.Any(), Arg.Is(o => o.ToString().Contains("Invalid JSON")), @@ -102,7 +99,7 @@ public class RabbitMqEventListenerServiceTests await sutProvider.Sut.ProcessReceivedMessageAsync(eventArgs); - _logger.Received(1).Log( + sutProvider.GetDependency>>().Received(1).Log( LogLevel.Error, Arg.Any(), Arg.Any(), @@ -125,7 +122,7 @@ public class RabbitMqEventListenerServiceTests await sutProvider.Sut.ProcessReceivedMessageAsync(eventArgs); - _logger.Received(1).Log( + sutProvider.GetDependency>>().Received(1).Log( LogLevel.Error, Arg.Any(), Arg.Any(), diff --git a/test/Core.Test/AdminConsole/Services/RabbitMqIntegrationListenerServiceTests.cs b/test/Core.Test/AdminConsole/Services/RabbitMqIntegrationListenerServiceTests.cs index bb3f211afa..df40e17dd4 100644 --- a/test/Core.Test/AdminConsole/Services/RabbitMqIntegrationListenerServiceTests.cs +++ b/test/Core.Test/AdminConsole/Services/RabbitMqIntegrationListenerServiceTests.cs @@ -15,23 +15,17 @@ namespace Bit.Core.Test.Services; [SutProviderCustomize] public class RabbitMqIntegrationListenerServiceTests { - private const int _maxRetries = 3; - private const string _queueName = "test_queue"; - private const string _retryQueueName = "test_queue_retry"; - private const string _routingKey = "test_routing_key"; private readonly DateTime _now = new DateTime(2014, 3, 2, 1, 0, 0, DateTimeKind.Utc); private readonly IIntegrationHandler _handler = Substitute.For(); private readonly IRabbitMqService _rabbitMqService = Substitute.For(); + private readonly TestListenerConfiguration _config = new(); - private SutProvider GetSutProvider() + private SutProvider> GetSutProvider() { - var sutProvider = new SutProvider() + var sutProvider = new SutProvider>() + .SetDependency(_config) .SetDependency(_handler) .SetDependency(_rabbitMqService) - .SetDependency(_queueName, "queueName") - .SetDependency(_retryQueueName, "retryQueueName") - .SetDependency(_routingKey, "routingKey") - .SetDependency(_maxRetries, "maxRetries") .WithFakeTimeProvider() .Create(); sutProvider.GetDependency().SetUtcNow(_now); @@ -46,10 +40,10 @@ public class RabbitMqIntegrationListenerServiceTests var cancellationToken = CancellationToken.None; await sutProvider.Sut.StartAsync(cancellationToken); - await _rabbitMqService.Received(1).CreateIntegrationQueuesAsync( - Arg.Is(_queueName), - Arg.Is(_retryQueueName), - Arg.Is(_routingKey), + await sutProvider.GetDependency().Received(1).CreateIntegrationQueuesAsync( + Arg.Is(_config.IntegrationQueueName), + Arg.Is(_config.IntegrationRetryQueueName), + Arg.Is(((IIntegrationListenerConfiguration)_config).RoutingKey), Arg.Is(cancellationToken) ); } @@ -101,7 +95,7 @@ public class RabbitMqIntegrationListenerServiceTests await sutProvider.Sut.StartAsync(cancellationToken); message.DelayUntilDate = null; - message.RetryCount = _maxRetries; + message.RetryCount = _config.MaxRetries; var eventArgs = new BasicDeliverEventArgs( consumerTag: string.Empty, deliveryTag: 0,