Add `HandleContextAsync` method to the `IFeatureManagementProvider`.

pull/11895/head
maliming 4 years ago
parent cdf8880766
commit 731c4b1154
No known key found for this signature in database
GPG Key ID: 096224957E51C89E

@ -0,0 +1,30 @@
using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace Volo.Abp;
/// <summary>
/// This class can be used to provide an action when
/// DisposeAsync method is called.
/// </summary>
public class AsyncDisposeFunc : IAsyncDisposable
{
private readonly Func<Task> _func;
/// <summary>
/// Creates a new <see cref="AsyncDisposeFunc"/> object.
/// </summary>
/// <param name="func">func to be executed when this object is DisposeAsync.</param>
public AsyncDisposeFunc([NotNull] Func<Task> func)
{
Check.NotNull(func, nameof(func));
_func = func;
}
public async ValueTask DisposeAsync()
{
await _func();
}
}

@ -0,0 +1,19 @@
using System;
using System.Threading.Tasks;
namespace Volo.Abp;
public sealed class NullAsyncDisposable : IAsyncDisposable
{
public static NullAsyncDisposable Instance { get; } = new NullAsyncDisposable();
private NullAsyncDisposable()
{
}
public ValueTask DisposeAsync()
{
return default;
}
}

@ -20,8 +20,8 @@ public class AbpFeatureManagementDomainModule : AbpModule
options.Providers.Add<DefaultValueFeatureManagementProvider>();
options.Providers.Add<EditionFeatureManagementProvider>();
//TODO: Should be moved to the Tenant Management module
options.Providers.Add<TenantFeatureManagementProvider>();
//TODO: Should be moved to the Tenant Management module
options.Providers.Add<TenantFeatureManagementProvider>();
options.ProviderPolicies[TenantFeatureValueProvider.ProviderName] = "AbpTenantManagement.Tenants.ManageFeatures";
});

@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Features;
@ -13,6 +14,11 @@ public class DefaultValueFeatureManagementProvider : IFeatureManagementProvider,
return providerName == Name;
}
public Task<IAsyncDisposable> HandleContextAsync(string providerName, string providerKey)
{
return Task.FromResult<IAsyncDisposable>(NullAsyncDisposable.Instance);
}
public virtual Task<string> GetOrNullAsync(FeatureDefinition feature, string providerKey)
{
return Task.FromResult(feature.DefaultValue);

@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System;
using System.Threading.Tasks;
using Volo.Abp.Features;
namespace Volo.Abp.FeatureManagement;
@ -19,6 +20,11 @@ public abstract class FeatureManagementProvider : IFeatureManagementProvider
return providerName == Name;
}
public virtual Task<IAsyncDisposable> HandleContextAsync(string providerName, string providerKey)
{
return Task.FromResult<IAsyncDisposable>(NullAsyncDisposable.Instance);
}
public virtual async Task<string> GetOrNullAsync(FeatureDefinition feature, string providerKey)
{
return await Store.GetOrNullAsync(feature.Name, Name, await NormalizeProviderKeyAsync(providerKey));

@ -132,7 +132,7 @@ public class FeatureManager : IFeatureManager, ISingletonDependency
var feature = FeatureDefinitionManager.Get(name);
if (feature?.ValueType?.Validator.IsValid(value) == false)
if (feature.ValueType?.Validator.IsValid(value) == false)
{
throw new FeatureValueInvalidException(feature.DisplayName.Localize(StringLocalizerFactory));
}
@ -149,11 +149,14 @@ public class FeatureManager : IFeatureManager, ISingletonDependency
if (providers.Count > 1 && !forceToSet && value != null)
{
var fallbackValue = await GetOrNullInternalAsync(name, providers[1].Name, null);
if (fallbackValue.Value == value)
await using (await providers[0].HandleContextAsync(providerName, providerKey))
{
//Clear the value if it's same as it's fallback value
value = null;
var fallbackValue = await GetOrNullInternalAsync(name, providers[1].Name, null);
if (fallbackValue.Value == value)
{
//Clear the value if it's same as it's fallback value
value = null;
}
}
}

@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Volo.Abp.Features;
@ -11,6 +12,9 @@ public interface IFeatureManagementProvider
//TODO: Other better method name.
bool Compatible(string providerName);
//TODO: Other better method name.
Task<IAsyncDisposable> HandleContextAsync(string providerName, string providerKey);
Task<string> GetOrNullAsync([NotNull] FeatureDefinition feature, [CanBeNull] string providerKey);
Task SetAsync([NotNull] FeatureDefinition feature, [NotNull] string value, [CanBeNull] string providerKey);

@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Features;
using Volo.Abp.MultiTenancy;
@ -19,6 +20,24 @@ public class TenantFeatureManagementProvider : FeatureManagementProvider, ITrans
CurrentTenant = currentTenant;
}
public override Task<IAsyncDisposable> HandleContextAsync(string providerName, string providerKey)
{
if (providerName == Name && !providerKey.IsNullOrWhiteSpace())
{
if (Guid.TryParse(providerKey, out var tenantId))
{
var disposable = CurrentTenant.Change(tenantId);
return Task.FromResult<IAsyncDisposable>(new AsyncDisposeFunc(() =>
{
disposable.Dispose();
return Task.CompletedTask;
}));
}
}
return base.HandleContextAsync(providerName, providerKey);
}
protected override Task<string> NormalizeProviderKeyAsync(string providerKey)
{
if (providerKey != null)

@ -12,12 +12,14 @@ public class FeatureManager_Tests : FeatureManagementDomainTestBase
private readonly IFeatureManager _featureManager;
private readonly ICurrentTenant _currentTenant;
private readonly IFeatureChecker _featureChecker;
private readonly IFeatureValueRepository _featureValueRepository;
public FeatureManager_Tests()
{
_featureManager = GetRequiredService<IFeatureManager>();
_featureChecker = GetRequiredService<IFeatureChecker>();
_currentTenant = GetRequiredService<ICurrentTenant>();
_featureValueRepository = GetRequiredService<IFeatureValueRepository>();
}
[Fact]
@ -153,4 +155,44 @@ public class FeatureManager_Tests : FeatureManagementDomainTestBase
x.Provider.Name == EditionFeatureValueProvider.ProviderName);
}
[Fact]
public async Task Test_HandleContextAsync()
{
var featureValue = await _featureValueRepository.FindAsync(
TestFeatureDefinitionProvider.EmailSupport,
TenantFeatureValueProvider.ProviderName,
TestEditionIds.TenantId.ToString()
);
featureValue.ShouldNotBeNull();
featureValue.Value.ShouldBe(false.ToString().ToLower());
featureValue = await _featureValueRepository.FindAsync(
TestFeatureDefinitionProvider.EmailSupport,
NextTenantFeatureManagementProvider.ProviderName,
TestEditionIds.TenantId.ToString()
);
featureValue.ShouldNotBeNull();
featureValue.Value.ShouldBe(true.ToString().ToLower());
await _featureManager.SetAsync(TestFeatureDefinitionProvider.EmailSupport, true.ToString().ToLower(),
TenantFeatureValueProvider.ProviderName, TestEditionIds.TenantId.ToString());
featureValue = await _featureValueRepository.FindAsync(
TestFeatureDefinitionProvider.EmailSupport,
TenantFeatureValueProvider.ProviderName,
TestEditionIds.TenantId.ToString()
);
featureValue.ShouldBeNull();
featureValue = await _featureValueRepository.FindAsync(
TestFeatureDefinitionProvider.EmailSupport,
NextTenantFeatureManagementProvider.ProviderName,
TestEditionIds.TenantId.ToString()
);
featureValue.ShouldNotBeNull();
featureValue.Value.ShouldBe(true.ToString().ToLower());
}
}

