Browse Source

Revised tenant resolve and added query string resolver.

pull/81/head
Halil İbrahim Kalkan 10 years ago
parent
commit
458dd7e6ba
  1. 5
      src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetMultiTenancyModule.cs
  2. 31
      src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolver.cs
  3. 4
      src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/AbpAspNetCoreModule.cs
  4. 15
      src/Volo.Abp.AspNetCore/Volo/Abp/ServiceProviderAccessorExtensions.cs
  5. 4
      src/Volo.Abp.AspNetCore/project.json
  6. 16
      src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenantResolveContext.cs
  7. 4
      src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ICurrentTenantResolveContext.cs
  8. 2
      src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ITenantScopeProvider.cs
  9. 24
      src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenancyManager.cs
  10. 2
      src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenancyOptions.cs
  11. 7
      src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantInfo.cs
  12. 9
      src/Volo.Abp/Volo/Abp/IServiceProviderAccessor.cs
  13. 2
      test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/App/AppModule.cs
  14. 13
      test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/MultiTenancy/AspNetCoreMultiTenancy_Tests.cs
  15. 17
      test/Volo.Abp.MultiTenancy.Tests/Volo/Abp/MultiTenancy/AsyncLocalAmbientTenantScopeProvider_Tests.cs
  16. 25
      test/Volo.Abp.MultiTenancy.Tests/Volo/Abp/MultiTenancy/MultiTenantManager_TenantResolver_Tests.cs

5
src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetMultiTenancyModule.cs

@ -9,6 +9,11 @@ namespace Volo.Abp.AspNetCore.MultiTenancy
{ {
public override void ConfigureServices(IServiceCollection services) public override void ConfigureServices(IServiceCollection services)
{ {
services.Configure<MultiTenancyOptions>(options =>
{
options.TenantResolvers.Insert(0, new QueryStringTenantResolver());
});
services.AddAssemblyOf<AbpAspNetMultiTenancyModule>(); services.AddAssemblyOf<AbpAspNetMultiTenancyModule>();
} }
} }

31
src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolver.cs

@ -0,0 +1,31 @@
using Volo.Abp.MultiTenancy;
using Volo.DependencyInjection;
using Volo.ExtensionMethods.Collections.Generic;
namespace Volo.Abp.AspNetCore.MultiTenancy
{
public class QueryStringTenantResolver : ITenantResolver, ITransientDependency
{
public const string TenantIdKey = "_Abp.TenantId";
public void Resolve(ICurrentTenantResolveContext context)
{
var httpContext = context.GetHttpContext();
if (httpContext == null)
{
return;
}
if (!httpContext.Request.QueryString.HasValue)
{
return;
}
var tenantId = httpContext.Request.Query[TenantIdKey];
if (!tenantId.IsNullOrEmpty())
{
context.Tenant = new TenantInfo(tenantId);
}
}
}
}

4
src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/AbpAspNetCoreModule.cs

