diff --git a/src/Messaging/NBB.Messaging.MultiTenancy/NBB.Messaging.MultiTenancy.csproj b/src/Messaging/NBB.Messaging.MultiTenancy/NBB.Messaging.MultiTenancy.csproj index 1db817d0..5dbd641f 100644 --- a/src/Messaging/NBB.Messaging.MultiTenancy/NBB.Messaging.MultiTenancy.csproj +++ b/src/Messaging/NBB.Messaging.MultiTenancy/NBB.Messaging.MultiTenancy.csproj @@ -15,6 +15,7 @@ + diff --git a/src/Messaging/NBB.Messaging.MultiTenancy/TenantMiddleware.cs b/src/Messaging/NBB.Messaging.MultiTenancy/TenantMiddleware.cs index 5f4614ab..9dfd32f0 100644 --- a/src/Messaging/NBB.Messaging.MultiTenancy/TenantMiddleware.cs +++ b/src/Messaging/NBB.Messaging.MultiTenancy/TenantMiddleware.cs @@ -13,6 +13,9 @@ using NBB.MultiTenancy.Identification.Services; using NBB.MultiTenancy.Abstractions; using System.Diagnostics; +using Microsoft.Extensions.Logging; +using MediatR; +using System.Linq; namespace NBB.Messaging.MultiTenancy { @@ -21,45 +24,80 @@ namespace NBB.Messaging.MultiTenancy /// obtained from the current identification strategy and builds the tenant context. /// /// - public class TenantMiddleware : IPipelineMiddleware - { - private readonly ITenantContextAccessor _tenantContextAccessor; - private readonly ITenantIdentificationService _tenantIdentificationService; - private readonly IOptions _tenancyOptions; - private readonly ITenantRepository _tenantRepository; - - public TenantMiddleware(ITenantContextAccessor tenantContextAccessor, ITenantIdentificationService tenantIdentificationService, IOptions tenancyOptions, ITenantRepository tenantRepository) - { - _tenantContextAccessor = tenantContextAccessor; - _tenantIdentificationService = tenantIdentificationService; - _tenancyOptions = tenancyOptions; - _tenantRepository = tenantRepository; - } - + public class TenantMiddleware( + ITenantContextAccessor tenantContextAccessor, + ITenantIdentificationService tenantIdentificationService, + IOptions tenancyOptions, + ITenantRepository tenantRepository, + ILogger logger + ) : IPipelineMiddleware + { public async Task Invoke(MessagingContext context, CancellationToken cancellationToken, Func next) { - if (_tenantContextAccessor.TenantContext != null) + if (tenantContextAccessor.TenantContext != null) { throw new ApplicationException("Tenant context is already set"); } - if (_tenancyOptions.Value.TenancyType == TenancyType.MonoTenant) + if (tenancyOptions.Value.TenancyType == TenancyType.MonoTenant) { - _tenantContextAccessor.TenantContext = new TenantContext(Tenant.Default); + tenantContextAccessor.TenantContext = new TenantContext(Tenant.Default); await next(); return; } - var tenantId = await _tenantIdentificationService.GetTenantIdAsync(); - var tenant = await _tenantRepository.Get(tenantId, cancellationToken) - ?? throw new ApplicationException($"Tenant {tenantId} not found"); + Tenant tenant; + + if (context.MessagingEnvelope.Payload is INotification) + { + tenant = await TryLoadTenant(context.TopicName, cancellationToken); + if (tenant == null) + { + return; + } + } + else + { + tenant = await LoadTenant(cancellationToken); + } - _tenantContextAccessor.TenantContext = new TenantContext(tenant); - Activity.Current?.SetTag(TracingTags.TenantId, tenantId); + tenantContextAccessor.TenantContext = new TenantContext(tenant); + + Activity.Current?.SetTag(TracingTags.TenantId, tenant.TenantId); await next(); } + + private async Task LoadTenant(CancellationToken cancellationToken) + { + var tenantId = await tenantIdentificationService.GetTenantIdAsync(); + var tenant = await tenantRepository.Get(tenantId, cancellationToken) + ?? throw new ApplicationException($"Tenant {tenantId} not found"); + + return tenant; + } + + + private async Task TryLoadTenant(string topic, CancellationToken cancellationToken) + { + var tenantId = await tenantIdentificationService.TryGetTenantIdAsync(); + if (!tenantId.HasValue) + { + logger.LogDebug("Tenant could not be identified. Message {Topic} will be ignored.", topic); + return null; + } + + var tenant = await tenantRepository.TryGet(tenantId.Value, cancellationToken); + if (tenant == null) + { + logger.LogDebug("Tenant {Tenant} not found or not enabled. Message {Topic} will be ignored.", tenantId.Value, topic); + return null; + } + + return tenant; + } + } diff --git a/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/CachedTenantRepositoryDecorator.cs b/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/CachedTenantRepositoryDecorator.cs index d9716f41..8c5aadec 100644 --- a/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/CachedTenantRepositoryDecorator.cs +++ b/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/CachedTenantRepositoryDecorator.cs @@ -23,7 +23,7 @@ public CachedTenantRepositoryDecorator(ITenantRepository tenantRepository, IDist _cache = cache; } - public async Task Get(Guid id, CancellationToken token) + public async Task TryGet(Guid id, CancellationToken token) { var cacheKey = CacheTenantByIdKey(id); var cachedTenant = await GetTenantFromCache(cacheKey, token); @@ -31,8 +31,8 @@ public async Task Get(Guid id, CancellationToken token) { return cachedTenant; } - - var dbTenant = await _tenantRepository.Get(id, token); + + var dbTenant = await _tenantRepository.TryGet(id, token); if (dbTenant == null) { return null; diff --git a/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ConfigurationTenantRepository.cs b/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ConfigurationTenantRepository.cs index 73c69efe..b1830014 100644 --- a/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ConfigurationTenantRepository.cs +++ b/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ConfigurationTenantRepository.cs @@ -43,15 +43,14 @@ public ConfigurationTenantRepository(IConfiguration configuration, IOptions Get(Guid id, CancellationToken token = default) + public Task TryGet(Guid id, CancellationToken token = default) { - if (!tenantMap.TryGetValue(id, out var result)) + if (!tenantMap.TryGetValue(id, out var result) || !result.Enabled) { - throw new TenantNotFoundException(id); + return Task.FromResult(default(Tenant)); } - - return Task.FromResult(result.Enabled ? result : throw new Exception($"Tenant {result.Code} is disabled ")); + return Task.FromResult(result); } public Task GetByHost(string host, CancellationToken token = default) diff --git a/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ITenantRepository.cs b/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ITenantRepository.cs index 9293e0e1..0ebf09d1 100644 --- a/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ITenantRepository.cs +++ b/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ITenantRepository.cs @@ -10,8 +10,11 @@ namespace NBB.MultiTenancy.Abstractions.Repositories { public interface ITenantRepository { - Task Get(Guid id, CancellationToken token = default); + Task TryGet(Guid id, CancellationToken token = default); Task GetByHost(string host, CancellationToken token = default); Task> GetAll(CancellationToken token = default); + + async Task Get(Guid id, CancellationToken token = default) + => await TryGet(id, token) ?? throw new TenantNotFoundException(id); } } diff --git a/test/UnitTests/MultiTenancy/NBB.MultiTenancy.Configuration.Tests/ConfigurationTenantRepositoryTests.cs b/test/UnitTests/MultiTenancy/NBB.MultiTenancy.Configuration.Tests/ConfigurationTenantRepositoryTests.cs index a4602ca5..b9b8456c 100644 --- a/test/UnitTests/MultiTenancy/NBB.MultiTenancy.Configuration.Tests/ConfigurationTenantRepositoryTests.cs +++ b/test/UnitTests/MultiTenancy/NBB.MultiTenancy.Configuration.Tests/ConfigurationTenantRepositoryTests.cs @@ -190,14 +190,14 @@ public async Task get_tenant_should_throw_for_disabled_tenant() var options = new OptionsWrapper(tenancyHostingOptions); - var repo = new ConfigurationTenantRepository(configuration, options); + ITenantRepository repo = new ConfigurationTenantRepository(configuration, options); //Act Func action = async() => await repo.Get(Guid.Parse(tenantId)); //Assert - await action.Should().ThrowAsync().WithMessage("*disabled*"); + await action.Should().ThrowAsync(); } [Fact] @@ -232,7 +232,7 @@ public async Task get_should_bind_tenant_code_from_section_name() var repo = new ConfigurationTenantRepository(configuration, options); //arrange - var actual = await repo.Get(System.Guid.Parse("ef8d5362-9969-4e02-8794-0d1af56816f6")); + var actual = await repo.TryGet(Guid.Parse("ef8d5362-9969-4e02-8794-0d1af56816f6")); // Assert actual.Should().NotBeNull();