mirror of
https://github.com/bitwarden/server.git
synced 2025-12-10 00:42:07 -06:00
[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
This commit is contained in:
parent
43372b7168
commit
a84e5554fb
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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; }
|
||||||
|
}
|
||||||
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,27 +2,26 @@
|
|||||||
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Azure.Messaging.ServiceBus;
|
using Azure.Messaging.ServiceBus;
|
||||||
using Bit.Core.Settings;
|
using Bit.Core.AdminConsole.Models.Data.EventIntegrations;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Bit.Core.Services;
|
namespace Bit.Core.Services;
|
||||||
|
|
||||||
public class AzureServiceBusEventListenerService : EventLoggingListenerService
|
public class AzureServiceBusEventListenerService<TConfiguration> : EventLoggingListenerService
|
||||||
|
where TConfiguration : IEventListenerConfiguration
|
||||||
{
|
{
|
||||||
private readonly ServiceBusProcessor _processor;
|
private readonly ServiceBusProcessor _processor;
|
||||||
|
|
||||||
public AzureServiceBusEventListenerService(
|
public AzureServiceBusEventListenerService(
|
||||||
|
TConfiguration configuration,
|
||||||
IEventMessageHandler handler,
|
IEventMessageHandler handler,
|
||||||
IAzureServiceBusService serviceBusService,
|
IAzureServiceBusService serviceBusService,
|
||||||
string subscriptionName,
|
ILogger<AzureServiceBusEventListenerService<TConfiguration>> logger) : base(handler, logger)
|
||||||
GlobalSettings globalSettings,
|
|
||||||
ILogger<AzureServiceBusEventListenerService> logger) : base(handler, logger)
|
|
||||||
{
|
{
|
||||||
_processor = serviceBusService.CreateProcessor(
|
_processor = serviceBusService.CreateProcessor(
|
||||||
globalSettings.EventLogging.AzureServiceBus.EventTopicName,
|
topicName: configuration.EventTopicName,
|
||||||
subscriptionName,
|
subscriptionName: configuration.EventSubscriptionName,
|
||||||
new ServiceBusProcessorOptions());
|
new ServiceBusProcessorOptions());
|
||||||
_logger = logger;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
|
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
|
||||||
|
|||||||
@ -1,32 +1,36 @@
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
using Azure.Messaging.ServiceBus;
|
using Azure.Messaging.ServiceBus;
|
||||||
|
using Bit.Core.AdminConsole.Models.Data.EventIntegrations;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Bit.Core.Services;
|
namespace Bit.Core.Services;
|
||||||
|
|
||||||
public class AzureServiceBusIntegrationListenerService : BackgroundService
|
public class AzureServiceBusIntegrationListenerService<TConfiguration> : BackgroundService
|
||||||
|
where TConfiguration : IIntegrationListenerConfiguration
|
||||||
{
|
{
|
||||||
private readonly int _maxRetries;
|
private readonly int _maxRetries;
|
||||||
private readonly IAzureServiceBusService _serviceBusService;
|
private readonly IAzureServiceBusService _serviceBusService;
|
||||||
private readonly IIntegrationHandler _handler;
|
private readonly IIntegrationHandler _handler;
|
||||||
private readonly ServiceBusProcessor _processor;
|
private readonly ServiceBusProcessor _processor;
|
||||||
private readonly ILogger<AzureServiceBusIntegrationListenerService> _logger;
|
private readonly ILogger<AzureServiceBusIntegrationListenerService<TConfiguration>> _logger;
|
||||||
|
|
||||||
public AzureServiceBusIntegrationListenerService(IIntegrationHandler handler,
|
public AzureServiceBusIntegrationListenerService(
|
||||||
string topicName,
|
TConfiguration configuration,
|
||||||
string subscriptionName,
|
IIntegrationHandler handler,
|
||||||
int maxRetries,
|
|
||||||
IAzureServiceBusService serviceBusService,
|
IAzureServiceBusService serviceBusService,
|
||||||
ILogger<AzureServiceBusIntegrationListenerService> logger)
|
ILogger<AzureServiceBusIntegrationListenerService<TConfiguration>> logger)
|
||||||
{
|
{
|
||||||
_handler = handler;
|
_handler = handler;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_maxRetries = maxRetries;
|
_maxRetries = configuration.MaxRetries;
|
||||||
_serviceBusService = serviceBusService;
|
_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)
|
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
|
||||||
|
|||||||
@ -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
|
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.
|
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
|
## ServiceCollectionExtensions
|
||||||
|
|
||||||
In our `ServiceCollectionExtensions`, we pull all the above pieces together to start listeners on each message
|
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
|
tier with handlers to process the integration.
|
||||||
to add a new integration - one call per platform.
|
|
||||||
|
|
||||||
Also note that if an integration needs a custom singleton / service defined, the add listeners method is a
|
The core method for all event integration setup is `AddEventIntegrationServices`. This method is called by
|
||||||
good place to set that up. For instance, `SlackIntegrationHandler` needs a `SlackService`, so the singleton
|
both of the add listeners methods, which ensures that we have one common place to set up cross-messaging-platform
|
||||||
declaration is right above the add integration method for slack. Same thing for webhooks when it comes to
|
dependencies and integrations. For instance, `SlackIntegrationHandler` needs a `SlackService`, so
|
||||||
defining a custom HttpClient by name.
|
`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
|
``` csharp
|
||||||
services.AddRabbitMqIntegration<ExampleIntegrationConfigurationDetails, ExampleIntegrationHandler>(
|
var exampleConfiguration = new ExampleListenerConfiguration(globalSettings);
|
||||||
globalSettings.EventLogging.RabbitMq.ExampleEventsQueueName,
|
|
||||||
globalSettings.EventLogging.RabbitMq.ExampleIntegrationQueueName,
|
|
||||||
globalSettings.EventLogging.RabbitMq.ExampleIntegrationRetryQueueName,
|
|
||||||
globalSettings.EventLogging.RabbitMq.MaxRetries,
|
|
||||||
IntegrationType.Example);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
2. In `AddAzureServiceBusListeners` add the integration:
|
2. Add the integration to both the RabbitMQ and ASB specific declarations:
|
||||||
|
|
||||||
``` csharp
|
``` csharp
|
||||||
services.AddAzureServiceBusIntegration<ExampleIntegrationConfigurationDetails, ExampleIntegrationHandler>(
|
services.AddRabbitMqIntegration<ExampleIntegrationConfigurationDetails, ExampleListenerConfiguration>(exampleConfiguration);
|
||||||
eventSubscriptionName: globalSettings.EventLogging.AzureServiceBus.ExampleEventSubscriptionName,
|
|
||||||
integrationSubscriptionName: globalSettings.EventLogging.AzureServiceBus.ExampleIntegrationSubscriptionName,
|
|
||||||
integrationType: IntegrationType.Example,
|
|
||||||
globalSettings: globalSettings);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
and
|
||||||
|
|
||||||
|
``` csharp
|
||||||
|
services.AddAzureServiceBusIntegration<ExampleIntegrationConfigurationDetails, ExampleListenerConfiguration>(exampleConfiguration);
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
# Deploying a new integration
|
# Deploying a new integration
|
||||||
|
|
||||||
## RabbitMQ
|
## RabbitMQ
|
||||||
|
|||||||
@ -1,13 +1,15 @@
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using Bit.Core.AdminConsole.Models.Data.EventIntegrations;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using RabbitMQ.Client;
|
using RabbitMQ.Client;
|
||||||
using RabbitMQ.Client.Events;
|
using RabbitMQ.Client.Events;
|
||||||
|
|
||||||
namespace Bit.Core.Services;
|
namespace Bit.Core.Services;
|
||||||
|
|
||||||
public class RabbitMqEventListenerService : EventLoggingListenerService
|
public class RabbitMqEventListenerService<TConfiguration> : EventLoggingListenerService
|
||||||
|
where TConfiguration : IEventListenerConfiguration
|
||||||
{
|
{
|
||||||
private readonly Lazy<Task<IChannel>> _lazyChannel;
|
private readonly Lazy<Task<IChannel>> _lazyChannel;
|
||||||
private readonly string _queueName;
|
private readonly string _queueName;
|
||||||
@ -15,12 +17,11 @@ public class RabbitMqEventListenerService : EventLoggingListenerService
|
|||||||
|
|
||||||
public RabbitMqEventListenerService(
|
public RabbitMqEventListenerService(
|
||||||
IEventMessageHandler handler,
|
IEventMessageHandler handler,
|
||||||
string queueName,
|
TConfiguration configuration,
|
||||||
IRabbitMqService rabbitMqService,
|
IRabbitMqService rabbitMqService,
|
||||||
ILogger<RabbitMqEventListenerService> logger) : base(handler, logger)
|
ILogger<RabbitMqEventListenerService<TConfiguration>> logger) : base(handler, logger)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_queueName = configuration.EventQueueName;
|
||||||
_queueName = queueName;
|
|
||||||
_rabbitMqService = rabbitMqService;
|
_rabbitMqService = rabbitMqService;
|
||||||
_lazyChannel = new Lazy<Task<IChannel>>(() => _rabbitMqService.CreateChannelAsync());
|
_lazyChannel = new Lazy<Task<IChannel>>(() => _rabbitMqService.CreateChannelAsync());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,8 @@ using RabbitMQ.Client.Events;
|
|||||||
|
|
||||||
namespace Bit.Core.Services;
|
namespace Bit.Core.Services;
|
||||||
|
|
||||||
public class RabbitMqIntegrationListenerService : BackgroundService
|
public class RabbitMqIntegrationListenerService<TConfiguration> : BackgroundService
|
||||||
|
where TConfiguration : IIntegrationListenerConfiguration
|
||||||
{
|
{
|
||||||
private readonly int _maxRetries;
|
private readonly int _maxRetries;
|
||||||
private readonly string _queueName;
|
private readonly string _queueName;
|
||||||
@ -19,26 +20,24 @@ public class RabbitMqIntegrationListenerService : BackgroundService
|
|||||||
private readonly IIntegrationHandler _handler;
|
private readonly IIntegrationHandler _handler;
|
||||||
private readonly Lazy<Task<IChannel>> _lazyChannel;
|
private readonly Lazy<Task<IChannel>> _lazyChannel;
|
||||||
private readonly IRabbitMqService _rabbitMqService;
|
private readonly IRabbitMqService _rabbitMqService;
|
||||||
private readonly ILogger<RabbitMqIntegrationListenerService> _logger;
|
private readonly ILogger<RabbitMqIntegrationListenerService<TConfiguration>> _logger;
|
||||||
private readonly TimeProvider _timeProvider;
|
private readonly TimeProvider _timeProvider;
|
||||||
|
|
||||||
public RabbitMqIntegrationListenerService(IIntegrationHandler handler,
|
public RabbitMqIntegrationListenerService(
|
||||||
string routingKey,
|
IIntegrationHandler handler,
|
||||||
string queueName,
|
TConfiguration configuration,
|
||||||
string retryQueueName,
|
|
||||||
int maxRetries,
|
|
||||||
IRabbitMqService rabbitMqService,
|
IRabbitMqService rabbitMqService,
|
||||||
ILogger<RabbitMqIntegrationListenerService> logger,
|
ILogger<RabbitMqIntegrationListenerService<TConfiguration>> logger,
|
||||||
TimeProvider timeProvider)
|
TimeProvider timeProvider)
|
||||||
{
|
{
|
||||||
_handler = handler;
|
_handler = handler;
|
||||||
_routingKey = routingKey;
|
_maxRetries = configuration.MaxRetries;
|
||||||
_retryQueueName = retryQueueName;
|
_routingKey = configuration.RoutingKey;
|
||||||
_queueName = queueName;
|
_retryQueueName = configuration.IntegrationRetryQueueName;
|
||||||
|
_queueName = configuration.IntegrationQueueName;
|
||||||
_rabbitMqService = rabbitMqService;
|
_rabbitMqService = rabbitMqService;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_timeProvider = timeProvider;
|
_timeProvider = timeProvider;
|
||||||
_maxRetries = maxRetries;
|
|
||||||
_lazyChannel = new Lazy<Task<IChannel>>(() => _rabbitMqService.CreateChannelAsync());
|
_lazyChannel = new Lazy<Task<IChannel>>(() => _rabbitMqService.CreateChannelAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -288,6 +288,7 @@ public class GlobalSettings : IGlobalSettings
|
|||||||
public AzureServiceBusSettings AzureServiceBus { get; set; } = new AzureServiceBusSettings();
|
public AzureServiceBusSettings AzureServiceBus { get; set; } = new AzureServiceBusSettings();
|
||||||
public RabbitMqSettings RabbitMq { get; set; } = new RabbitMqSettings();
|
public RabbitMqSettings RabbitMq { get; set; } = new RabbitMqSettings();
|
||||||
public int IntegrationCacheRefreshIntervalMinutes { get; set; } = 10;
|
public int IntegrationCacheRefreshIntervalMinutes { get; set; } = 10;
|
||||||
|
public int MaxRetries { get; set; } = 3;
|
||||||
|
|
||||||
public class AzureServiceBusSettings
|
public class AzureServiceBusSettings
|
||||||
{
|
{
|
||||||
@ -295,7 +296,6 @@ public class GlobalSettings : IGlobalSettings
|
|||||||
private string _eventTopicName;
|
private string _eventTopicName;
|
||||||
private string _integrationTopicName;
|
private string _integrationTopicName;
|
||||||
|
|
||||||
public int MaxRetries { get; set; } = 3;
|
|
||||||
public virtual string EventRepositorySubscriptionName { get; set; } = "events-write-subscription";
|
public virtual string EventRepositorySubscriptionName { get; set; } = "events-write-subscription";
|
||||||
public virtual string SlackEventSubscriptionName { get; set; } = "events-slack-subscription";
|
public virtual string SlackEventSubscriptionName { get; set; } = "events-slack-subscription";
|
||||||
public virtual string SlackIntegrationSubscriptionName { get; set; } = "integration-slack-subscription";
|
public virtual string SlackIntegrationSubscriptionName { get; set; } = "integration-slack-subscription";
|
||||||
@ -331,7 +331,6 @@ public class GlobalSettings : IGlobalSettings
|
|||||||
private string _eventExchangeName;
|
private string _eventExchangeName;
|
||||||
private string _integrationExchangeName;
|
private string _integrationExchangeName;
|
||||||
|
|
||||||
public int MaxRetries { get; set; } = 3;
|
|
||||||
public int RetryTiming { get; set; } = 30000; // 30s
|
public int RetryTiming { get; set; } = 30000; // 30s
|
||||||
public bool UseDelayPlugin { get; set; } = false;
|
public bool UseDelayPlugin { get; set; } = false;
|
||||||
public virtual string EventRepositoryQueueName { get; set; } = "events-write-queue";
|
public virtual string EventRepositoryQueueName { get; set; } = "events-write-queue";
|
||||||
|
|||||||
@ -373,7 +373,6 @@ public static class ServiceCollectionExtensions
|
|||||||
services.AddSingleton<IPushRegistrationService, NoopPushRegistrationService>();
|
services.AddSingleton<IPushRegistrationService, NoopPushRegistrationService>();
|
||||||
services.AddSingleton<IAttachmentStorageService, NoopAttachmentStorageService>();
|
services.AddSingleton<IAttachmentStorageService, NoopAttachmentStorageService>();
|
||||||
services.AddSingleton<ILicensingService, NoopLicensingService>();
|
services.AddSingleton<ILicensingService, NoopLicensingService>();
|
||||||
services.AddSingleton<IEventWriteService, NoopEventWriteService>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IdentityBuilder AddCustomIdentityServices(
|
public static IdentityBuilder AddCustomIdentityServices(
|
||||||
@ -550,198 +549,57 @@ public static class ServiceCollectionExtensions
|
|||||||
{
|
{
|
||||||
if (!globalSettings.SelfHosted && CoreHelpers.SettingHasValue(globalSettings.Events.ConnectionString))
|
if (!globalSettings.SelfHosted && CoreHelpers.SettingHasValue(globalSettings.Events.ConnectionString))
|
||||||
{
|
{
|
||||||
services.AddKeyedSingleton<IEventWriteService, AzureQueueEventWriteService>("storage");
|
services.TryAddKeyedSingleton<IEventWriteService, AzureQueueEventWriteService>("storage");
|
||||||
|
|
||||||
if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.ConnectionString) &&
|
if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.ConnectionString) &&
|
||||||
CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.EventTopicName))
|
CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.EventTopicName))
|
||||||
{
|
{
|
||||||
services.AddSingleton<IEventIntegrationPublisher, AzureServiceBusService>();
|
services.TryAddSingleton<IEventIntegrationPublisher, AzureServiceBusService>();
|
||||||
services.AddKeyedSingleton<IEventWriteService, EventIntegrationEventWriteService>("broadcast");
|
services.TryAddKeyedSingleton<IEventWriteService, EventIntegrationEventWriteService>("broadcast");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
services.AddKeyedSingleton<IEventWriteService, NoopEventWriteService>("broadcast");
|
services.TryAddKeyedSingleton<IEventWriteService, NoopEventWriteService>("broadcast");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (globalSettings.SelfHosted)
|
else if (globalSettings.SelfHosted)
|
||||||
{
|
{
|
||||||
services.AddKeyedSingleton<IEventWriteService, RepositoryEventWriteService>("storage");
|
services.TryAddKeyedSingleton<IEventWriteService, RepositoryEventWriteService>("storage");
|
||||||
|
|
||||||
if (IsRabbitMqEnabled(globalSettings))
|
if (IsRabbitMqEnabled(globalSettings))
|
||||||
{
|
{
|
||||||
services.AddSingleton<IEventIntegrationPublisher, RabbitMqService>();
|
services.TryAddSingleton<IEventIntegrationPublisher, RabbitMqService>();
|
||||||
services.AddKeyedSingleton<IEventWriteService, EventIntegrationEventWriteService>("broadcast");
|
services.TryAddKeyedSingleton<IEventWriteService, EventIntegrationEventWriteService>("broadcast");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
services.AddKeyedSingleton<IEventWriteService, NoopEventWriteService>("broadcast");
|
services.TryAddKeyedSingleton<IEventWriteService, NoopEventWriteService>("broadcast");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
services.AddKeyedSingleton<IEventWriteService, NoopEventWriteService>("storage");
|
services.TryAddKeyedSingleton<IEventWriteService, NoopEventWriteService>("storage");
|
||||||
services.AddKeyedSingleton<IEventWriteService, NoopEventWriteService>("broadcast");
|
services.TryAddKeyedSingleton<IEventWriteService, NoopEventWriteService>("broadcast");
|
||||||
}
|
}
|
||||||
|
|
||||||
services.AddScoped<IEventWriteService, EventRouteService>();
|
services.TryAddScoped<IEventWriteService, EventRouteService>();
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IServiceCollection AddAzureServiceBusEventRepositoryListener(this IServiceCollection services, GlobalSettings globalSettings)
|
|
||||||
{
|
|
||||||
services.AddSingleton<IEventRepository, TableStorageRepos.EventRepository>();
|
|
||||||
services.AddSingleton<AzureTableStorageEventHandler>();
|
|
||||||
services.AddKeyedSingleton<IEventWriteService, RepositoryEventWriteService>("persistent");
|
|
||||||
services.AddSingleton<IHostedService>(provider =>
|
|
||||||
new AzureServiceBusEventListenerService(
|
|
||||||
handler: provider.GetRequiredService<AzureTableStorageEventHandler>(),
|
|
||||||
serviceBusService: provider.GetRequiredService<IAzureServiceBusService>(),
|
|
||||||
subscriptionName: globalSettings.EventLogging.AzureServiceBus.EventRepositorySubscriptionName,
|
|
||||||
globalSettings: globalSettings,
|
|
||||||
logger: provider.GetRequiredService<ILogger<AzureServiceBusEventListenerService>>()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IServiceCollection AddAzureServiceBusIntegration<TConfig, THandler>(
|
|
||||||
this IServiceCollection services,
|
|
||||||
string eventSubscriptionName,
|
|
||||||
string integrationSubscriptionName,
|
|
||||||
IntegrationType integrationType,
|
|
||||||
GlobalSettings globalSettings)
|
|
||||||
where TConfig : class
|
|
||||||
where THandler : class, IIntegrationHandler<TConfig>
|
|
||||||
{
|
|
||||||
var routingKey = integrationType.ToRoutingKey();
|
|
||||||
|
|
||||||
services.AddKeyedSingleton<IEventMessageHandler>(routingKey, (provider, _) =>
|
|
||||||
new EventIntegrationHandler<TConfig>(
|
|
||||||
integrationType,
|
|
||||||
provider.GetRequiredService<IEventIntegrationPublisher>(),
|
|
||||||
provider.GetRequiredService<IIntegrationFilterService>(),
|
|
||||||
provider.GetRequiredService<IIntegrationConfigurationDetailsCache>(),
|
|
||||||
provider.GetRequiredService<IUserRepository>(),
|
|
||||||
provider.GetRequiredService<IOrganizationRepository>(),
|
|
||||||
provider.GetRequiredService<ILogger<EventIntegrationHandler<TConfig>>>()));
|
|
||||||
|
|
||||||
services.AddSingleton<IHostedService>(provider =>
|
|
||||||
new AzureServiceBusEventListenerService(
|
|
||||||
handler: provider.GetRequiredKeyedService<IEventMessageHandler>(routingKey),
|
|
||||||
serviceBusService: provider.GetRequiredService<IAzureServiceBusService>(),
|
|
||||||
subscriptionName: eventSubscriptionName,
|
|
||||||
globalSettings: globalSettings,
|
|
||||||
logger: provider.GetRequiredService<ILogger<AzureServiceBusEventListenerService>>()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
services.AddSingleton<IIntegrationHandler<TConfig>, THandler>();
|
|
||||||
services.AddSingleton<IHostedService>(provider =>
|
|
||||||
new AzureServiceBusIntegrationListenerService(
|
|
||||||
handler: provider.GetRequiredService<IIntegrationHandler<TConfig>>(),
|
|
||||||
topicName: globalSettings.EventLogging.AzureServiceBus.IntegrationTopicName,
|
|
||||||
subscriptionName: integrationSubscriptionName,
|
|
||||||
maxRetries: globalSettings.EventLogging.AzureServiceBus.MaxRetries,
|
|
||||||
serviceBusService: provider.GetRequiredService<IAzureServiceBusService>(),
|
|
||||||
logger: provider.GetRequiredService<ILogger<AzureServiceBusIntegrationListenerService>>()));
|
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IServiceCollection AddAzureServiceBusListeners(this IServiceCollection services, GlobalSettings globalSettings)
|
public static IServiceCollection AddAzureServiceBusListeners(this IServiceCollection services, GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
if (!CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.ConnectionString) ||
|
if (!IsAzureServiceBusEnabled(globalSettings))
|
||||||
!CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.EventTopicName))
|
{
|
||||||
return services;
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
services.AddSingleton<IntegrationConfigurationDetailsCacheService>();
|
services.TryAddSingleton<IAzureServiceBusService, AzureServiceBusService>();
|
||||||
services.AddSingleton<IIntegrationConfigurationDetailsCache>(provider =>
|
services.TryAddSingleton<IEventIntegrationPublisher, AzureServiceBusService>();
|
||||||
provider.GetRequiredService<IntegrationConfigurationDetailsCacheService>());
|
services.TryAddSingleton<IEventRepository, TableStorageRepos.EventRepository>();
|
||||||
services.AddHostedService(provider => provider.GetRequiredService<IntegrationConfigurationDetailsCacheService>());
|
services.TryAddKeyedSingleton<IEventWriteService, RepositoryEventWriteService>("persistent");
|
||||||
services.AddSingleton<IIntegrationFilterService, IntegrationFilterService>();
|
services.TryAddSingleton<AzureTableStorageEventHandler>();
|
||||||
services.AddSingleton<IAzureServiceBusService, AzureServiceBusService>();
|
|
||||||
services.AddSingleton<IEventIntegrationPublisher, AzureServiceBusService>();
|
|
||||||
services.AddAzureServiceBusEventRepositoryListener(globalSettings);
|
|
||||||
|
|
||||||
services.AddSlackService(globalSettings);
|
services.AddEventIntegrationServices(globalSettings);
|
||||||
services.AddAzureServiceBusIntegration<SlackIntegrationConfigurationDetails, SlackIntegrationHandler>(
|
|
||||||
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<WebhookIntegrationConfigurationDetails, WebhookIntegrationHandler>(
|
|
||||||
eventSubscriptionName: globalSettings.EventLogging.AzureServiceBus.WebhookEventSubscriptionName,
|
|
||||||
integrationSubscriptionName: globalSettings.EventLogging.AzureServiceBus.WebhookIntegrationSubscriptionName,
|
|
||||||
integrationType: IntegrationType.Webhook,
|
|
||||||
globalSettings: globalSettings);
|
|
||||||
|
|
||||||
services.AddAzureServiceBusIntegration<WebhookIntegrationConfigurationDetails, WebhookIntegrationHandler>(
|
|
||||||
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<EventRepositoryHandler>();
|
|
||||||
services.AddKeyedSingleton<IEventWriteService, RepositoryEventWriteService>("persistent");
|
|
||||||
|
|
||||||
services.AddSingleton<IHostedService>(provider =>
|
|
||||||
new RabbitMqEventListenerService(
|
|
||||||
provider.GetRequiredService<EventRepositoryHandler>(),
|
|
||||||
globalSettings.EventLogging.RabbitMq.EventRepositoryQueueName,
|
|
||||||
provider.GetRequiredService<IRabbitMqService>(),
|
|
||||||
provider.GetRequiredService<ILogger<RabbitMqEventListenerService>>()));
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IServiceCollection AddRabbitMqIntegration<TConfig, THandler>(this IServiceCollection services,
|
|
||||||
string eventQueueName,
|
|
||||||
string integrationQueueName,
|
|
||||||
string integrationRetryQueueName,
|
|
||||||
int maxRetries,
|
|
||||||
IntegrationType integrationType)
|
|
||||||
where TConfig : class
|
|
||||||
where THandler : class, IIntegrationHandler<TConfig>
|
|
||||||
{
|
|
||||||
var routingKey = integrationType.ToRoutingKey();
|
|
||||||
|
|
||||||
services.AddKeyedSingleton<IEventMessageHandler>(routingKey, (provider, _) =>
|
|
||||||
new EventIntegrationHandler<TConfig>(
|
|
||||||
integrationType,
|
|
||||||
provider.GetRequiredService<IEventIntegrationPublisher>(),
|
|
||||||
provider.GetRequiredService<IIntegrationFilterService>(),
|
|
||||||
provider.GetRequiredService<IIntegrationConfigurationDetailsCache>(),
|
|
||||||
provider.GetRequiredService<IUserRepository>(),
|
|
||||||
provider.GetRequiredService<IOrganizationRepository>(),
|
|
||||||
provider.GetRequiredService<ILogger<EventIntegrationHandler<TConfig>>>()));
|
|
||||||
|
|
||||||
services.AddSingleton<IHostedService>(provider =>
|
|
||||||
new RabbitMqEventListenerService(
|
|
||||||
provider.GetRequiredKeyedService<IEventMessageHandler>(routingKey),
|
|
||||||
eventQueueName,
|
|
||||||
provider.GetRequiredService<IRabbitMqService>(),
|
|
||||||
provider.GetRequiredService<ILogger<RabbitMqEventListenerService>>()));
|
|
||||||
|
|
||||||
services.AddSingleton<IIntegrationHandler<TConfig>, THandler>();
|
|
||||||
services.AddSingleton<IHostedService>(provider =>
|
|
||||||
new RabbitMqIntegrationListenerService(
|
|
||||||
handler: provider.GetRequiredService<IIntegrationHandler<TConfig>>(),
|
|
||||||
routingKey: routingKey,
|
|
||||||
queueName: integrationQueueName,
|
|
||||||
retryQueueName: integrationRetryQueueName,
|
|
||||||
maxRetries: maxRetries,
|
|
||||||
rabbitMqService: provider.GetRequiredService<IRabbitMqService>(),
|
|
||||||
logger: provider.GetRequiredService<ILogger<RabbitMqIntegrationListenerService>>(),
|
|
||||||
timeProvider: provider.GetRequiredService<TimeProvider>()));
|
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
@ -753,49 +611,15 @@ public static class ServiceCollectionExtensions
|
|||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
services.AddSingleton<IntegrationConfigurationDetailsCacheService>();
|
services.TryAddSingleton<IRabbitMqService, RabbitMqService>();
|
||||||
services.AddSingleton<IIntegrationConfigurationDetailsCache>(provider =>
|
services.TryAddSingleton<IEventIntegrationPublisher, RabbitMqService>();
|
||||||
provider.GetRequiredService<IntegrationConfigurationDetailsCacheService>());
|
services.TryAddSingleton<EventRepositoryHandler>();
|
||||||
services.AddHostedService(provider => provider.GetRequiredService<IntegrationConfigurationDetailsCacheService>());
|
|
||||||
services.AddSingleton<IIntegrationFilterService, IntegrationFilterService>();
|
|
||||||
services.AddSingleton<IRabbitMqService, RabbitMqService>();
|
|
||||||
services.AddSingleton<IEventIntegrationPublisher, RabbitMqService>();
|
|
||||||
services.AddRabbitMqEventRepositoryListener(globalSettings);
|
|
||||||
|
|
||||||
services.AddSlackService(globalSettings);
|
services.AddEventIntegrationServices(globalSettings);
|
||||||
services.AddRabbitMqIntegration<SlackIntegrationConfigurationDetails, SlackIntegrationHandler>(
|
|
||||||
globalSettings.EventLogging.RabbitMq.SlackEventsQueueName,
|
|
||||||
globalSettings.EventLogging.RabbitMq.SlackIntegrationQueueName,
|
|
||||||
globalSettings.EventLogging.RabbitMq.SlackIntegrationRetryQueueName,
|
|
||||||
globalSettings.EventLogging.RabbitMq.MaxRetries,
|
|
||||||
IntegrationType.Slack);
|
|
||||||
|
|
||||||
services.AddHttpClient(WebhookIntegrationHandler.HttpClientName);
|
|
||||||
services.AddRabbitMqIntegration<WebhookIntegrationConfigurationDetails, WebhookIntegrationHandler>(
|
|
||||||
globalSettings.EventLogging.RabbitMq.WebhookEventsQueueName,
|
|
||||||
globalSettings.EventLogging.RabbitMq.WebhookIntegrationQueueName,
|
|
||||||
globalSettings.EventLogging.RabbitMq.WebhookIntegrationRetryQueueName,
|
|
||||||
globalSettings.EventLogging.RabbitMq.MaxRetries,
|
|
||||||
IntegrationType.Webhook);
|
|
||||||
|
|
||||||
services.AddRabbitMqIntegration<WebhookIntegrationConfigurationDetails, WebhookIntegrationHandler>(
|
|
||||||
globalSettings.EventLogging.RabbitMq.HecEventsQueueName,
|
|
||||||
globalSettings.EventLogging.RabbitMq.HecIntegrationQueueName,
|
|
||||||
globalSettings.EventLogging.RabbitMq.HecIntegrationRetryQueueName,
|
|
||||||
globalSettings.EventLogging.RabbitMq.MaxRetries,
|
|
||||||
IntegrationType.Hec);
|
|
||||||
|
|
||||||
return services;
|
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)
|
public static IServiceCollection AddSlackService(this IServiceCollection services, GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
if (CoreHelpers.SettingHasValue(globalSettings.Slack.ClientId) &&
|
if (CoreHelpers.SettingHasValue(globalSettings.Slack.ClientId) &&
|
||||||
@ -803,11 +627,11 @@ public static class ServiceCollectionExtensions
|
|||||||
CoreHelpers.SettingHasValue(globalSettings.Slack.Scopes))
|
CoreHelpers.SettingHasValue(globalSettings.Slack.Scopes))
|
||||||
{
|
{
|
||||||
services.AddHttpClient(SlackService.HttpClientName);
|
services.AddHttpClient(SlackService.HttpClientName);
|
||||||
services.AddSingleton<ISlackService, SlackService>();
|
services.TryAddSingleton<ISlackService, SlackService>();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
services.AddSingleton<ISlackService, NoopSlackService>();
|
services.TryAddSingleton<ISlackService, NoopSlackService>();
|
||||||
}
|
}
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
@ -1043,4 +867,161 @@ public static class ServiceCollectionExtensions
|
|||||||
|
|
||||||
return (provider, connectionString);
|
return (provider, connectionString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static IServiceCollection AddAzureServiceBusIntegration<TConfig, TListenerConfig>(this IServiceCollection services,
|
||||||
|
TListenerConfig listenerConfiguration)
|
||||||
|
where TConfig : class
|
||||||
|
where TListenerConfig : IIntegrationListenerConfiguration
|
||||||
|
{
|
||||||
|
services.TryAddKeyedSingleton<IEventMessageHandler>(serviceKey: listenerConfiguration.RoutingKey, implementationFactory: (provider, _) =>
|
||||||
|
new EventIntegrationHandler<TConfig>(
|
||||||
|
integrationType: listenerConfiguration.IntegrationType,
|
||||||
|
eventIntegrationPublisher: provider.GetRequiredService<IEventIntegrationPublisher>(),
|
||||||
|
integrationFilterService: provider.GetRequiredService<IIntegrationFilterService>(),
|
||||||
|
configurationCache: provider.GetRequiredService<IIntegrationConfigurationDetailsCache>(),
|
||||||
|
userRepository: provider.GetRequiredService<IUserRepository>(),
|
||||||
|
organizationRepository: provider.GetRequiredService<IOrganizationRepository>(),
|
||||||
|
logger: provider.GetRequiredService<ILogger<EventIntegrationHandler<TConfig>>>()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService,
|
||||||
|
AzureServiceBusEventListenerService<TListenerConfig>>(provider =>
|
||||||
|
new AzureServiceBusEventListenerService<TListenerConfig>(
|
||||||
|
configuration: listenerConfiguration,
|
||||||
|
handler: provider.GetRequiredKeyedService<IEventMessageHandler>(serviceKey: listenerConfiguration.RoutingKey),
|
||||||
|
serviceBusService: provider.GetRequiredService<IAzureServiceBusService>(),
|
||||||
|
logger: provider.GetRequiredService<ILogger<AzureServiceBusEventListenerService<TListenerConfig>>>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService,
|
||||||
|
AzureServiceBusIntegrationListenerService<TListenerConfig>>(provider =>
|
||||||
|
new AzureServiceBusIntegrationListenerService<TListenerConfig>(
|
||||||
|
configuration: listenerConfiguration,
|
||||||
|
handler: provider.GetRequiredService<IIntegrationHandler<TConfig>>(),
|
||||||
|
serviceBusService: provider.GetRequiredService<IAzureServiceBusService>(),
|
||||||
|
logger: provider.GetRequiredService<ILogger<AzureServiceBusIntegrationListenerService<TListenerConfig>>>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IServiceCollection AddEventIntegrationServices(this IServiceCollection services,
|
||||||
|
GlobalSettings globalSettings)
|
||||||
|
{
|
||||||
|
// Add common services
|
||||||
|
services.TryAddSingleton<IntegrationConfigurationDetailsCacheService>();
|
||||||
|
services.TryAddSingleton<IIntegrationConfigurationDetailsCache>(provider =>
|
||||||
|
provider.GetRequiredService<IntegrationConfigurationDetailsCacheService>());
|
||||||
|
services.AddHostedService(provider => provider.GetRequiredService<IntegrationConfigurationDetailsCacheService>());
|
||||||
|
services.TryAddSingleton<IIntegrationFilterService, IntegrationFilterService>();
|
||||||
|
services.TryAddKeyedSingleton<IEventWriteService, RepositoryEventWriteService>("persistent");
|
||||||
|
|
||||||
|
// Add services in support of handlers
|
||||||
|
services.AddSlackService(globalSettings);
|
||||||
|
services.TryAddSingleton(TimeProvider.System);
|
||||||
|
services.AddHttpClient(WebhookIntegrationHandler.HttpClientName);
|
||||||
|
|
||||||
|
// Add integration handlers
|
||||||
|
services.TryAddSingleton<IIntegrationHandler<SlackIntegrationConfigurationDetails>, SlackIntegrationHandler>();
|
||||||
|
services.TryAddSingleton<IIntegrationHandler<WebhookIntegrationConfigurationDetails>, 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<IHostedService,
|
||||||
|
RabbitMqEventListenerService<RepositoryListenerConfiguration>>(provider =>
|
||||||
|
new RabbitMqEventListenerService<RepositoryListenerConfiguration>(
|
||||||
|
handler: provider.GetRequiredService<EventRepositoryHandler>(),
|
||||||
|
configuration: repositoryConfiguration,
|
||||||
|
rabbitMqService: provider.GetRequiredService<IRabbitMqService>(),
|
||||||
|
logger: provider.GetRequiredService<ILogger<RabbitMqEventListenerService<RepositoryListenerConfiguration>>>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
services.AddRabbitMqIntegration<SlackIntegrationConfigurationDetails, SlackListenerConfiguration>(slackConfiguration);
|
||||||
|
services.AddRabbitMqIntegration<WebhookIntegrationConfigurationDetails, WebhookListenerConfiguration>(webhookConfiguration);
|
||||||
|
services.AddRabbitMqIntegration<WebhookIntegrationConfigurationDetails, HecListenerConfiguration>(hecConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsAzureServiceBusEnabled(globalSettings))
|
||||||
|
{
|
||||||
|
services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService,
|
||||||
|
AzureServiceBusEventListenerService<RepositoryListenerConfiguration>>(provider =>
|
||||||
|
new AzureServiceBusEventListenerService<RepositoryListenerConfiguration>(
|
||||||
|
configuration: repositoryConfiguration,
|
||||||
|
handler: provider.GetRequiredService<AzureTableStorageEventHandler>(),
|
||||||
|
serviceBusService: provider.GetRequiredService<IAzureServiceBusService>(),
|
||||||
|
logger: provider.GetRequiredService<ILogger<AzureServiceBusEventListenerService<RepositoryListenerConfiguration>>>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
services.AddAzureServiceBusIntegration<SlackIntegrationConfigurationDetails, SlackListenerConfiguration>(slackConfiguration);
|
||||||
|
services.AddAzureServiceBusIntegration<WebhookIntegrationConfigurationDetails, WebhookListenerConfiguration>(webhookConfiguration);
|
||||||
|
services.AddAzureServiceBusIntegration<WebhookIntegrationConfigurationDetails, HecListenerConfiguration>(hecConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IServiceCollection AddRabbitMqIntegration<TConfig, TListenerConfig>(this IServiceCollection services,
|
||||||
|
TListenerConfig listenerConfiguration)
|
||||||
|
where TConfig : class
|
||||||
|
where TListenerConfig : IIntegrationListenerConfiguration
|
||||||
|
{
|
||||||
|
services.TryAddKeyedSingleton<IEventMessageHandler>(serviceKey: listenerConfiguration.RoutingKey, implementationFactory: (provider, _) =>
|
||||||
|
new EventIntegrationHandler<TConfig>(
|
||||||
|
integrationType: listenerConfiguration.IntegrationType,
|
||||||
|
eventIntegrationPublisher: provider.GetRequiredService<IEventIntegrationPublisher>(),
|
||||||
|
integrationFilterService: provider.GetRequiredService<IIntegrationFilterService>(),
|
||||||
|
configurationCache: provider.GetRequiredService<IIntegrationConfigurationDetailsCache>(),
|
||||||
|
userRepository: provider.GetRequiredService<IUserRepository>(),
|
||||||
|
organizationRepository: provider.GetRequiredService<IOrganizationRepository>(),
|
||||||
|
logger: provider.GetRequiredService<ILogger<EventIntegrationHandler<TConfig>>>()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService,
|
||||||
|
RabbitMqEventListenerService<TListenerConfig>>(provider =>
|
||||||
|
new RabbitMqEventListenerService<TListenerConfig>(
|
||||||
|
handler: provider.GetRequiredKeyedService<IEventMessageHandler>(serviceKey: listenerConfiguration.RoutingKey),
|
||||||
|
configuration: listenerConfiguration,
|
||||||
|
rabbitMqService: provider.GetRequiredService<IRabbitMqService>(),
|
||||||
|
logger: provider.GetRequiredService<ILogger<RabbitMqEventListenerService<TListenerConfig>>>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService,
|
||||||
|
RabbitMqIntegrationListenerService<TListenerConfig>>(provider =>
|
||||||
|
new RabbitMqIntegrationListenerService<TListenerConfig>(
|
||||||
|
handler: provider.GetRequiredService<IIntegrationHandler<TConfig>>(),
|
||||||
|
configuration: listenerConfiguration,
|
||||||
|
rabbitMqService: provider.GetRequiredService<IRabbitMqService>(),
|
||||||
|
logger: provider.GetRequiredService<ILogger<RabbitMqIntegrationListenerService<TListenerConfig>>>(),
|
||||||
|
timeProvider: provider.GetRequiredService<TimeProvider>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Azure.Messaging.ServiceBus;
|
using Azure.Messaging.ServiceBus;
|
||||||
|
using Bit.Core.AdminConsole.Models.Data.EventIntegrations;
|
||||||
using Bit.Core.Models.Data;
|
using Bit.Core.Models.Data;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
@ -14,20 +15,28 @@ namespace Bit.Core.Test.Services;
|
|||||||
[SutProviderCustomize]
|
[SutProviderCustomize]
|
||||||
public class AzureServiceBusEventListenerServiceTests
|
public class AzureServiceBusEventListenerServiceTests
|
||||||
{
|
{
|
||||||
private readonly IEventMessageHandler _handler = Substitute.For<IEventMessageHandler>();
|
|
||||||
private readonly ILogger<AzureServiceBusEventListenerService> _logger =
|
|
||||||
Substitute.For<ILogger<AzureServiceBusEventListenerService>>();
|
|
||||||
private const string _messageId = "messageId";
|
private const string _messageId = "messageId";
|
||||||
|
private readonly TestListenerConfiguration _config = new();
|
||||||
|
|
||||||
private SutProvider<AzureServiceBusEventListenerService> GetSutProvider()
|
private SutProvider<AzureServiceBusEventListenerService<TestListenerConfiguration>> GetSutProvider()
|
||||||
{
|
{
|
||||||
return new SutProvider<AzureServiceBusEventListenerService>()
|
return new SutProvider<AzureServiceBusEventListenerService<TestListenerConfiguration>>()
|
||||||
.SetDependency(_handler)
|
.SetDependency(_config)
|
||||||
.SetDependency(_logger)
|
|
||||||
.SetDependency("test-subscription", "subscriptionName")
|
|
||||||
.Create();
|
.Create();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_CreatesProcessor()
|
||||||
|
{
|
||||||
|
var sutProvider = GetSutProvider();
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IAzureServiceBusService>().Received(1).CreateProcessor(
|
||||||
|
Arg.Is(_config.EventTopicName),
|
||||||
|
Arg.Is(_config.EventSubscriptionName),
|
||||||
|
Arg.Any<ServiceBusProcessorOptions>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async Task ProcessErrorAsync_LogsError(ProcessErrorEventArgs args)
|
public async Task ProcessErrorAsync_LogsError(ProcessErrorEventArgs args)
|
||||||
{
|
{
|
||||||
@ -35,7 +44,7 @@ public class AzureServiceBusEventListenerServiceTests
|
|||||||
|
|
||||||
await sutProvider.Sut.ProcessErrorAsync(args);
|
await sutProvider.Sut.ProcessErrorAsync(args);
|
||||||
|
|
||||||
_logger.Received(1).Log(
|
sutProvider.GetDependency<ILogger<AzureServiceBusEventListenerService<TestListenerConfiguration>>>().Received(1).Log(
|
||||||
LogLevel.Error,
|
LogLevel.Error,
|
||||||
Arg.Any<EventId>(),
|
Arg.Any<EventId>(),
|
||||||
Arg.Any<object>(),
|
Arg.Any<object>(),
|
||||||
@ -49,7 +58,7 @@ public class AzureServiceBusEventListenerServiceTests
|
|||||||
var sutProvider = GetSutProvider();
|
var sutProvider = GetSutProvider();
|
||||||
await sutProvider.Sut.ProcessReceivedMessageAsync(string.Empty, _messageId);
|
await sutProvider.Sut.ProcessReceivedMessageAsync(string.Empty, _messageId);
|
||||||
|
|
||||||
_logger.Received(1).Log(
|
sutProvider.GetDependency<ILogger<AzureServiceBusEventListenerService<TestListenerConfiguration>>>().Received(1).Log(
|
||||||
LogLevel.Error,
|
LogLevel.Error,
|
||||||
Arg.Any<EventId>(),
|
Arg.Any<EventId>(),
|
||||||
Arg.Any<object>(),
|
Arg.Any<object>(),
|
||||||
@ -63,7 +72,7 @@ public class AzureServiceBusEventListenerServiceTests
|
|||||||
var sutProvider = GetSutProvider();
|
var sutProvider = GetSutProvider();
|
||||||
await sutProvider.Sut.ProcessReceivedMessageAsync("{ Inavlid JSON }", _messageId);
|
await sutProvider.Sut.ProcessReceivedMessageAsync("{ Inavlid JSON }", _messageId);
|
||||||
|
|
||||||
_logger.Received(1).Log(
|
sutProvider.GetDependency<ILogger<AzureServiceBusEventListenerService<TestListenerConfiguration>>>().Received(1).Log(
|
||||||
LogLevel.Error,
|
LogLevel.Error,
|
||||||
Arg.Any<EventId>(),
|
Arg.Any<EventId>(),
|
||||||
Arg.Is<object>(o => o.ToString().Contains("Invalid JSON")),
|
Arg.Is<object>(o => o.ToString().Contains("Invalid JSON")),
|
||||||
@ -80,7 +89,7 @@ public class AzureServiceBusEventListenerServiceTests
|
|||||||
_messageId
|
_messageId
|
||||||
);
|
);
|
||||||
|
|
||||||
_logger.Received(1).Log(
|
sutProvider.GetDependency<ILogger<AzureServiceBusEventListenerService<TestListenerConfiguration>>>().Received(1).Log(
|
||||||
LogLevel.Error,
|
LogLevel.Error,
|
||||||
Arg.Any<EventId>(),
|
Arg.Any<EventId>(),
|
||||||
Arg.Any<object>(),
|
Arg.Any<object>(),
|
||||||
@ -97,7 +106,7 @@ public class AzureServiceBusEventListenerServiceTests
|
|||||||
_messageId
|
_messageId
|
||||||
);
|
);
|
||||||
|
|
||||||
_logger.Received(1).Log(
|
sutProvider.GetDependency<ILogger<AzureServiceBusEventListenerService<TestListenerConfiguration>>>().Received(1).Log(
|
||||||
LogLevel.Error,
|
LogLevel.Error,
|
||||||
Arg.Any<EventId>(),
|
Arg.Any<EventId>(),
|
||||||
Arg.Any<object>(),
|
Arg.Any<object>(),
|
||||||
|
|||||||
@ -14,33 +14,38 @@ namespace Bit.Core.Test.Services;
|
|||||||
[SutProviderCustomize]
|
[SutProviderCustomize]
|
||||||
public class AzureServiceBusIntegrationListenerServiceTests
|
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<IIntegrationHandler>();
|
private readonly IIntegrationHandler _handler = Substitute.For<IIntegrationHandler>();
|
||||||
private readonly IAzureServiceBusService _serviceBusService = Substitute.For<IAzureServiceBusService>();
|
private readonly IAzureServiceBusService _serviceBusService = Substitute.For<IAzureServiceBusService>();
|
||||||
private readonly ILogger<AzureServiceBusIntegrationListenerService> _logger =
|
private readonly TestListenerConfiguration _config = new();
|
||||||
Substitute.For<ILogger<AzureServiceBusIntegrationListenerService>>();
|
|
||||||
|
|
||||||
private SutProvider<AzureServiceBusIntegrationListenerService> GetSutProvider()
|
private SutProvider<AzureServiceBusIntegrationListenerService<TestListenerConfiguration>> GetSutProvider()
|
||||||
{
|
{
|
||||||
return new SutProvider<AzureServiceBusIntegrationListenerService>()
|
return new SutProvider<AzureServiceBusIntegrationListenerService<TestListenerConfiguration>>()
|
||||||
|
.SetDependency(_config)
|
||||||
.SetDependency(_handler)
|
.SetDependency(_handler)
|
||||||
.SetDependency(_serviceBusService)
|
.SetDependency(_serviceBusService)
|
||||||
.SetDependency(_topicName, "topicName")
|
|
||||||
.SetDependency(_subscriptionName, "subscriptionName")
|
|
||||||
.SetDependency(_maxRetries, "maxRetries")
|
|
||||||
.SetDependency(_logger)
|
|
||||||
.Create();
|
.Create();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_CreatesProcessor()
|
||||||
|
{
|
||||||
|
var sutProvider = GetSutProvider();
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IAzureServiceBusService>().Received(1).CreateProcessor(
|
||||||
|
Arg.Is(_config.IntegrationTopicName),
|
||||||
|
Arg.Is(_config.IntegrationSubscriptionName),
|
||||||
|
Arg.Any<ServiceBusProcessorOptions>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async Task ProcessErrorAsync_LogsError(ProcessErrorEventArgs args)
|
public async Task ProcessErrorAsync_LogsError(ProcessErrorEventArgs args)
|
||||||
{
|
{
|
||||||
var sutProvider = GetSutProvider();
|
var sutProvider = GetSutProvider();
|
||||||
await sutProvider.Sut.ProcessErrorAsync(args);
|
await sutProvider.Sut.ProcessErrorAsync(args);
|
||||||
|
|
||||||
_logger.Received(1).Log(
|
sutProvider.GetDependency<ILogger<AzureServiceBusIntegrationListenerService<TestListenerConfiguration>>>().Received(1).Log(
|
||||||
LogLevel.Error,
|
LogLevel.Error,
|
||||||
Arg.Any<EventId>(),
|
Arg.Any<EventId>(),
|
||||||
Arg.Any<object>(),
|
Arg.Any<object>(),
|
||||||
@ -70,7 +75,7 @@ public class AzureServiceBusIntegrationListenerServiceTests
|
|||||||
public async Task HandleMessageAsync_FailureRetryableButTooManyRetries_PublishesToDeadLetterQueue(IntegrationMessage<WebhookIntegrationConfiguration> message)
|
public async Task HandleMessageAsync_FailureRetryableButTooManyRetries_PublishesToDeadLetterQueue(IntegrationMessage<WebhookIntegrationConfiguration> message)
|
||||||
{
|
{
|
||||||
var sutProvider = GetSutProvider();
|
var sutProvider = GetSutProvider();
|
||||||
message.RetryCount = _maxRetries;
|
message.RetryCount = _config.MaxRetries;
|
||||||
var result = new IntegrationHandlerResult(false, message);
|
var result = new IntegrationHandlerResult(false, message);
|
||||||
result.Retryable = true;
|
result.Retryable = true;
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using Bit.Core.AdminConsole.Models.Data.EventIntegrations;
|
||||||
using Bit.Core.Models.Data;
|
using Bit.Core.Models.Data;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
@ -15,16 +16,12 @@ namespace Bit.Core.Test.Services;
|
|||||||
[SutProviderCustomize]
|
[SutProviderCustomize]
|
||||||
public class RabbitMqEventListenerServiceTests
|
public class RabbitMqEventListenerServiceTests
|
||||||
{
|
{
|
||||||
private const string _queueName = "test_queue";
|
private readonly TestListenerConfiguration _config = new();
|
||||||
private readonly IRabbitMqService _rabbitMqService = Substitute.For<IRabbitMqService>();
|
|
||||||
private readonly ILogger<RabbitMqEventListenerService> _logger = Substitute.For<ILogger<RabbitMqEventListenerService>>();
|
|
||||||
|
|
||||||
private SutProvider<RabbitMqEventListenerService> GetSutProvider()
|
private SutProvider<RabbitMqEventListenerService<TestListenerConfiguration>> GetSutProvider()
|
||||||
{
|
{
|
||||||
return new SutProvider<RabbitMqEventListenerService>()
|
return new SutProvider<RabbitMqEventListenerService<TestListenerConfiguration>>()
|
||||||
.SetDependency(_rabbitMqService)
|
.SetDependency(_config)
|
||||||
.SetDependency(_logger)
|
|
||||||
.SetDependency(_queueName, "queueName")
|
|
||||||
.Create();
|
.Create();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,8 +32,8 @@ public class RabbitMqEventListenerServiceTests
|
|||||||
var cancellationToken = CancellationToken.None;
|
var cancellationToken = CancellationToken.None;
|
||||||
await sutProvider.Sut.StartAsync(cancellationToken);
|
await sutProvider.Sut.StartAsync(cancellationToken);
|
||||||
|
|
||||||
await _rabbitMqService.Received(1).CreateEventQueueAsync(
|
await sutProvider.GetDependency<IRabbitMqService>().Received(1).CreateEventQueueAsync(
|
||||||
Arg.Is(_queueName),
|
Arg.Is(_config.EventQueueName),
|
||||||
Arg.Is(cancellationToken)
|
Arg.Is(cancellationToken)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -52,11 +49,11 @@ public class RabbitMqEventListenerServiceTests
|
|||||||
exchange: string.Empty,
|
exchange: string.Empty,
|
||||||
routingKey: string.Empty,
|
routingKey: string.Empty,
|
||||||
new BasicProperties(),
|
new BasicProperties(),
|
||||||
body: new byte[0]);
|
body: Array.Empty<byte>());
|
||||||
|
|
||||||
await sutProvider.Sut.ProcessReceivedMessageAsync(eventArgs);
|
await sutProvider.Sut.ProcessReceivedMessageAsync(eventArgs);
|
||||||
|
|
||||||
_logger.Received(1).Log(
|
sutProvider.GetDependency<ILogger<RabbitMqEventListenerService<TestListenerConfiguration>>>().Received(1).Log(
|
||||||
LogLevel.Error,
|
LogLevel.Error,
|
||||||
Arg.Any<EventId>(),
|
Arg.Any<EventId>(),
|
||||||
Arg.Any<object>(),
|
Arg.Any<object>(),
|
||||||
@ -75,11 +72,11 @@ public class RabbitMqEventListenerServiceTests
|
|||||||
exchange: string.Empty,
|
exchange: string.Empty,
|
||||||
routingKey: string.Empty,
|
routingKey: string.Empty,
|
||||||
new BasicProperties(),
|
new BasicProperties(),
|
||||||
body: JsonSerializer.SerializeToUtf8Bytes("{ Inavlid JSON"));
|
body: JsonSerializer.SerializeToUtf8Bytes("{ Invalid JSON"));
|
||||||
|
|
||||||
await sutProvider.Sut.ProcessReceivedMessageAsync(eventArgs);
|
await sutProvider.Sut.ProcessReceivedMessageAsync(eventArgs);
|
||||||
|
|
||||||
_logger.Received(1).Log(
|
sutProvider.GetDependency<ILogger<RabbitMqEventListenerService<TestListenerConfiguration>>>().Received(1).Log(
|
||||||
LogLevel.Error,
|
LogLevel.Error,
|
||||||
Arg.Any<EventId>(),
|
Arg.Any<EventId>(),
|
||||||
Arg.Is<object>(o => o.ToString().Contains("Invalid JSON")),
|
Arg.Is<object>(o => o.ToString().Contains("Invalid JSON")),
|
||||||
@ -102,7 +99,7 @@ public class RabbitMqEventListenerServiceTests
|
|||||||
|
|
||||||
await sutProvider.Sut.ProcessReceivedMessageAsync(eventArgs);
|
await sutProvider.Sut.ProcessReceivedMessageAsync(eventArgs);
|
||||||
|
|
||||||
_logger.Received(1).Log(
|
sutProvider.GetDependency<ILogger<RabbitMqEventListenerService<TestListenerConfiguration>>>().Received(1).Log(
|
||||||
LogLevel.Error,
|
LogLevel.Error,
|
||||||
Arg.Any<EventId>(),
|
Arg.Any<EventId>(),
|
||||||
Arg.Any<object>(),
|
Arg.Any<object>(),
|
||||||
@ -125,7 +122,7 @@ public class RabbitMqEventListenerServiceTests
|
|||||||
|
|
||||||
await sutProvider.Sut.ProcessReceivedMessageAsync(eventArgs);
|
await sutProvider.Sut.ProcessReceivedMessageAsync(eventArgs);
|
||||||
|
|
||||||
_logger.Received(1).Log(
|
sutProvider.GetDependency<ILogger<RabbitMqEventListenerService<TestListenerConfiguration>>>().Received(1).Log(
|
||||||
LogLevel.Error,
|
LogLevel.Error,
|
||||||
Arg.Any<EventId>(),
|
Arg.Any<EventId>(),
|
||||||
Arg.Any<object>(),
|
Arg.Any<object>(),
|
||||||
|
|||||||
@ -15,23 +15,17 @@ namespace Bit.Core.Test.Services;
|
|||||||
[SutProviderCustomize]
|
[SutProviderCustomize]
|
||||||
public class RabbitMqIntegrationListenerServiceTests
|
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 DateTime _now = new DateTime(2014, 3, 2, 1, 0, 0, DateTimeKind.Utc);
|
||||||
private readonly IIntegrationHandler _handler = Substitute.For<IIntegrationHandler>();
|
private readonly IIntegrationHandler _handler = Substitute.For<IIntegrationHandler>();
|
||||||
private readonly IRabbitMqService _rabbitMqService = Substitute.For<IRabbitMqService>();
|
private readonly IRabbitMqService _rabbitMqService = Substitute.For<IRabbitMqService>();
|
||||||
|
private readonly TestListenerConfiguration _config = new();
|
||||||
|
|
||||||
private SutProvider<RabbitMqIntegrationListenerService> GetSutProvider()
|
private SutProvider<RabbitMqIntegrationListenerService<TestListenerConfiguration>> GetSutProvider()
|
||||||
{
|
{
|
||||||
var sutProvider = new SutProvider<RabbitMqIntegrationListenerService>()
|
var sutProvider = new SutProvider<RabbitMqIntegrationListenerService<TestListenerConfiguration>>()
|
||||||
|
.SetDependency(_config)
|
||||||
.SetDependency(_handler)
|
.SetDependency(_handler)
|
||||||
.SetDependency(_rabbitMqService)
|
.SetDependency(_rabbitMqService)
|
||||||
.SetDependency(_queueName, "queueName")
|
|
||||||
.SetDependency(_retryQueueName, "retryQueueName")
|
|
||||||
.SetDependency(_routingKey, "routingKey")
|
|
||||||
.SetDependency(_maxRetries, "maxRetries")
|
|
||||||
.WithFakeTimeProvider()
|
.WithFakeTimeProvider()
|
||||||
.Create();
|
.Create();
|
||||||
sutProvider.GetDependency<FakeTimeProvider>().SetUtcNow(_now);
|
sutProvider.GetDependency<FakeTimeProvider>().SetUtcNow(_now);
|
||||||
@ -46,10 +40,10 @@ public class RabbitMqIntegrationListenerServiceTests
|
|||||||
var cancellationToken = CancellationToken.None;
|
var cancellationToken = CancellationToken.None;
|
||||||
await sutProvider.Sut.StartAsync(cancellationToken);
|
await sutProvider.Sut.StartAsync(cancellationToken);
|
||||||
|
|
||||||
await _rabbitMqService.Received(1).CreateIntegrationQueuesAsync(
|
await sutProvider.GetDependency<IRabbitMqService>().Received(1).CreateIntegrationQueuesAsync(
|
||||||
Arg.Is(_queueName),
|
Arg.Is(_config.IntegrationQueueName),
|
||||||
Arg.Is(_retryQueueName),
|
Arg.Is(_config.IntegrationRetryQueueName),
|
||||||
Arg.Is(_routingKey),
|
Arg.Is(((IIntegrationListenerConfiguration)_config).RoutingKey),
|
||||||
Arg.Is(cancellationToken)
|
Arg.Is(cancellationToken)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -101,7 +95,7 @@ public class RabbitMqIntegrationListenerServiceTests
|
|||||||
await sutProvider.Sut.StartAsync(cancellationToken);
|
await sutProvider.Sut.StartAsync(cancellationToken);
|
||||||
|
|
||||||
message.DelayUntilDate = null;
|
message.DelayUntilDate = null;
|
||||||
message.RetryCount = _maxRetries;
|
message.RetryCount = _config.MaxRetries;
|
||||||
var eventArgs = new BasicDeliverEventArgs(
|
var eventArgs = new BasicDeliverEventArgs(
|
||||||
consumerTag: string.Empty,
|
consumerTag: string.Empty,
|
||||||
deliveryTag: 0,
|
deliveryTag: 0,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user