@ -1,5 +1,7 @@
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
namespace Volo.Abp.AspNetCore namespace Volo.Abp.AspNetCore
@ -8,6 +10,8 @@ namespace Volo.Abp.AspNetCore
{ {
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ {
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddObjectAccessor<IApplicationBuilder>(); services.AddObjectAccessor<IApplicationBuilder>();
services.AddAssemblyOf<AbpAspNetCoreModule>(); services.AddAssemblyOf<AbpAspNetCoreModule>();
} }

15
src/Volo.Abp.AspNetCore/Volo/Abp/ServiceProviderAccessorExtensions.cs

@ -0,0 +1,15 @@
using JetBrains.Annotations;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
namespace Volo.Abp
{
public static class ServiceProviderAccessorExtensions
{
[CanBeNull]
public static HttpContext GetHttpContext(this IServiceProviderAccessor serviceProviderAccessor)
{
return serviceProviderAccessor.ServiceProvider.GetRequiredService<IHttpContextAccessor>().HttpContext;
}
}
}

4
src/Volo.Abp.AspNetCore/project.json

@ -4,9 +4,9 @@
"dependencies": { "dependencies": {
"NETStandard.Library": "1.6.1", "NETStandard.Library": "1.6.1",
"Volo.Abp": "1.0.0-*", "Volo.Abp": "1.0.0-*",
"Microsoft.AspNetCore.Http.Abstractions": "1.1.0",
"Microsoft.AspNetCore.Hosting.Abstractions": "1.1.0", "Microsoft.AspNetCore.Hosting.Abstractions": "1.1.0",
"Microsoft.AspNetCore.Routing.Abstractions": "1.1.0" "Microsoft.AspNetCore.Routing.Abstractions": "1.1.0",
"Microsoft.AspNetCore.Http": "1.1.0"
}, },
"frameworks": { "frameworks": {

16
src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenantResolveContext.cs

@ -1,9 +1,23 @@
using System;
namespace Volo.Abp.MultiTenancy namespace Volo.Abp.MultiTenancy
{ {
public class CurrentTenantResolveContext : ICurrentTenantResolveContext public class CurrentTenantResolveContext : ICurrentTenantResolveContext
{ {
public IServiceProvider ServiceProvider { get; }
public TenantInfo Tenant { get; set; } public TenantInfo Tenant { get; set; }
public bool Handled { get; set; } public bool? Handled { get; set; }
internal bool IsHandled()
{
return Handled == true || (Handled == null && Tenant != null);
}
public CurrentTenantResolveContext(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
}
} }
} }

4
src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ICurrentTenantResolveContext.cs

@ -1,9 +1,9 @@
namespace Volo.Abp.MultiTenancy namespace Volo.Abp.MultiTenancy
{ {
public interface ICurrentTenantResolveContext public interface ICurrentTenantResolveContext : IServiceProviderAccessor
{ {
TenantInfo Tenant { get; set; } TenantInfo Tenant { get; set; }
bool Handled { get; set; } bool? Handled { get; set; }
} }
} }

2
src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ITenantScopeProvider.cs

@ -1,9 +1,11 @@
using System; using System;
using JetBrains.Annotations;
namespace Volo.Abp.MultiTenancy namespace Volo.Abp.MultiTenancy
{ {
public interface ITenantScopeProvider public interface ITenantScopeProvider
{ {
[CanBeNull]
TenantScope CurrentScope { get; } TenantScope CurrentScope { get; }
IDisposable EnterScope(TenantInfo tenantInfo); IDisposable EnterScope(TenantInfo tenantInfo);

24
src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenancyManager.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Linq;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Volo.DependencyInjection; using Volo.DependencyInjection;
@ -8,13 +9,16 @@ namespace Volo.Abp.MultiTenancy
{ {
public TenantInfo CurrentTenant => GetCurrentTenant(); public TenantInfo CurrentTenant => GetCurrentTenant();
private readonly IServiceProvider _serviceProvider;
private readonly ITenantScopeProvider _tenantScopeProvider; private readonly ITenantScopeProvider _tenantScopeProvider;
private readonly MultiTenancyOptions _options; private readonly MultiTenancyOptions _options;
public MultiTenancyManager( public MultiTenancyManager(
IServiceProvider serviceProvider,
ITenantScopeProvider tenantScopeProvider, ITenantScopeProvider tenantScopeProvider,
IOptions<MultiTenancyOptions> options) IOptions<MultiTenancyOptions> options)
{ {
_serviceProvider = serviceProvider;
_tenantScopeProvider = tenantScopeProvider; _tenantScopeProvider = tenantScopeProvider;
_options = options.Value; _options = options.Value;
} }
@ -26,15 +30,27 @@ namespace Volo.Abp.MultiTenancy
return _tenantScopeProvider.CurrentScope.Tenant; return _tenantScopeProvider.CurrentScope.Tenant;
} }
var context = new CurrentTenantResolveContext(); return GetCurrentTenantFromResolvers();
}
foreach (var currentTenantResolver in _options.TenantResolvers) protected virtual TenantInfo GetCurrentTenantFromResolvers()
{
if (!_options.TenantResolvers.Any())
{ {
currentTenantResolver.Resolve(context); return null;
if (context.Handled) }
var context = new CurrentTenantResolveContext(_serviceProvider);
foreach (var tenantResolver in _options.TenantResolvers)
{
tenantResolver.Resolve(context);
if (context.IsHandled())
{ {
break; break;
} }
context.Handled = null;
} }
return context.Tenant; return context.Tenant;

2
src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenancyOptions.cs

@ -1,9 +1,11 @@
using System.Collections.Generic; using System.Collections.Generic;
using JetBrains.Annotations;
namespace Volo.Abp.MultiTenancy namespace Volo.Abp.MultiTenancy
{ {
public class MultiTenancyOptions public class MultiTenancyOptions
{ {
[NotNull]
public List<ITenantResolver> TenantResolvers { get; } public List<ITenantResolver> TenantResolvers { get; }
public MultiTenancyOptions() public MultiTenancyOptions()

7
src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantInfo.cs

@ -4,10 +4,9 @@ namespace Volo.Abp.MultiTenancy
{ {
public class TenantInfo public class TenantInfo
{ {
[NotNull]
public string Id { get; } public string Id { get; }
public string Name { get; }
//TODO: Needed for serialization //TODO: Needed for serialization
[UsedImplicitly] [UsedImplicitly]
private TenantInfo() private TenantInfo()
@ -15,13 +14,11 @@ namespace Volo.Abp.MultiTenancy
} }
public TenantInfo([NotNull] string id, [NotNull] string name) public TenantInfo([NotNull] string id)
{ {
Check.NotNull(id, nameof(id)); Check.NotNull(id, nameof(id));
Check.NotNull(name, nameof(name));
Id = id; Id = id;
Name = name;
} }
} }
} }

9
src/Volo.Abp/Volo/Abp/IServiceProviderAccessor.cs

@ -0,0 +1,9 @@
using System;
namespace Volo.Abp
{
public interface IServiceProviderAccessor
{
IServiceProvider ServiceProvider { get; }
}
}

2
test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/App/AppModule.cs

@ -27,7 +27,7 @@ namespace Volo.Abp.AspNetCore.App
var dictionary = new Dictionary<string, string> var dictionary = new Dictionary<string, string>
{ {
["TenantName"] = manager.CurrentTenant?.Name ?? "<host>" ["TenantId"] = manager.CurrentTenant?.Id ?? ""
}; };
await ctx.Response.WriteAsync(dictionary.ToJsonString()); await ctx.Response.WriteAsync(dictionary.ToJsonString());

13
test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/MultiTenancy/AspNetCoreMultiTenancy_Tests.cs

@ -12,9 +12,18 @@ namespace Volo.Abp.AspNetCore.MultiTenancy
public async Task Should_Use_Host_If_Tenant_Is_Not_Specified() public async Task Should_Use_Host_If_Tenant_Is_Not_Specified()
{ {
var result = await GetResponseAsObjectAsync<Dictionary<string, string>>("http://abp.io"); var result = await GetResponseAsObjectAsync<Dictionary<string, string>>("http://abp.io");
result["TenantName"].ShouldBe("<host>"); result["TenantId"].ShouldBe("");
} }
//TODO: Specify tenant in the header, cookie, url, querystring and so on... [Fact]
public async Task Should_Use_QueryString_Tenant_Id_If_Specified()
{
const string testTenantId = "42";
var result = await GetResponseAsObjectAsync<Dictionary<string, string>>($"http://abp.io?{QueryStringTenantResolver.TenantIdKey}={testTenantId}");
result["TenantId"].ShouldBe(testTenantId);
}
//TODO: Specify tenant in the header, cookie, domain, subdomain, route, querystring and so on...
} }
} }

17
test/Volo.Abp.MultiTenancy.Tests/Volo/Abp/MultiTenancy/AsyncLocalAmbientTenantScopeProvider_Tests.cs

@ -10,25 +10,26 @@ namespace Volo.Abp.MultiTenancy
{ {
var scopeProvider = new AsyncLocalTenantScopeProvider(); var scopeProvider = new AsyncLocalTenantScopeProvider();
scopeProvider.CurrentScope.ShouldBeNull(); Assert.Null(scopeProvider.CurrentScope?.Tenant);
using (scopeProvider.EnterScope(new TenantInfo("1","A"))) using (scopeProvider.EnterScope(new TenantInfo("A")))
{ {
scopeProvider.CurrentScope.Tenant.Name.ShouldBe("A"); Assert.Equal("A", scopeProvider.CurrentScope?.Tenant?.Id);
using (scopeProvider.EnterScope(new TenantInfo("2", "B"))) using (scopeProvider.EnterScope(new TenantInfo("B")))
{ {
scopeProvider.CurrentScope.Tenant.Name.ShouldBe("B"); Assert.Equal("B", scopeProvider.CurrentScope?.Tenant?.Id);
using (scopeProvider.EnterScope(null)) using (scopeProvider.EnterScope(null))
{ {
scopeProvider.CurrentScope.Tenant.ShouldBeNull(); Assert.NotNull(scopeProvider.CurrentScope);
Assert.Null(scopeProvider.CurrentScope.Tenant);
} }
scopeProvider.CurrentScope.Tenant.Name.ShouldBe("B"); Assert.Equal("B", scopeProvider.CurrentScope?.Tenant?.Id);
} }
scopeProvider.CurrentScope.Tenant.Name.ShouldBe("A"); Assert.Equal("A", scopeProvider.CurrentScope?.Tenant?.Id);
} }
scopeProvider.CurrentScope.ShouldBeNull(); scopeProvider.CurrentScope.ShouldBeNull();

25
test/Volo.Abp.MultiTenancy.Tests/Volo/Abp/MultiTenancy/MultiTenantManager_TenantResolver_Tests.cs

@ -14,6 +14,7 @@ namespace Volo.Abp.MultiTenancy
//Arrange //Arrange
var manager = new MultiTenancyManager( var manager = new MultiTenancyManager(
Substitute.For<IServiceProvider>(),
Substitute.For<ITenantScopeProvider>(), Substitute.For<ITenantScopeProvider>(),
new OptionsWrapper<MultiTenancyOptions>(new MultiTenancyOptions()) new OptionsWrapper<MultiTenancyOptions>(new MultiTenancyOptions())
); );
@ -28,9 +29,10 @@ namespace Volo.Abp.MultiTenancy
{ {
//Arrange //Arrange
var fakeTenant = new TenantInfo(Guid.NewGuid().ToString(), "acme"); var fakeTenant = new TenantInfo("A");
var manager = new MultiTenancyManager( var manager = new MultiTenancyManager(
Substitute.For<IServiceProvider>(),
Substitute.For<ITenantScopeProvider>(), Substitute.For<ITenantScopeProvider>(),
new OptionsWrapper<MultiTenancyOptions>(new MultiTenancyOptions new OptionsWrapper<MultiTenancyOptions>(new MultiTenancyOptions
{ {
@ -53,13 +55,14 @@ namespace Volo.Abp.MultiTenancy
[Fact] [Fact]
public void Should_Get_Current_Tenant_From_Two_Resolvers() public void Should_Get_Current_Tenant_From_Multiple_Resolvers()
{ {
//Arrange //Arrange
var fakeTenant = new TenantInfo(Guid.NewGuid().ToString(), "acme"); var selectedTenant = new TenantInfo("A");
var manager = new MultiTenancyManager( var manager = new MultiTenancyManager(
Substitute.For<IServiceProvider>(),
Substitute.For<ITenantScopeProvider>(), Substitute.For<ITenantScopeProvider>(),
new OptionsWrapper<MultiTenancyOptions>(new MultiTenancyOptions new OptionsWrapper<MultiTenancyOptions>(new MultiTenancyOptions
{ {
@ -67,17 +70,16 @@ namespace Volo.Abp.MultiTenancy
{ {
new SimpleTenantResolver(context => new SimpleTenantResolver(context =>
{ {
context.Tenant = new TenantInfo(Guid.NewGuid().ToString(), "skipped-tenant-1"); context.Tenant = new TenantInfo("B");
context.Handled = false; //Causes go to the next resolver
}), }),
new SimpleTenantResolver(context => new SimpleTenantResolver(context =>
{ {
context.Tenant = fakeTenant; context.Tenant = selectedTenant;
context.Handled = true;
}), }),
new SimpleTenantResolver(context => new SimpleTenantResolver(context =>
{ {
context.Tenant = new TenantInfo(Guid.NewGuid().ToString(), "skipped-tenant-2"); context.Tenant = new TenantInfo("C");
context.Handled = true;
}) })
} }
} }
@ -86,7 +88,7 @@ namespace Volo.Abp.MultiTenancy
//Assert //Assert
manager.CurrentTenant.ShouldBe(fakeTenant); manager.CurrentTenant.ShouldBe(selectedTenant);
} }
[Fact] [Fact]
@ -94,9 +96,10 @@ namespace Volo.Abp.MultiTenancy
{ {
//Arrange //Arrange
var oldTenant = new TenantInfo(Guid.NewGuid().ToString(), "old-tenant"); var oldTenant = new TenantInfo("A");
var manager = new MultiTenancyManager( var manager = new MultiTenancyManager(
Substitute.For<IServiceProvider>(),
new AsyncLocalTenantScopeProvider(), new AsyncLocalTenantScopeProvider(),
new OptionsWrapper<MultiTenancyOptions>( new OptionsWrapper<MultiTenancyOptions>(
new MultiTenancyOptions new MultiTenancyOptions
@ -116,7 +119,7 @@ namespace Volo.Abp.MultiTenancy
//Act //Act
var overridedTenant = new TenantInfo(Guid.NewGuid().ToString(), "overrided-tenant"); var overridedTenant = new TenantInfo("B");
using (manager.ChangeTenant(overridedTenant)) using (manager.ChangeTenant(overridedTenant))
{ {
//Assert //Assert

Loading…
Cancel
Save