@ -1,4 +1,5 @@
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Authorization;
using Volo.Abp.Autofac;
using Volo.Abp.Features;
@ -29,8 +30,10 @@ public class FeatureManagementTestBaseModule : AbpModule
{
context.Services.Configure<FeatureManagementOptions>(options =>
{
//TODO: Any value can pass. After completing the permission unit test, look at it again.
options.ProviderPolicies[EditionFeatureValueProvider.ProviderName] = EditionFeatureValueProvider.ProviderName;
options.Providers.InsertBefore(typeof(TenantFeatureManagementProvider), typeof(NextTenantFeatureManagementProvider));
//TODO: Any value can pass. After completing the permission unit test, look at it again.
options.ProviderPolicies[EditionFeatureValueProvider.ProviderName] = EditionFeatureValueProvider.ProviderName;
});
}

@ -23,6 +23,28 @@ public class FeatureManagementTestDataBuilder : ITransientDependency
public async Task BuildAsync()
{
// Tenant EmailSupport
await _featureValueRepository.InsertAsync(
new FeatureValue(
_guidGenerator.Create(),
TestFeatureDefinitionProvider.EmailSupport,
false.ToString().ToLowerInvariant(),
TenantFeatureValueProvider.ProviderName,
TestEditionIds.TenantId.ToString()
)
);
// NextTenant EmailSupport
await _featureValueRepository.InsertAsync(
new FeatureValue(
_guidGenerator.Create(),
TestFeatureDefinitionProvider.EmailSupport,
true.ToString().ToLowerInvariant(),
NextTenantFeatureManagementProvider.ProviderName,
TestEditionIds.TenantId.ToString()
)
);
#region "Regular" edition features
//SocialLogins

@ -0,0 +1,32 @@
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.FeatureManagement;
public class NextTenantFeatureManagementProvider : FeatureManagementProvider, ITransientDependency
{
public static string ProviderName => "TENANT";
public override string Name => ProviderName;
protected ICurrentTenant CurrentTenant { get; }
public NextTenantFeatureManagementProvider(
IFeatureManagementStore store,
ICurrentTenant currentTenant)
: base(store)
{
CurrentTenant = currentTenant;
}
protected override Task<string> NormalizeProviderKeyAsync(string providerKey)
{
if (providerKey != null)
{
return Task.FromResult(providerKey);
}
return Task.FromResult(CurrentTenant.Id?.ToString());
}
}

@ -8,10 +8,13 @@ public static class TestEditionIds
public static Guid Enterprise { get; }
public static Guid Ultimate { get; }
public static Guid TenantId { get; }
static TestEditionIds()
{
Regular = Guid.Parse("532599ab-c0c0-4345-a04a-e322867b6e15");
Enterprise = Guid.Parse("27e50758-1feb-436a-be4f-cae8519e0cb2");
Ultimate = Guid.Parse("6ea78c22-be32-497e-aaba-a2332c564c5e");
TenantId = Guid.Parse("8899799a-9edf-44ac-9121-310d25ef4fa1");
}
}

Loading…
Cancel
Save