Browse Source

Revised tenant resolve and added query string resolver.

pull/81/head
Halil İbrahim Kalkan 9 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)
{
services.Configure<MultiTenancyOptions>(options =>
{
options.TenantResolvers.Insert(0, new QueryStringTenantResolver());
});
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.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.Modularity;
namespace Volo.Abp.AspNetCore
@ -8,6 +10,8 @@ namespace Volo.Abp.AspNetCore
{
public void ConfigureServices(IServiceCollection services)
{
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddObjectAccessor<IApplicationBuilder>();
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": {
"NETStandard.Library": "1.6.1",
"Volo.Abp": "1.0.0-*",
"Microsoft.AspNetCore.Http.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": {

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

@ -1,9 +1,23 @@
using System;
namespace Volo.Abp.MultiTenancy
{
public class CurrentTenantResolveContext : ICurrentTenantResolveContext
{
public IServiceProvider ServiceProvider { get; }
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
{
public interface ICurrentTenantResolveContext
public interface ICurrentTenantResolveContext : IServiceProviderAccessor
{
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 JetBrains.Annotations;
namespace Volo.Abp.MultiTenancy
{
public interface ITenantScopeProvider
{
[CanBeNull]
TenantScope CurrentScope { get; }
IDisposable EnterScope(TenantInfo tenantInfo);

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

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

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

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

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

@ -4,10 +4,9 @@ namespace Volo.Abp.MultiTenancy
{
public class TenantInfo
{
[NotNull]
public string Id { get; }
public string Name { get; }
//TODO: Needed for serialization
[UsedImplicitly]
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(name, nameof(name));
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>
{
["TenantName"] = manager.CurrentTenant?.Name ?? "<host>"
["TenantId"] = manager.CurrentTenant?.Id ?? ""
};
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()
{
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();
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))
{
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();

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

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

Loading…
Cancel
Save