mirror of https://github.com/abpframework/abp.git
27 changed files with 568 additions and 55 deletions
@ -1,14 +0,0 @@ |
|||
using Volo.Abp.Settings; |
|||
|
|||
namespace Volo.Abp.FeatureManagement |
|||
{ |
|||
public class FeatureManagementSettingDefinitionProvider : SettingDefinitionProvider |
|||
{ |
|||
public override void Define(ISettingDefinitionContext context) |
|||
{ |
|||
/* Define module settings here. |
|||
* Use names from FeatureManagementSettings class. |
|||
*/ |
|||
} |
|||
} |
|||
} |
|||
@ -1,11 +0,0 @@ |
|||
namespace Volo.Abp.FeatureManagement |
|||
{ |
|||
public static class FeatureManagementSettings |
|||
{ |
|||
public const string GroupName = "AbpFeatureManagement"; |
|||
|
|||
/* Add constants for setting names. Example: |
|||
* public const string MySettingName = GroupName + ".MySettingName"; |
|||
*/ |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Features; |
|||
|
|||
namespace Volo.Abp.FeatureManagement |
|||
{ |
|||
public class DefaultValueFeatureManagementProvider : IFeatureManagementProvider, ISingletonDependency |
|||
{ |
|||
public string Name => DefaultValueFeatureValueProvider.ProviderName; |
|||
|
|||
public Task<string> GetOrNullAsync(FeatureDefinition feature, string providerKey) |
|||
{ |
|||
return Task.FromResult(feature.DefaultValue); |
|||
} |
|||
|
|||
public Task SetAsync(FeatureDefinition feature, string value, string providerKey) |
|||
{ |
|||
throw new AbpException($"Can not set default value of a feature. It is only possible while defining the feature in a {typeof(IFeatureDefinitionProvider)} implementation."); |
|||
} |
|||
|
|||
public Task ClearAsync(FeatureDefinition feature, string providerKey) |
|||
{ |
|||
throw new AbpException($"Can not clear default value of a feature. It is only possible while defining the feature in a {typeof(IFeatureDefinitionProvider)} implementation."); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp.Features; |
|||
|
|||
namespace Volo.Abp.FeatureManagement |
|||
{ |
|||
public static class DefaultValueFeatureManagerExtensions |
|||
{ |
|||
public static Task<string> GetOrNullDefaultAsync(this IFeatureManager featureManager, [NotNull] string name, bool fallback = true) |
|||
{ |
|||
return featureManager.GetOrNullAsync(name, DefaultValueFeatureValueProvider.ProviderName, null, fallback); |
|||
} |
|||
|
|||
public static Task<List<FeatureNameValue>> GetAllDefaultAsync(this IFeatureManager featureManager, bool fallback = true) |
|||
{ |
|||
return featureManager.GetAllAsync(DefaultValueFeatureValueProvider.ProviderName, null, fallback); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using Volo.Abp.Collections; |
|||
|
|||
namespace Volo.Abp.FeatureManagement |
|||
{ |
|||
public class FeatureManagementOptions |
|||
{ |
|||
public ITypeList<IFeatureManagementProvider> Providers { get; } |
|||
|
|||
public FeatureManagementOptions() |
|||
{ |
|||
Providers = new TypeList<IFeatureManagementProvider>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Features; |
|||
|
|||
namespace Volo.Abp.FeatureManagement |
|||
{ |
|||
public abstract class FeatureManagementProvider : IFeatureManagementProvider |
|||
{ |
|||
public abstract string Name { get; } |
|||
|
|||
protected IFeatureManagementStore Store { get; } |
|||
|
|||
protected FeatureManagementProvider(IFeatureManagementStore store) |
|||
{ |
|||
Store = store; |
|||
} |
|||
|
|||
public async Task<string> GetOrNullAsync(FeatureDefinition feature, string providerKey) |
|||
{ |
|||
return await Store.GetOrNullAsync(feature.Name, Name, NormalizeProviderKey(providerKey)); |
|||
} |
|||
|
|||
public virtual async Task SetAsync(FeatureDefinition feature, string value, string providerKey) |
|||
{ |
|||
await Store.SetAsync(feature.Name, value, Name, NormalizeProviderKey(providerKey)); |
|||
} |
|||
|
|||
public virtual async Task ClearAsync(FeatureDefinition feature, string providerKey) |
|||
{ |
|||
await Store.DeleteAsync(feature.Name, Name, NormalizeProviderKey(providerKey)); |
|||
} |
|||
|
|||
protected virtual string NormalizeProviderKey(string providerKey) |
|||
{ |
|||
return providerKey; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,81 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Caching; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Guids; |
|||
|
|||
namespace Volo.Abp.FeatureManagement |
|||
{ |
|||
public class FeatureManagementStore : IFeatureManagementStore, ITransientDependency |
|||
{ |
|||
protected IDistributedCache<FeatureValueCacheItem> Cache { get; } |
|||
protected IFeatureValueRepository FeatureValueRepository { get; } |
|||
protected IGuidGenerator GuidGenerator { get; } |
|||
|
|||
public FeatureManagementStore( |
|||
IFeatureValueRepository featureValueRepository, |
|||
IGuidGenerator guidGenerator, |
|||
IDistributedCache<FeatureValueCacheItem> cache) |
|||
{ |
|||
FeatureValueRepository = featureValueRepository; |
|||
GuidGenerator = guidGenerator; |
|||
Cache = cache; |
|||
} |
|||
|
|||
public async Task<string> GetOrNullAsync(string name, string providerName, string providerKey) |
|||
{ |
|||
var cacheItem = await GetCacheItemAsync(name, providerName, providerKey); |
|||
return cacheItem.Value; |
|||
} |
|||
|
|||
public async Task SetAsync(string name, string value, string providerName, string providerKey) |
|||
{ |
|||
var featureValue = await FeatureValueRepository.FindAsync(name, providerName, providerKey); |
|||
if (featureValue == null) |
|||
{ |
|||
featureValue = new FeatureValue(GuidGenerator.Create(), name, value, providerName, providerKey); |
|||
await FeatureValueRepository.InsertAsync(featureValue); |
|||
} |
|||
else |
|||
{ |
|||
featureValue.Value = value; |
|||
await FeatureValueRepository.UpdateAsync(featureValue); |
|||
} |
|||
} |
|||
|
|||
public async Task DeleteAsync(string name, string providerName, string providerKey) |
|||
{ |
|||
var featureValue = await FeatureValueRepository.FindAsync(name, providerName, providerKey); |
|||
if (featureValue != null) |
|||
{ |
|||
await FeatureValueRepository.DeleteAsync(featureValue); |
|||
} |
|||
} |
|||
|
|||
protected virtual async Task<FeatureValueCacheItem> GetCacheItemAsync(string name, string providerName, string providerKey) |
|||
{ |
|||
var cacheKey = CalculateCacheKey(name, providerName, providerKey); |
|||
var cacheItem = await Cache.GetAsync(cacheKey); |
|||
|
|||
if (cacheItem != null) |
|||
{ |
|||
return cacheItem; |
|||
} |
|||
|
|||
var featureValue = await FeatureValueRepository.FindAsync(name, providerName, providerKey); |
|||
|
|||
cacheItem = new FeatureValueCacheItem(featureValue?.Value); |
|||
|
|||
await Cache.SetAsync( |
|||
cacheKey, |
|||
cacheItem |
|||
); |
|||
|
|||
return cacheItem; |
|||
} |
|||
|
|||
protected virtual string CalculateCacheKey(string name, string providerName, string providerKey) |
|||
{ |
|||
return FeatureValueCacheItem.CalculateCacheKey(name, providerName, providerKey); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,186 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Features; |
|||
|
|||
namespace Volo.Abp.FeatureManagement |
|||
{ |
|||
public class FeatureManager : IFeatureManager, ISingletonDependency |
|||
{ |
|||
protected IFeatureDefinitionManager FeatureDefinitionManager { get; } |
|||
protected List<IFeatureManagementProvider> Providers => _lazyProviders.Value; |
|||
protected FeatureManagementOptions Options { get; } |
|||
|
|||
private readonly Lazy<List<IFeatureManagementProvider>> _lazyProviders; |
|||
|
|||
public FeatureManager( |
|||
IOptions<FeatureManagementOptions> options, |
|||
IServiceProvider serviceProvider, |
|||
IFeatureDefinitionManager featureDefinitionManager) |
|||
{ |
|||
FeatureDefinitionManager = featureDefinitionManager; |
|||
Options = options.Value; |
|||
|
|||
//TODO: Instead, use IHybridServiceScopeFactory and create a scope..?
|
|||
|
|||
_lazyProviders = new Lazy<List<IFeatureManagementProvider>>( |
|||
() => Options |
|||
.Providers |
|||
.Select(c => serviceProvider.GetRequiredService(c) as IFeatureManagementProvider) |
|||
.ToList(), |
|||
true |
|||
); |
|||
} |
|||
|
|||
public virtual Task<string> GetOrNullAsync( |
|||
string name, |
|||
string providerName, |
|||
string providerKey, |
|||
bool fallback = true) |
|||
{ |
|||
Check.NotNull(name, nameof(name)); |
|||
Check.NotNull(providerName, nameof(providerName)); |
|||
|
|||
return GetOrNullInternalAsync(name, providerName, providerKey, fallback); |
|||
} |
|||
|
|||
public virtual async Task<List<FeatureNameValue>> GetAllAsync( |
|||
string providerName, |
|||
string providerKey, |
|||
bool fallback = true) |
|||
{ |
|||
Check.NotNull(providerName, nameof(providerName)); |
|||
|
|||
var featureDefinitions = FeatureDefinitionManager.GetAll(); |
|||
var providers = Enumerable.Reverse(Providers) |
|||
.SkipWhile(c => c.Name != providerName); |
|||
|
|||
if (!fallback) |
|||
{ |
|||
providers = providers.TakeWhile(c => c.Name == providerName); |
|||
} |
|||
|
|||
var providerList = providers.Reverse().ToList(); |
|||
|
|||
if (!providerList.Any()) |
|||
{ |
|||
return new List<FeatureNameValue>(); |
|||
} |
|||
|
|||
var featureValues = new Dictionary<string, FeatureNameValue>(); |
|||
|
|||
foreach (var feature in featureDefinitions) |
|||
{ |
|||
string value = null; |
|||
|
|||
foreach (var provider in providerList) |
|||
{ |
|||
var providerValue = await provider.GetOrNullAsync( |
|||
feature, |
|||
provider.Name == providerName ? providerKey : null |
|||
); |
|||
|
|||
if (providerValue != null) |
|||
{ |
|||
value = providerValue; |
|||
} |
|||
} |
|||
|
|||
if (value != null) |
|||
{ |
|||
featureValues[feature.Name] = new FeatureNameValue(feature.Name, value); |
|||
} |
|||
} |
|||
|
|||
return featureValues.Values.ToList(); |
|||
} |
|||
|
|||
public virtual async Task SetAsync( |
|||
string name, |
|||
string value, |
|||
string providerName, |
|||
string providerKey, |
|||
bool forceToSet = false) |
|||
{ |
|||
Check.NotNull(name, nameof(name)); |
|||
Check.NotNull(providerName, nameof(providerName)); |
|||
|
|||
var feature = FeatureDefinitionManager.Get(name); |
|||
|
|||
var providers = Enumerable |
|||
.Reverse(Providers) |
|||
.SkipWhile(p => p.Name != providerName) |
|||
.ToList(); |
|||
|
|||
if (!providers.Any()) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (providers.Count > 1 && !forceToSet && value != null) |
|||
{ |
|||
var fallbackValue = await GetOrNullInternalAsync(name, providers[1].Name, null); |
|||
if (fallbackValue == value) |
|||
{ |
|||
//Clear the value if it's same as it's fallback value
|
|||
value = null; |
|||
} |
|||
} |
|||
|
|||
providers = providers |
|||
.TakeWhile(p => p.Name == providerName) |
|||
.ToList(); //Getting list for case of there are more than one provider with same providerName
|
|||
|
|||
if (value == null) |
|||
{ |
|||
foreach (var provider in providers) |
|||
{ |
|||
await provider.ClearAsync(feature, providerKey); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
foreach (var provider in providers) |
|||
{ |
|||
await provider.SetAsync(feature, value, providerKey); |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected virtual async Task<string> GetOrNullInternalAsync( |
|||
string name, |
|||
string providerName, |
|||
string providerKey, |
|||
bool fallback = true) //TODO: Fallback is not used
|
|||
{ |
|||
var feature = FeatureDefinitionManager.Get(name); |
|||
var providers = Enumerable |
|||
.Reverse(Providers); |
|||
|
|||
if (providerName != null) |
|||
{ |
|||
providers = providers.SkipWhile(c => c.Name != providerName); |
|||
} |
|||
|
|||
string value = null; |
|||
foreach (var provider in providers) |
|||
{ |
|||
value = await provider.GetOrNullAsync( |
|||
feature, |
|||
provider.Name == providerName ? providerKey : null |
|||
); |
|||
|
|||
if (value != null) |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.FeatureManagement |
|||
{ |
|||
[Serializable] |
|||
public class FeatureNameValue : NameValue |
|||
{ |
|||
public FeatureNameValue() |
|||
{ |
|||
|
|||
} |
|||
|
|||
public FeatureNameValue(string name, string value) |
|||
{ |
|||
Name = name; |
|||
Value = value; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.FeatureManagement |
|||
{ |
|||
[Serializable] |
|||
public class FeatureValueCacheItem |
|||
{ |
|||
public string Value { get; set; } |
|||
|
|||
public FeatureValueCacheItem() |
|||
{ |
|||
|
|||
} |
|||
|
|||
public FeatureValueCacheItem(string value) |
|||
{ |
|||
Value = value; |
|||
} |
|||
|
|||
public static string CalculateCacheKey(string name, string providerName, string providerKey) |
|||
{ |
|||
return "pn:" + providerName + ",pk:" + providerKey + ",n:" + name; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Caching; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Domain.Entities.Events; |
|||
using Volo.Abp.EventBus; |
|||
|
|||
namespace Volo.Abp.FeatureManagement |
|||
{ |
|||
public class FeatureValueCacheItemInvalidator : |
|||
ILocalEventHandler<EntityChangedEventData<FeatureValue>>, |
|||
ITransientDependency |
|||
{ |
|||
protected IDistributedCache<FeatureValueCacheItem> Cache { get; } |
|||
|
|||
public FeatureValueCacheItemInvalidator(IDistributedCache<FeatureValueCacheItem> cache) |
|||
{ |
|||
Cache = cache; |
|||
} |
|||
|
|||
public virtual async Task HandleEventAsync(EntityChangedEventData<FeatureValue> eventData) |
|||
{ |
|||
var cacheKey = CalculateCacheKey( |
|||
eventData.Entity.Name, |
|||
eventData.Entity.ProviderName, |
|||
eventData.Entity.ProviderKey |
|||
); |
|||
|
|||
await Cache.RemoveAsync(cacheKey); |
|||
} |
|||
|
|||
protected virtual string CalculateCacheKey(string name, string providerName, string providerKey) |
|||
{ |
|||
return FeatureValueCacheItem.CalculateCacheKey(name, providerName, providerKey); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp.Features; |
|||
|
|||
namespace Volo.Abp.FeatureManagement |
|||
{ |
|||
public interface IFeatureManagementProvider |
|||
{ |
|||
string Name { get; } |
|||
|
|||
Task<string> GetOrNullAsync([NotNull] FeatureDefinition feature, [CanBeNull] string providerKey); |
|||
|
|||
Task SetAsync([NotNull] FeatureDefinition feature, [NotNull] string value, [CanBeNull] string providerKey); |
|||
|
|||
Task ClearAsync([NotNull] FeatureDefinition feature, [CanBeNull] string providerKey); |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Volo.Abp.FeatureManagement |
|||
{ |
|||
public interface IFeatureManagementStore |
|||
{ |
|||
Task<string> GetOrNullAsync(string name, string providerName, string providerKey); |
|||
|
|||
Task SetAsync(string name, string value, string providerName, string providerKey); |
|||
|
|||
Task DeleteAsync(string name, string providerName, string providerKey); |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.FeatureManagement |
|||
{ |
|||
public interface IFeatureManager |
|||
{ |
|||
Task<string> GetOrNullAsync([NotNull]string name, [NotNull] string providerName, [CanBeNull] string providerKey, bool fallback = true); |
|||
|
|||
Task<List<FeatureNameValue>> GetAllAsync([NotNull] string providerName, [CanBeNull] string providerKey, bool fallback = true); |
|||
|
|||
Task SetAsync([NotNull] string name, [CanBeNull] string value, [NotNull] string providerName, [CanBeNull] string providerKey, bool forceToSet = false); |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using Volo.Abp.Features; |
|||
|
|||
namespace Volo.Abp.FeatureManagement |
|||
{ |
|||
public class TenantFeatureManagementProvider : FeatureManagementProvider |
|||
{ |
|||
public override string Name => TenantFeatureValueProvider.ProviderName; |
|||
|
|||
public TenantFeatureManagementProvider(IFeatureManagementStore store) |
|||
: base(store) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp.Features; |
|||
|
|||
namespace Volo.Abp.FeatureManagement |
|||
{ |
|||
public static class TenantFeatureManagerExtensions |
|||
{ |
|||
public static Task<string> GetOrNullForTenantAsync(this IFeatureManager featureManager, [NotNull] string name, Guid tenantId, bool fallback = true) |
|||
{ |
|||
return featureManager.GetOrNullAsync(name, TenantFeatureValueProvider.ProviderName, tenantId.ToString(), fallback); |
|||
} |
|||
|
|||
public static Task<string> GetOrNullForCurrentTenantAsync(this IFeatureManager featureManager, [NotNull] string name, bool fallback = true) |
|||
{ |
|||
return featureManager.GetOrNullAsync(name, TenantFeatureValueProvider.ProviderName, null, fallback); |
|||
} |
|||
|
|||
public static Task<List<FeatureNameValue>> GetAllForTenantAsync(this IFeatureManager featureManager, Guid tenantId, bool fallback = true) |
|||
{ |
|||
return featureManager.GetAllAsync(TenantFeatureValueProvider.ProviderName, tenantId.ToString(), fallback); |
|||
} |
|||
|
|||
public static Task<List<FeatureNameValue>> GetAllForCurrentTenantAsync(this IFeatureManager featureManager, bool fallback = true) |
|||
{ |
|||
return featureManager.GetAllAsync(TenantFeatureValueProvider.ProviderName, null, fallback); |
|||
} |
|||
|
|||
public static Task SetForTenantAsync(this IFeatureManager featureManager, Guid tenantId, [NotNull] string name, [CanBeNull] string value, bool forceToSet = false) |
|||
{ |
|||
return featureManager.SetAsync(name, value, TenantFeatureValueProvider.ProviderName, tenantId.ToString(), forceToSet); |
|||
} |
|||
|
|||
public static Task SetForCurrentTenantAsync(this IFeatureManager featureManager, [NotNull] string name, [CanBeNull] string value, bool forceToSet = false) |
|||
{ |
|||
return featureManager.SetAsync(name, value, TenantFeatureValueProvider.ProviderName, null, forceToSet); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue