mirror of
https://github.com/bitwarden/server.git
synced 2025-04-04 12:40:22 -05:00
[PM-17562] Add Azure Service Bus for Distributed Events (#5382)
* [PM-17562] Add Azure Service Bus for Distributed Events * Fix failing test * Addressed issues mentioned in SonarQube * Respond to PR feedback * Respond to PR feedback - make webhook opt-in, remove message body from log
This commit is contained in:
parent
e01cace189
commit
02262476d6
@ -109,6 +109,21 @@ services:
|
|||||||
profiles:
|
profiles:
|
||||||
- proxy
|
- proxy
|
||||||
|
|
||||||
|
service-bus:
|
||||||
|
container_name: service-bus
|
||||||
|
image: mcr.microsoft.com/azure-messaging/servicebus-emulator:latest
|
||||||
|
pull_policy: always
|
||||||
|
volumes:
|
||||||
|
- "./servicebusemulator_config.json:/ServiceBus_Emulator/ConfigFiles/Config.json"
|
||||||
|
ports:
|
||||||
|
- "5672:5672"
|
||||||
|
environment:
|
||||||
|
SQL_SERVER: mssql
|
||||||
|
MSSQL_SA_PASSWORD: "${MSSQL_PASSWORD}"
|
||||||
|
ACCEPT_EULA: "Y"
|
||||||
|
profiles:
|
||||||
|
- servicebus
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
mssql_dev_data:
|
mssql_dev_data:
|
||||||
postgres_dev_data:
|
postgres_dev_data:
|
||||||
|
38
dev/servicebusemulator_config.json
Normal file
38
dev/servicebusemulator_config.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"UserConfig": {
|
||||||
|
"Namespaces": [
|
||||||
|
{
|
||||||
|
"Name": "sbemulatorns",
|
||||||
|
"Queues": [
|
||||||
|
{
|
||||||
|
"Name": "queue.1",
|
||||||
|
"Properties": {
|
||||||
|
"DeadLetteringOnMessageExpiration": false,
|
||||||
|
"DefaultMessageTimeToLive": "PT1H",
|
||||||
|
"DuplicateDetectionHistoryTimeWindow": "PT20S",
|
||||||
|
"ForwardDeadLetteredMessagesTo": "",
|
||||||
|
"ForwardTo": "",
|
||||||
|
"LockDuration": "PT1M",
|
||||||
|
"MaxDeliveryCount": 3,
|
||||||
|
"RequiresDuplicateDetection": false,
|
||||||
|
"RequiresSession": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Topics": [
|
||||||
|
{
|
||||||
|
"Name": "event-logging",
|
||||||
|
"Subscriptions": [
|
||||||
|
{
|
||||||
|
"Name": "events-write-subscription"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Logging": {
|
||||||
|
"Type": "File"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using Azure.Messaging.ServiceBus;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
|
using Bit.Core.Settings;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Bit.Core.Services;
|
||||||
|
|
||||||
|
public class AzureServiceBusEventListenerService : EventLoggingListenerService
|
||||||
|
{
|
||||||
|
private readonly ILogger<AzureServiceBusEventListenerService> _logger;
|
||||||
|
private readonly ServiceBusClient _client;
|
||||||
|
private readonly ServiceBusProcessor _processor;
|
||||||
|
|
||||||
|
public AzureServiceBusEventListenerService(
|
||||||
|
IEventMessageHandler handler,
|
||||||
|
ILogger<AzureServiceBusEventListenerService> logger,
|
||||||
|
GlobalSettings globalSettings,
|
||||||
|
string subscriptionName) : base(handler)
|
||||||
|
{
|
||||||
|
_client = new ServiceBusClient(globalSettings.EventLogging.AzureServiceBus.ConnectionString);
|
||||||
|
_processor = _client.CreateProcessor(globalSettings.EventLogging.AzureServiceBus.TopicName, subscriptionName, new ServiceBusProcessorOptions());
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_processor.ProcessMessageAsync += async args =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var eventMessage = JsonSerializer.Deserialize<EventMessage>(args.Message.Body.ToString());
|
||||||
|
|
||||||
|
await _handler.HandleEventAsync(eventMessage);
|
||||||
|
await args.CompleteMessageAsync(args.Message);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
_logger.LogError(
|
||||||
|
exception,
|
||||||
|
"An error occured while processing message: {MessageId}",
|
||||||
|
args.Message.MessageId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_processor.ProcessErrorAsync += args =>
|
||||||
|
{
|
||||||
|
_logger.LogError(
|
||||||
|
args.Exception,
|
||||||
|
"An error occurred. Entity Path: {EntityPath}, Error Source: {ErrorSource}",
|
||||||
|
args.EntityPath,
|
||||||
|
args.ErrorSource
|
||||||
|
);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
};
|
||||||
|
|
||||||
|
await _processor.StartProcessingAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await _processor.StopProcessingAsync(cancellationToken);
|
||||||
|
await base.StopAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
_processor.DisposeAsync().GetAwaiter().GetResult();
|
||||||
|
_client.DisposeAsync().GetAwaiter().GetResult();
|
||||||
|
base.Dispose();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using Azure.Messaging.ServiceBus;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
using Bit.Core.Settings;
|
||||||
|
|
||||||
|
namespace Bit.Core.AdminConsole.Services.Implementations;
|
||||||
|
|
||||||
|
public class AzureServiceBusEventWriteService : IEventWriteService, IAsyncDisposable
|
||||||
|
{
|
||||||
|
private readonly ServiceBusClient _client;
|
||||||
|
private readonly ServiceBusSender _sender;
|
||||||
|
|
||||||
|
public AzureServiceBusEventWriteService(GlobalSettings globalSettings)
|
||||||
|
{
|
||||||
|
_client = new ServiceBusClient(globalSettings.EventLogging.AzureServiceBus.ConnectionString);
|
||||||
|
_sender = _client.CreateSender(globalSettings.EventLogging.AzureServiceBus.TopicName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CreateAsync(IEvent e)
|
||||||
|
{
|
||||||
|
var message = new ServiceBusMessage(JsonSerializer.SerializeToUtf8Bytes(e))
|
||||||
|
{
|
||||||
|
ContentType = "application/json"
|
||||||
|
};
|
||||||
|
|
||||||
|
await _sender.SendMessageAsync(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CreateManyAsync(IEnumerable<IEvent> events)
|
||||||
|
{
|
||||||
|
foreach (var e in events)
|
||||||
|
{
|
||||||
|
await CreateAsync(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask DisposeAsync()
|
||||||
|
{
|
||||||
|
await _sender.DisposeAsync();
|
||||||
|
await _client.DisposeAsync();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
using Bit.Core.Models.Data;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Bit.Core.Services;
|
||||||
|
|
||||||
|
public class AzureTableStorageEventHandler(
|
||||||
|
[FromKeyedServices("persistent")] IEventWriteService eventWriteService)
|
||||||
|
: IEventMessageHandler
|
||||||
|
{
|
||||||
|
public Task HandleEventAsync(EventMessage eventMessage)
|
||||||
|
{
|
||||||
|
return eventWriteService.CreateManyAsync(EventTableEntity.IndexEvent(eventMessage));
|
||||||
|
}
|
||||||
|
}
|
@ -38,7 +38,10 @@ public class RabbitMqEventListenerService : EventLoggingListenerService
|
|||||||
_connection = await _factory.CreateConnectionAsync(cancellationToken);
|
_connection = await _factory.CreateConnectionAsync(cancellationToken);
|
||||||
_channel = await _connection.CreateChannelAsync(cancellationToken: cancellationToken);
|
_channel = await _connection.CreateChannelAsync(cancellationToken: cancellationToken);
|
||||||
|
|
||||||
await _channel.ExchangeDeclareAsync(exchange: _exchangeName, type: ExchangeType.Fanout, durable: true);
|
await _channel.ExchangeDeclareAsync(exchange: _exchangeName,
|
||||||
|
type: ExchangeType.Fanout,
|
||||||
|
durable: true,
|
||||||
|
cancellationToken: cancellationToken);
|
||||||
await _channel.QueueDeclareAsync(queue: _queueName,
|
await _channel.QueueDeclareAsync(queue: _queueName,
|
||||||
durable: true,
|
durable: true,
|
||||||
exclusive: false,
|
exclusive: false,
|
||||||
@ -52,7 +55,7 @@ public class RabbitMqEventListenerService : EventLoggingListenerService
|
|||||||
await base.StartAsync(cancellationToken);
|
await base.StartAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var consumer = new AsyncEventingBasicConsumer(_channel);
|
var consumer = new AsyncEventingBasicConsumer(_channel);
|
||||||
consumer.ReceivedAsync += async (_, eventArgs) =>
|
consumer.ReceivedAsync += async (_, eventArgs) =>
|
||||||
@ -68,18 +71,13 @@ public class RabbitMqEventListenerService : EventLoggingListenerService
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
await _channel.BasicConsumeAsync(_queueName, autoAck: true, consumer: consumer, cancellationToken: stoppingToken);
|
await _channel.BasicConsumeAsync(_queueName, autoAck: true, consumer: consumer, cancellationToken: cancellationToken);
|
||||||
|
|
||||||
while (!stoppingToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
await Task.Delay(1_000, stoppingToken);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task StopAsync(CancellationToken cancellationToken)
|
public override async Task StopAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
await _channel.CloseAsync();
|
await _channel.CloseAsync(cancellationToken);
|
||||||
await _connection.CloseAsync();
|
await _connection.CloseAsync(cancellationToken);
|
||||||
await base.StopAsync(cancellationToken);
|
await base.StopAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
@ -4,25 +4,25 @@ using Bit.Core.Settings;
|
|||||||
|
|
||||||
namespace Bit.Core.Services;
|
namespace Bit.Core.Services;
|
||||||
|
|
||||||
public class HttpPostEventHandler : IEventMessageHandler
|
public class WebhookEventHandler : IEventMessageHandler
|
||||||
{
|
{
|
||||||
private readonly HttpClient _httpClient;
|
private readonly HttpClient _httpClient;
|
||||||
private readonly string _httpPostUrl;
|
private readonly string _webhookUrl;
|
||||||
|
|
||||||
public const string HttpClientName = "HttpPostEventHandlerHttpClient";
|
public const string HttpClientName = "WebhookEventHandlerHttpClient";
|
||||||
|
|
||||||
public HttpPostEventHandler(
|
public WebhookEventHandler(
|
||||||
IHttpClientFactory httpClientFactory,
|
IHttpClientFactory httpClientFactory,
|
||||||
GlobalSettings globalSettings)
|
GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
_httpClient = httpClientFactory.CreateClient(HttpClientName);
|
_httpClient = httpClientFactory.CreateClient(HttpClientName);
|
||||||
_httpPostUrl = globalSettings.EventLogging.RabbitMq.HttpPostUrl;
|
_webhookUrl = globalSettings.EventLogging.WebhookUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task HandleEventAsync(EventMessage eventMessage)
|
public async Task HandleEventAsync(EventMessage eventMessage)
|
||||||
{
|
{
|
||||||
var content = JsonContent.Create(eventMessage);
|
var content = JsonContent.Create(eventMessage);
|
||||||
var response = await _httpClient.PostAsync(_httpPostUrl, content);
|
var response = await _httpClient.PostAsync(_webhookUrl, content);
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -260,8 +260,31 @@ public class GlobalSettings : IGlobalSettings
|
|||||||
|
|
||||||
public class EventLoggingSettings
|
public class EventLoggingSettings
|
||||||
{
|
{
|
||||||
|
public AzureServiceBusSettings AzureServiceBus { get; set; } = new AzureServiceBusSettings();
|
||||||
|
public virtual string WebhookUrl { get; set; }
|
||||||
public RabbitMqSettings RabbitMq { get; set; } = new RabbitMqSettings();
|
public RabbitMqSettings RabbitMq { get; set; } = new RabbitMqSettings();
|
||||||
|
|
||||||
|
public class AzureServiceBusSettings
|
||||||
|
{
|
||||||
|
private string _connectionString;
|
||||||
|
private string _topicName;
|
||||||
|
|
||||||
|
public virtual string EventRepositorySubscriptionName { get; set; } = "events-write-subscription";
|
||||||
|
public virtual string WebhookSubscriptionName { get; set; } = "events-webhook-subscription";
|
||||||
|
|
||||||
|
public string ConnectionString
|
||||||
|
{
|
||||||
|
get => _connectionString;
|
||||||
|
set => _connectionString = value.Trim('"');
|
||||||
|
}
|
||||||
|
|
||||||
|
public string TopicName
|
||||||
|
{
|
||||||
|
get => _topicName;
|
||||||
|
set => _topicName = value.Trim('"');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class RabbitMqSettings
|
public class RabbitMqSettings
|
||||||
{
|
{
|
||||||
private string _hostName;
|
private string _hostName;
|
||||||
@ -270,8 +293,7 @@ public class GlobalSettings : IGlobalSettings
|
|||||||
private string _exchangeName;
|
private string _exchangeName;
|
||||||
|
|
||||||
public virtual string EventRepositoryQueueName { get; set; } = "events-write-queue";
|
public virtual string EventRepositoryQueueName { get; set; } = "events-write-queue";
|
||||||
public virtual string HttpPostQueueName { get; set; } = "events-httpPost-queue";
|
public virtual string WebhookQueueName { get; set; } = "events-webhook-queue";
|
||||||
public virtual string HttpPostUrl { get; set; }
|
|
||||||
|
|
||||||
public string HostName
|
public string HostName
|
||||||
{
|
{
|
||||||
|
@ -95,20 +95,20 @@ public class Startup
|
|||||||
new RabbitMqEventListenerService(
|
new RabbitMqEventListenerService(
|
||||||
provider.GetRequiredService<EventRepositoryHandler>(),
|
provider.GetRequiredService<EventRepositoryHandler>(),
|
||||||
provider.GetRequiredService<ILogger<RabbitMqEventListenerService>>(),
|
provider.GetRequiredService<ILogger<RabbitMqEventListenerService>>(),
|
||||||
provider.GetRequiredService<GlobalSettings>(),
|
globalSettings,
|
||||||
globalSettings.EventLogging.RabbitMq.EventRepositoryQueueName));
|
globalSettings.EventLogging.RabbitMq.EventRepositoryQueueName));
|
||||||
|
|
||||||
if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.HttpPostUrl))
|
if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.WebhookUrl))
|
||||||
{
|
{
|
||||||
services.AddSingleton<HttpPostEventHandler>();
|
services.AddSingleton<WebhookEventHandler>();
|
||||||
services.AddHttpClient(HttpPostEventHandler.HttpClientName);
|
services.AddHttpClient(WebhookEventHandler.HttpClientName);
|
||||||
|
|
||||||
services.AddSingleton<IHostedService>(provider =>
|
services.AddSingleton<IHostedService>(provider =>
|
||||||
new RabbitMqEventListenerService(
|
new RabbitMqEventListenerService(
|
||||||
provider.GetRequiredService<HttpPostEventHandler>(),
|
provider.GetRequiredService<WebhookEventHandler>(),
|
||||||
provider.GetRequiredService<ILogger<RabbitMqEventListenerService>>(),
|
provider.GetRequiredService<ILogger<RabbitMqEventListenerService>>(),
|
||||||
provider.GetRequiredService<GlobalSettings>(),
|
globalSettings,
|
||||||
globalSettings.EventLogging.RabbitMq.HttpPostQueueName));
|
globalSettings.EventLogging.RabbitMq.WebhookQueueName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Settings;
|
using Bit.Core.Settings;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Bit.SharedWeb.Utilities;
|
using Bit.SharedWeb.Utilities;
|
||||||
using Microsoft.IdentityModel.Logging;
|
using Microsoft.IdentityModel.Logging;
|
||||||
|
using TableStorageRepos = Bit.Core.Repositories.TableStorage;
|
||||||
|
|
||||||
namespace Bit.EventsProcessor;
|
namespace Bit.EventsProcessor;
|
||||||
|
|
||||||
@ -24,9 +27,37 @@ public class Startup
|
|||||||
services.AddOptions();
|
services.AddOptions();
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
services.AddGlobalSettingsServices(Configuration, Environment);
|
var globalSettings = services.AddGlobalSettingsServices(Configuration, Environment);
|
||||||
|
|
||||||
// Hosted Services
|
// Hosted Services
|
||||||
|
|
||||||
|
// Optional Azure Service Bus Listeners
|
||||||
|
if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.ConnectionString) &&
|
||||||
|
CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.TopicName))
|
||||||
|
{
|
||||||
|
services.AddSingleton<IEventRepository, TableStorageRepos.EventRepository>();
|
||||||
|
services.AddSingleton<AzureTableStorageEventHandler>();
|
||||||
|
services.AddKeyedSingleton<IEventWriteService, RepositoryEventWriteService>("persistent");
|
||||||
|
services.AddSingleton<IHostedService>(provider =>
|
||||||
|
new AzureServiceBusEventListenerService(
|
||||||
|
provider.GetRequiredService<AzureTableStorageEventHandler>(),
|
||||||
|
provider.GetRequiredService<ILogger<AzureServiceBusEventListenerService>>(),
|
||||||
|
globalSettings,
|
||||||
|
globalSettings.EventLogging.AzureServiceBus.EventRepositorySubscriptionName));
|
||||||
|
|
||||||
|
if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.WebhookUrl))
|
||||||
|
{
|
||||||
|
services.AddSingleton<WebhookEventHandler>();
|
||||||
|
services.AddHttpClient(WebhookEventHandler.HttpClientName);
|
||||||
|
|
||||||
|
services.AddSingleton<IHostedService>(provider =>
|
||||||
|
new AzureServiceBusEventListenerService(
|
||||||
|
provider.GetRequiredService<WebhookEventHandler>(),
|
||||||
|
provider.GetRequiredService<ILogger<AzureServiceBusEventListenerService>>(),
|
||||||
|
globalSettings,
|
||||||
|
globalSettings.EventLogging.AzureServiceBus.WebhookSubscriptionName));
|
||||||
|
}
|
||||||
|
}
|
||||||
services.AddHostedService<AzureQueueHostedService>();
|
services.AddHostedService<AzureQueueHostedService>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,9 +320,17 @@ public static class ServiceCollectionExtensions
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!globalSettings.SelfHosted && CoreHelpers.SettingHasValue(globalSettings.Events.ConnectionString))
|
if (!globalSettings.SelfHosted && CoreHelpers.SettingHasValue(globalSettings.Events.ConnectionString))
|
||||||
|
{
|
||||||
|
if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.ConnectionString) &&
|
||||||
|
CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.TopicName))
|
||||||
|
{
|
||||||
|
services.AddSingleton<IEventWriteService, AzureServiceBusEventWriteService>();
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
services.AddSingleton<IEventWriteService, AzureQueueEventWriteService>();
|
services.AddSingleton<IEventWriteService, AzureQueueEventWriteService>();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (globalSettings.SelfHosted)
|
else if (globalSettings.SelfHosted)
|
||||||
{
|
{
|
||||||
if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.HostName) &&
|
if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.HostName) &&
|
||||||
|
@ -13,14 +13,14 @@ using GlobalSettings = Bit.Core.Settings.GlobalSettings;
|
|||||||
namespace Bit.Core.Test.Services;
|
namespace Bit.Core.Test.Services;
|
||||||
|
|
||||||
[SutProviderCustomize]
|
[SutProviderCustomize]
|
||||||
public class HttpPostEventHandlerTests
|
public class WebhookEventHandlerTests
|
||||||
{
|
{
|
||||||
private readonly MockedHttpMessageHandler _handler;
|
private readonly MockedHttpMessageHandler _handler;
|
||||||
private HttpClient _httpClient;
|
private HttpClient _httpClient;
|
||||||
|
|
||||||
private const string _httpPostUrl = "http://localhost/test/event";
|
private const string _webhookUrl = "http://localhost/test/event";
|
||||||
|
|
||||||
public HttpPostEventHandlerTests()
|
public WebhookEventHandlerTests()
|
||||||
{
|
{
|
||||||
_handler = new MockedHttpMessageHandler();
|
_handler = new MockedHttpMessageHandler();
|
||||||
_handler.Fallback
|
_handler.Fallback
|
||||||
@ -29,15 +29,15 @@ public class HttpPostEventHandlerTests
|
|||||||
_httpClient = _handler.ToHttpClient();
|
_httpClient = _handler.ToHttpClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
public SutProvider<HttpPostEventHandler> GetSutProvider()
|
public SutProvider<WebhookEventHandler> GetSutProvider()
|
||||||
{
|
{
|
||||||
var clientFactory = Substitute.For<IHttpClientFactory>();
|
var clientFactory = Substitute.For<IHttpClientFactory>();
|
||||||
clientFactory.CreateClient(HttpPostEventHandler.HttpClientName).Returns(_httpClient);
|
clientFactory.CreateClient(WebhookEventHandler.HttpClientName).Returns(_httpClient);
|
||||||
|
|
||||||
var globalSettings = new GlobalSettings();
|
var globalSettings = new GlobalSettings();
|
||||||
globalSettings.EventLogging.RabbitMq.HttpPostUrl = _httpPostUrl;
|
globalSettings.EventLogging.WebhookUrl = _webhookUrl;
|
||||||
|
|
||||||
return new SutProvider<HttpPostEventHandler>()
|
return new SutProvider<WebhookEventHandler>()
|
||||||
.SetDependency(globalSettings)
|
.SetDependency(globalSettings)
|
||||||
.SetDependency(clientFactory)
|
.SetDependency(clientFactory)
|
||||||
.Create();
|
.Create();
|
||||||
@ -51,7 +51,7 @@ public class HttpPostEventHandlerTests
|
|||||||
|
|
||||||
await sutProvider.Sut.HandleEventAsync(eventMessage);
|
await sutProvider.Sut.HandleEventAsync(eventMessage);
|
||||||
sutProvider.GetDependency<IHttpClientFactory>().Received(1).CreateClient(
|
sutProvider.GetDependency<IHttpClientFactory>().Received(1).CreateClient(
|
||||||
Arg.Is(AssertHelper.AssertPropertyEqual<string>(HttpPostEventHandler.HttpClientName))
|
Arg.Is(AssertHelper.AssertPropertyEqual<string>(WebhookEventHandler.HttpClientName))
|
||||||
);
|
);
|
||||||
|
|
||||||
Assert.Single(_handler.CapturedRequests);
|
Assert.Single(_handler.CapturedRequests);
|
||||||
@ -60,7 +60,7 @@ public class HttpPostEventHandlerTests
|
|||||||
var returned = await request.Content.ReadFromJsonAsync<EventMessage>();
|
var returned = await request.Content.ReadFromJsonAsync<EventMessage>();
|
||||||
|
|
||||||
Assert.Equal(HttpMethod.Post, request.Method);
|
Assert.Equal(HttpMethod.Post, request.Method);
|
||||||
Assert.Equal(_httpPostUrl, request.RequestUri.ToString());
|
Assert.Equal(_webhookUrl, request.RequestUri.ToString());
|
||||||
AssertHelper.AssertPropertyEqual(eventMessage, returned, new[] { "IdempotencyId" });
|
AssertHelper.AssertPropertyEqual(eventMessage, returned, new[] { "IdempotencyId" });
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user