From 02d7692ae1bc7f3155f36550891dfe870e33799f Mon Sep 17 00:00:00 2001 From: Brant DeBow Date: Fri, 25 Apr 2025 08:14:20 -0400 Subject: [PATCH] [PM-17562] Use EventBasedOrganizationIntegrations feature flag to turn on/off event queue --- .../Implementations/EventRouteService.cs | 34 ++++++++++ src/Events/Startup.cs | 16 +++-- .../Utilities/ServiceCollectionExtensions.cs | 16 +++-- .../Services/EventRouteServiceTests.cs | 65 +++++++++++++++++++ 4 files changed, 121 insertions(+), 10 deletions(-) create mode 100644 src/Core/AdminConsole/Services/Implementations/EventRouteService.cs create mode 100644 test/Core.Test/AdminConsole/Services/EventRouteServiceTests.cs diff --git a/src/Core/AdminConsole/Services/Implementations/EventRouteService.cs b/src/Core/AdminConsole/Services/Implementations/EventRouteService.cs new file mode 100644 index 0000000000..a542e75a7b --- /dev/null +++ b/src/Core/AdminConsole/Services/Implementations/EventRouteService.cs @@ -0,0 +1,34 @@ +using Bit.Core.Models.Data; +using Microsoft.Extensions.DependencyInjection; + +namespace Bit.Core.Services; + +public class EventRouteService( + [FromKeyedServices("broadcast")] IEventWriteService broadcastEventWriteService, + [FromKeyedServices("storage")] IEventWriteService storageEventWriteService, + IFeatureService _featureService) : IEventWriteService +{ + public async Task CreateAsync(IEvent e) + { + if (_featureService.IsEnabled(FeatureFlagKeys.EventBasedOrganizationIntegrations)) + { + await broadcastEventWriteService.CreateAsync(e); + } + else + { + await storageEventWriteService.CreateAsync(e); + } + } + + public async Task CreateManyAsync(IEnumerable e) + { + if (_featureService.IsEnabled(FeatureFlagKeys.EventBasedOrganizationIntegrations)) + { + await broadcastEventWriteService.CreateManyAsync(e); + } + else + { + await storageEventWriteService.CreateManyAsync(e); + } + } +} diff --git a/src/Events/Startup.cs b/src/Events/Startup.cs index 34ffed4ee6..366b562485 100644 --- a/src/Events/Startup.cs +++ b/src/Events/Startup.cs @@ -62,33 +62,39 @@ public class Startup { services.AddSingleton(); } - services.AddScoped(); + if (!globalSettings.SelfHosted && CoreHelpers.SettingHasValue(globalSettings.Events.ConnectionString)) { + services.AddKeyedSingleton("storage"); + if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.ConnectionString) && CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.TopicName)) { - services.AddSingleton(); + services.AddKeyedSingleton("broadcast"); } else { - services.AddSingleton(); + services.AddKeyedSingleton("broadcast"); } } else { + services.AddKeyedSingleton("storage"); + if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.HostName) && CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.Username) && CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.Password) && CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.ExchangeName)) { - services.AddSingleton(); + services.AddKeyedSingleton("broadcast"); } else { - services.AddSingleton(); + services.AddKeyedSingleton("broadcast"); } } + services.AddScoped(); + services.AddScoped(); services.AddOptionality(); diff --git a/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs b/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs index 8d48fc86ef..26e5c7abaf 100644 --- a/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs +++ b/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs @@ -332,34 +332,40 @@ public static class ServiceCollectionExtensions if (!globalSettings.SelfHosted && CoreHelpers.SettingHasValue(globalSettings.Events.ConnectionString)) { + services.AddKeyedSingleton("storage"); + if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.ConnectionString) && CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.TopicName)) { - services.AddSingleton(); + services.AddKeyedSingleton("broadcast"); } else { - services.AddSingleton(); + services.AddKeyedSingleton("broadcast"); } } else if (globalSettings.SelfHosted) { + services.AddKeyedSingleton("storage"); + if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.HostName) && CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.Username) && CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.Password) && CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.ExchangeName)) { - services.AddSingleton(); + services.AddKeyedSingleton("broadcast"); } else { - services.AddSingleton(); + services.AddKeyedSingleton("broadcast"); } } else { - services.AddSingleton(); + services.AddKeyedSingleton("storage"); + services.AddKeyedSingleton("broadcast"); } + services.AddScoped(); if (CoreHelpers.SettingHasValue(globalSettings.Attachment.ConnectionString)) { diff --git a/test/Core.Test/AdminConsole/Services/EventRouteServiceTests.cs b/test/Core.Test/AdminConsole/Services/EventRouteServiceTests.cs new file mode 100644 index 0000000000..f593a4628b --- /dev/null +++ b/test/Core.Test/AdminConsole/Services/EventRouteServiceTests.cs @@ -0,0 +1,65 @@ +using Bit.Core.Models.Data; +using Bit.Core.Services; +using Bit.Test.Common.AutoFixture.Attributes; +using NSubstitute; +using Xunit; + +namespace Bit.Core.Test.Services; + +[SutProviderCustomize] +public class EventRouteServiceTests +{ + private readonly IEventWriteService _broadcastEventWriteService = Substitute.For(); + private readonly IEventWriteService _storageEventWriteService = Substitute.For(); + private readonly IFeatureService _featureService = Substitute.For(); + private readonly EventRouteService Subject; + + public EventRouteServiceTests() + { + Subject = new EventRouteService(_broadcastEventWriteService, _storageEventWriteService, _featureService); + } + + [Theory, BitAutoData] + public async Task CreateAsync_FlagDisabled_EventSentToStorageService(EventMessage eventMessage) + { + _featureService.IsEnabled(FeatureFlagKeys.EventBasedOrganizationIntegrations).Returns(false); + + await Subject.CreateAsync(eventMessage); + + _broadcastEventWriteService.DidNotReceiveWithAnyArgs().CreateAsync(Arg.Any()); + _storageEventWriteService.Received(1).CreateAsync(eventMessage); + } + + [Theory, BitAutoData] + public async Task CreateAsync_FlagEnabled_EventSentToBroadcastService(EventMessage eventMessage) + { + _featureService.IsEnabled(FeatureFlagKeys.EventBasedOrganizationIntegrations).Returns(true); + + await Subject.CreateAsync(eventMessage); + + _broadcastEventWriteService.Received(1).CreateAsync(eventMessage); + _storageEventWriteService.DidNotReceiveWithAnyArgs().CreateAsync(Arg.Any()); + } + + [Theory, BitAutoData] + public async Task CreateManyAsync_FlagDisabled_EventsSentToStorageService(IEnumerable eventMessages) + { + _featureService.IsEnabled(FeatureFlagKeys.EventBasedOrganizationIntegrations).Returns(false); + + await Subject.CreateManyAsync(eventMessages); + + _broadcastEventWriteService.DidNotReceiveWithAnyArgs().CreateManyAsync(Arg.Any>()); + _storageEventWriteService.Received(1).CreateManyAsync(eventMessages); + } + + [Theory, BitAutoData] + public async Task CreateManyAsync_FlagEnabled_EventsSentToBroadcastService(IEnumerable eventMessages) + { + _featureService.IsEnabled(FeatureFlagKeys.EventBasedOrganizationIntegrations).Returns(true); + + await Subject.CreateManyAsync(eventMessages); + + _broadcastEventWriteService.Received(1).CreateManyAsync(eventMessages); + _storageEventWriteService.DidNotReceiveWithAnyArgs().CreateManyAsync(Arg.Any>()); + } +}