From d0d44277793428c44cef61b71f34b5a27940a5e5 Mon Sep 17 00:00:00 2001 From: Suhaib Mousa Date: Mon, 28 Apr 2025 11:03:52 +0300 Subject: [PATCH] Introduce ITenantNameValidator for extensible tenant name validation --- .../DefaultTenantNameValidator.cs | 28 +++++++++++++++ .../TenantManagement/ITenantNameValidator.cs | 9 +++++ .../Abp/TenantManagement/TenantManager.cs | 19 +++------- .../TenantNameValidator_Tests.cs | 35 +++++++++++++++++++ 4 files changed, 77 insertions(+), 14 deletions(-) create mode 100644 modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/DefaultTenantNameValidator.cs create mode 100644 modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/ITenantNameValidator.cs create mode 100644 modules/tenant-management/test/Volo.Abp.TenantManagement.Domain.Tests/Volo/Abp/TenantManagement/TenantNameValidator_Tests.cs diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/DefaultTenantNameValidator.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/DefaultTenantNameValidator.cs new file mode 100644 index 0000000000..0b348e17bc --- /dev/null +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/DefaultTenantNameValidator.cs @@ -0,0 +1,28 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Services; + +namespace Volo.Abp.TenantManagement; + +public class DefaultTenantNameValidator : ITenantNameValidator, ITransientDependency +{ + protected ITenantRepository TenantRepository { get; } + + public DefaultTenantNameValidator(ITenantRepository tenantRepository) + { + TenantRepository = tenantRepository; + } + + public virtual async Task ValidateAsync(string normalizedTenantName, Guid? expectedTenantId = null) + { + Check.NotNullOrWhiteSpace(normalizedTenantName, nameof(normalizedTenantName)); + + var existingTenant = await TenantRepository.FindByNameAsync(normalizedTenantName); + if (existingTenant != null && existingTenant.Id != expectedTenantId) + { + throw new BusinessException("Volo.Abp.TenantManagement:DuplicateTenantName") + .WithData("Name", normalizedTenantName); + } + } +} \ No newline at end of file diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/ITenantNameValidator.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/ITenantNameValidator.cs new file mode 100644 index 0000000000..41371aacec --- /dev/null +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/ITenantNameValidator.cs @@ -0,0 +1,9 @@ +using System; +using System.Threading.Tasks; + +namespace Volo.Abp.TenantManagement; + +public interface ITenantNameValidator +{ + Task ValidateAsync(string normalizedTenantName, Guid? expectedTenantId = null); +} \ No newline at end of file diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantManager.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantManager.cs index 0f40cfdd0b..e5aa664336 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantManager.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantManager.cs @@ -9,16 +9,16 @@ namespace Volo.Abp.TenantManagement; public class TenantManager : DomainService, ITenantManager { - protected ITenantRepository TenantRepository { get; } + public ITenantNameValidator TenantNameValidator { get; } protected ITenantNormalizer TenantNormalizer { get; } protected ILocalEventBus LocalEventBus { get; } public TenantManager( - ITenantRepository tenantRepository, + ITenantNameValidator tenantNameValidator, ITenantNormalizer tenantNormalizer, ILocalEventBus localEventBus) { - TenantRepository = tenantRepository; + TenantNameValidator = tenantNameValidator; TenantNormalizer = tenantNormalizer; LocalEventBus = localEventBus; } @@ -28,7 +28,7 @@ public class TenantManager : DomainService, ITenantManager Check.NotNull(name, nameof(name)); var normalizedName = TenantNormalizer.NormalizeName(name); - await ValidateNameAsync(normalizedName); + await TenantNameValidator.ValidateAsync(normalizedName); return new Tenant(GuidGenerator.Create(), name, normalizedName); } @@ -39,18 +39,9 @@ public class TenantManager : DomainService, ITenantManager var normalizedName = TenantNormalizer.NormalizeName(name); - await ValidateNameAsync(normalizedName, tenant.Id); + await TenantNameValidator.ValidateAsync(normalizedName, tenant.Id); await LocalEventBus.PublishAsync(new TenantChangedEvent(tenant.Id, tenant.NormalizedName)); tenant.SetName(name); tenant.SetNormalizedName(normalizedName); } - - protected virtual async Task ValidateNameAsync(string normalizeName, Guid? expectedId = null) - { - var tenant = await TenantRepository.FindByNameAsync(normalizeName); - if (tenant != null && tenant.Id != expectedId) - { - throw new BusinessException("Volo.Abp.TenantManagement:DuplicateTenantName").WithData("Name", normalizeName); - } - } } diff --git a/modules/tenant-management/test/Volo.Abp.TenantManagement.Domain.Tests/Volo/Abp/TenantManagement/TenantNameValidator_Tests.cs b/modules/tenant-management/test/Volo.Abp.TenantManagement.Domain.Tests/Volo/Abp/TenantManagement/TenantNameValidator_Tests.cs new file mode 100644 index 0000000000..cf93821384 --- /dev/null +++ b/modules/tenant-management/test/Volo.Abp.TenantManagement.Domain.Tests/Volo/Abp/TenantManagement/TenantNameValidator_Tests.cs @@ -0,0 +1,35 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.Guids; +using Xunit; + +namespace Volo.Abp.TenantManagement; + +public class TenantNameValidator_Tests : AbpTenantManagementDomainTestBase +{ + private readonly ITenantNameValidator _tenantNameValidator; + private readonly ITenantRepository _tenantRepository; + public TenantNameValidator_Tests() + { + _tenantRepository = GetRequiredService(); + _tenantNameValidator = GetRequiredService(); + } + + [Fact] + public async Task Should_Throw_If_Name_Is_Null() + { + await Assert.ThrowsAsync(() => _tenantNameValidator.ValidateAsync(null)); + } + + [Fact] + public async Task Should_Throw_If_Duplicate_Name() + { + await Assert.ThrowsAsync(() => _tenantNameValidator.ValidateAsync("VOLOSOFT")); + } + + [Fact] + public async Task Should_Not_Throw_For_Unique_Name() + { + await _tenantNameValidator.ValidateAsync("UNIQUE-TENANT-NAME"); + } +}