Browse Source

Merge pull request #23162 from suhaib-mousa/configuration-tenant-resolver

Configuration tenant resolver
pull/23210/head
Halil İbrahim Kalkan 11 months ago
committed by GitHub
parent
commit
e0fd62678f
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 17
      docs/en/framework/architecture/multi-tenancy/index.md
  2. 5
      framework/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/AbpTenantResolveOptions.cs
  3. 10
      framework/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/TenantResolverNames.cs
  4. 6
      framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantResolver.cs
  5. 1
      framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/MultiTenancy/AspNetCoreMultiTenancy_Without_DomainResolver_Tests.cs
  6. 87
      framework/test/Volo.Abp.MultiTenancy.Tests/Volo/Abp/MultiTenancy/FallbackTenantResolveContributor_Tests.cs

17
docs/en/framework/architecture/multi-tenancy/index.md

@ -380,6 +380,23 @@ namespace MultiTenancyDemo.Web
* A tenant resolver should set `context.TenantIdOrName` if it can determine it. If not, just leave it as is to allow the next resolver to determine it.
* `context.ServiceProvider` can be used if you need to additional services to resolve from the [dependency injection](../../fundamentals/dependency-injection.md) system.
##### Fallback Tenant
In some cases, the tenant cannot be resolved using any of the configured tenant resolvers. To handle such situations, ABP allows setting a **fallback tenant**.
The fallback tenant can be configured using the `FallbackTenant` property in `AbpTenantResolveOptions`:
```csharp
Configure<AbpTenantResolveOptions>(options =>
{
options.FallbackTenant = "default-tenant";
});
```
If no tenant is resolved and the `FallbackTenant` is not null or empty, ABP will automatically use this value as the current tenant. This provides a simple and consistent way to ensure that a tenant context is always available when needed.
> **Note:** The fallback tenant is only used if all other resolvers fail. It will never override an already resolved tenant.
#### Multi-Tenancy Middleware
Multi-Tenancy middleware is an ASP.NET Core request pipeline [middleware](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware) that determines the current tenant from the HTTP request and sets the `ICurrentTenant` properties.

5
framework/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/AbpTenantResolveOptions.cs

@ -8,6 +8,11 @@ public class AbpTenantResolveOptions
[NotNull]
public List<ITenantResolveContributor> TenantResolvers { get; }
/// <summary>
/// Fallback tenant to use when no other resolver resolves a tenant.
/// </summary>
public string? FallbackTenant { get; set; }
public AbpTenantResolveOptions()
{
TenantResolvers = new List<ITenantResolveContributor>();

10
framework/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/TenantResolverNames.cs

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Volo.Abp.MultiTenancy;
public static class TenantResolverNames
{
public const string FallbackTenant = "FallbackTenant";
}

6
framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantResolver.cs

@ -39,6 +39,12 @@ public class TenantResolver : ITenantResolver, ITransientDependency
}
}
if (result.TenantIdOrName.IsNullOrEmpty() && !string.IsNullOrWhiteSpace(_options.FallbackTenant))
{
result.TenantIdOrName = _options.FallbackTenant;
result.AppliedResolvers.Add(TenantResolverNames.FallbackTenant);
}
return result;
}
}

1
framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/MultiTenancy/AspNetCoreMultiTenancy_Without_DomainResolver_Tests.cs

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

87
framework/test/Volo.Abp.MultiTenancy.Tests/Volo/Abp/MultiTenancy/FallbackTenantResolveContributor_Tests.cs

@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Shouldly;
using Volo.Abp.MultiTenancy.ConfigurationStore;
using Xunit;
namespace Volo.Abp.MultiTenancy;
public class FallbackTenantResolveContributor_Tests : MultiTenancyTestBase
{
private readonly Guid _testTenantId = Guid.NewGuid();
private readonly string _testTenantName = "acme";
private readonly string _testTenantNormalizedName = "ACME";
private readonly AbpTenantResolveOptions _options;
private readonly ITenantResolver _tenantResolver;
public FallbackTenantResolveContributor_Tests()
{
_options = ServiceProvider.GetRequiredService<IOptions<AbpTenantResolveOptions>>().Value;
_tenantResolver = ServiceProvider.GetRequiredService<ITenantResolver>();
}
protected override void BeforeAddApplication(IServiceCollection services)
{
services.Configure<AbpDefaultTenantStoreOptions>(options =>
{
options.Tenants = new[]
{
new TenantConfiguration(_testTenantId, _testTenantName, _testTenantNormalizedName)
};
});
services.Configure<AbpTenantResolveOptions>(options =>
{
options.FallbackTenant = _testTenantName;
});
}
[Fact]
public async Task Should_Resolve_To_Fallback_Tenant_If_No_Other_Contributor_Succeeds()
{
var result = await _tenantResolver.ResolveTenantIdOrNameAsync();
result.TenantIdOrName.ShouldBe(_testTenantName);
result.AppliedResolvers.ShouldContain(TenantResolverNames.FallbackTenant);
}
[Fact]
public async Task Should_Not_Override_Resolved_Tenant()
{
// Arrange
var customTenantName = "resolved-tenant";
_options.TenantResolvers.Insert(0, new TestTenantResolveContributor(customTenantName));
// Act
var result = await _tenantResolver.ResolveTenantIdOrNameAsync();
// Assert
result.TenantIdOrName.ShouldBe(customTenantName);
result.AppliedResolvers.First().ShouldBe("Test");
result.AppliedResolvers.ShouldNotContain(TenantResolverNames.FallbackTenant);
}
public class TestTenantResolveContributor : TenantResolveContributorBase
{
private readonly string _tenant;
public TestTenantResolveContributor(string tenant)
{
_tenant = tenant;
}
public override string Name => "Test";
public override Task ResolveAsync(ITenantResolveContext context)
{
context.TenantIdOrName = _tenant;
return Task.CompletedTask;
}
}
}
Loading…
Cancel
Save