From c3e70d42567ed6c32553975498417af06415a1f3 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Tue, 12 Mar 2019 16:23:45 +0300 Subject: [PATCH] Built the domain layer of the feature management module --- .../Permissions/PermissionValueProvider.cs | 2 +- .../DefaultValueSettingValueProvider.cs | 2 +- .../Volo/Abp/Features/FeatureValueProvider.cs | 2 +- .../Volo/Abp/Settings/SettingValue.cs | 6 - .../Volo/Abp/Settings/SettingValueProvider.cs | 2 +- .../AbpFeatureManagementApplicationModule.cs | 6 - ...tureManagementSettingDefinitionProvider.cs | 14 -- .../FeatureManagementSettings.cs | 11 -- .../Volo.Abp.FeatureManagement.Domain.csproj | 1 + .../AbpFeatureManagementDomainModule.cs | 12 +- .../DefaultValueFeatureManagementProvider.cs | 26 +++ .../DefaultValueFeatureManagerExtensions.cs | 20 ++ .../FeatureManagementOptions.cs | 14 ++ .../FeatureManagementProvider.cs | 37 ++++ .../FeatureManagementStore.cs | 81 ++++++++ .../Abp/FeatureManagement/FeatureManager.cs | 186 ++++++++++++++++++ .../Abp/FeatureManagement/FeatureNameValue.cs | 19 ++ .../Abp/FeatureManagement/FeatureStore.cs | 17 +- .../FeatureValueCacheItem.cs | 25 +++ .../FeatureValueCacheItemInvalidator.cs | 36 ++++ .../IFeatureManagementProvider.cs | 17 ++ .../IFeatureManagementStore.cs | 13 ++ .../Abp/FeatureManagement/IFeatureManager.cs | 15 ++ .../TenantFeatureManagementProvider.cs | 14 ++ .../TenantFeatureManagerExtensions.cs | 41 ++++ .../Volo/Abp/SettingManagement/Setting.cs | 3 +- .../SettingManagementProvider.cs | 1 + 27 files changed, 568 insertions(+), 55 deletions(-) delete mode 100644 modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureManagementSettingDefinitionProvider.cs delete mode 100644 modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureManagementSettings.cs create mode 100644 modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/DefaultValueFeatureManagementProvider.cs create mode 100644 modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/DefaultValueFeatureManagerExtensions.cs create mode 100644 modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementOptions.cs create mode 100644 modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementProvider.cs create mode 100644 modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs create mode 100644 modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs create mode 100644 modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureNameValue.cs create mode 100644 modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValueCacheItem.cs create mode 100644 modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValueCacheItemInvalidator.cs create mode 100644 modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureManagementProvider.cs create mode 100644 modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureManagementStore.cs create mode 100644 modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureManager.cs create mode 100644 modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/TenantFeatureManagementProvider.cs create mode 100644 modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/TenantFeatureManagerExtensions.cs diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionValueProvider.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionValueProvider.cs index 91351593a5..44d23f718a 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionValueProvider.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionValueProvider.cs @@ -3,7 +3,7 @@ using Volo.Abp.DependencyInjection; namespace Volo.Abp.Authorization.Permissions { - public abstract class PermissionValueProvider : IPermissionValueProvider, ISingletonDependency + public abstract class PermissionValueProvider : IPermissionValueProvider, ISingletonDependency //TODO: to transient? { public abstract string Name { get; } diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/DefaultValueSettingValueProvider.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/DefaultValueSettingValueProvider.cs index d60a55586a..275be89af2 100644 --- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/DefaultValueSettingValueProvider.cs +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/DefaultValueSettingValueProvider.cs @@ -2,7 +2,7 @@ namespace Volo.Abp.Features { - public class DefaultValueFeatureValueProvider : FeatureValueProvider + public class DefaultValueFeatureValueProvider : FeatureValueProvider //TODO: Directly implement IFeatureValueProvider { public const string ProviderName = "Default"; diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureValueProvider.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureValueProvider.cs index b93b6903e2..7ef664e6ce 100644 --- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureValueProvider.cs +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureValueProvider.cs @@ -3,7 +3,7 @@ using Volo.Abp.DependencyInjection; namespace Volo.Abp.Features { - public abstract class FeatureValueProvider : IFeatureValueProvider, ISingletonDependency + public abstract class FeatureValueProvider : IFeatureValueProvider, ISingletonDependency //TODO: to transient? { public abstract string Name { get; } diff --git a/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingValue.cs b/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingValue.cs index d7f48081dd..b03a5159c8 100644 --- a/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingValue.cs +++ b/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingValue.cs @@ -5,17 +5,11 @@ namespace Volo.Abp.Settings [Serializable] public class SettingValue : NameValue { - /// - /// Creates a new . - /// public SettingValue() { } - /// - /// Creates a new . - /// public SettingValue(string name, string value) { Name = name; diff --git a/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingValueProvider.cs b/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingValueProvider.cs index 5173f80540..0a383a3e3b 100644 --- a/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingValueProvider.cs +++ b/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingValueProvider.cs @@ -3,7 +3,7 @@ using Volo.Abp.DependencyInjection; namespace Volo.Abp.Settings { - public abstract class SettingValueProvider : ISettingValueProvider, ISingletonDependency + public abstract class SettingValueProvider : ISettingValueProvider, ISingletonDependency //TODO: to transient? { public abstract string Name { get; } diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/AbpFeatureManagementApplicationModule.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/AbpFeatureManagementApplicationModule.cs index 8116e436b0..62ee615a10 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/AbpFeatureManagementApplicationModule.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/AbpFeatureManagementApplicationModule.cs @@ -1,6 +1,5 @@ using Volo.Abp.AutoMapper; using Volo.Abp.Modularity; -using Volo.Abp.Settings; namespace Volo.Abp.FeatureManagement { @@ -17,11 +16,6 @@ namespace Volo.Abp.FeatureManagement { options.AddProfile(validate: true); }); - - Configure(options => - { - options.DefinitionProviders.Add(); - }); } } } diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureManagementSettingDefinitionProvider.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureManagementSettingDefinitionProvider.cs deleted file mode 100644 index 9a064e6409..0000000000 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureManagementSettingDefinitionProvider.cs +++ /dev/null @@ -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. - */ - } - } -} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureManagementSettings.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureManagementSettings.cs deleted file mode 100644 index ed634b9bca..0000000000 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureManagementSettings.cs +++ /dev/null @@ -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"; - */ - } -} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo.Abp.FeatureManagement.Domain.csproj b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo.Abp.FeatureManagement.Domain.csproj index 5df85ab931..9a3ebac4f3 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo.Abp.FeatureManagement.Domain.csproj +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo.Abp.FeatureManagement.Domain.csproj @@ -10,6 +10,7 @@ + diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainModule.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainModule.cs index 82492b1fd7..db6dabf9c6 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainModule.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainModule.cs @@ -1,4 +1,5 @@ -using Volo.Abp.FeatureManagement.Localization; +using Volo.Abp.Caching; +using Volo.Abp.FeatureManagement.Localization; using Volo.Abp.Features; using Volo.Abp.Localization; using Volo.Abp.Localization.ExceptionHandling; @@ -9,12 +10,19 @@ namespace Volo.Abp.FeatureManagement { [DependsOn( typeof(AbpFeatureManagementDomainSharedModule), - typeof(AbpFeaturesModule) + typeof(AbpFeaturesModule), + typeof(AbpCachingModule) )] public class AbpFeatureManagementDomainModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { + Configure(options => + { + options.Providers.Add(); + options.Providers.Add(); + }); + Configure(options => { options.FileSets.AddEmbedded(); diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/DefaultValueFeatureManagementProvider.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/DefaultValueFeatureManagementProvider.cs new file mode 100644 index 0000000000..aa20684d3a --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/DefaultValueFeatureManagementProvider.cs @@ -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 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."); + } + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/DefaultValueFeatureManagerExtensions.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/DefaultValueFeatureManagerExtensions.cs new file mode 100644 index 0000000000..c000b66df3 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/DefaultValueFeatureManagerExtensions.cs @@ -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 GetOrNullDefaultAsync(this IFeatureManager featureManager, [NotNull] string name, bool fallback = true) + { + return featureManager.GetOrNullAsync(name, DefaultValueFeatureValueProvider.ProviderName, null, fallback); + } + + public static Task> GetAllDefaultAsync(this IFeatureManager featureManager, bool fallback = true) + { + return featureManager.GetAllAsync(DefaultValueFeatureValueProvider.ProviderName, null, fallback); + } + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementOptions.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementOptions.cs new file mode 100644 index 0000000000..0599c3e4c5 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementOptions.cs @@ -0,0 +1,14 @@ +using Volo.Abp.Collections; + +namespace Volo.Abp.FeatureManagement +{ + public class FeatureManagementOptions + { + public ITypeList Providers { get; } + + public FeatureManagementOptions() + { + Providers = new TypeList(); + } + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementProvider.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementProvider.cs new file mode 100644 index 0000000000..1d0c59773b --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementProvider.cs @@ -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 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; + } + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs new file mode 100644 index 0000000000..94c4d48067 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs @@ -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 Cache { get; } + protected IFeatureValueRepository FeatureValueRepository { get; } + protected IGuidGenerator GuidGenerator { get; } + + public FeatureManagementStore( + IFeatureValueRepository featureValueRepository, + IGuidGenerator guidGenerator, + IDistributedCache cache) + { + FeatureValueRepository = featureValueRepository; + GuidGenerator = guidGenerator; + Cache = cache; + } + + public async Task 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 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); + } + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs new file mode 100644 index 0000000000..06ca345bf6 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs @@ -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 Providers => _lazyProviders.Value; + protected FeatureManagementOptions Options { get; } + + private readonly Lazy> _lazyProviders; + + public FeatureManager( + IOptions options, + IServiceProvider serviceProvider, + IFeatureDefinitionManager featureDefinitionManager) + { + FeatureDefinitionManager = featureDefinitionManager; + Options = options.Value; + + //TODO: Instead, use IHybridServiceScopeFactory and create a scope..? + + _lazyProviders = new Lazy>( + () => Options + .Providers + .Select(c => serviceProvider.GetRequiredService(c) as IFeatureManagementProvider) + .ToList(), + true + ); + } + + public virtual Task 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> 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(); + } + + var featureValues = new Dictionary(); + + 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 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; + } + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureNameValue.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureNameValue.cs new file mode 100644 index 0000000000..c7b64d9a1c --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureNameValue.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureStore.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureStore.cs index dbe851008c..23d5ea7d50 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureStore.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureStore.cs @@ -4,24 +4,21 @@ using Volo.Abp.Features; namespace Volo.Abp.FeatureManagement { - //TODO: Implement caching - public class FeatureStore : IFeatureStore, ITransientDependency { - protected IFeatureValueRepository FeatureValueRepository { get; } + protected IFeatureManagementStore FeatureManagementStore { get; } - public FeatureStore(IFeatureValueRepository featureValueRepository) + public FeatureStore(IFeatureManagementStore featureManagementStore) { - FeatureValueRepository = featureValueRepository; + FeatureManagementStore = featureManagementStore; } - public async Task GetOrNullAsync( - string name, - string providerName, + public Task GetOrNullAsync( + string name, + string providerName, string providerKey) { - var featureValue = await FeatureValueRepository.FindAsync(name, providerName, providerKey); - return featureValue?.Value; + return FeatureManagementStore.GetOrNullAsync(name, providerName, providerKey); } } } diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValueCacheItem.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValueCacheItem.cs new file mode 100644 index 0000000000..765e6c9b32 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValueCacheItem.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValueCacheItemInvalidator.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValueCacheItemInvalidator.cs new file mode 100644 index 0000000000..63e207b1e7 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValueCacheItemInvalidator.cs @@ -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>, + ITransientDependency + { + protected IDistributedCache Cache { get; } + + public FeatureValueCacheItemInvalidator(IDistributedCache cache) + { + Cache = cache; + } + + public virtual async Task HandleEventAsync(EntityChangedEventData 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); + } + } +} diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureManagementProvider.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureManagementProvider.cs new file mode 100644 index 0000000000..204ed16235 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureManagementProvider.cs @@ -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 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); + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureManagementStore.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureManagementStore.cs new file mode 100644 index 0000000000..d32ff19d84 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureManagementStore.cs @@ -0,0 +1,13 @@ +using System.Threading.Tasks; + +namespace Volo.Abp.FeatureManagement +{ + public interface IFeatureManagementStore + { + Task 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); + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureManager.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureManager.cs new file mode 100644 index 0000000000..df19cd332b --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureManager.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using JetBrains.Annotations; + +namespace Volo.Abp.FeatureManagement +{ + public interface IFeatureManager + { + Task GetOrNullAsync([NotNull]string name, [NotNull] string providerName, [CanBeNull] string providerKey, bool fallback = true); + + Task> 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); + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/TenantFeatureManagementProvider.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/TenantFeatureManagementProvider.cs new file mode 100644 index 0000000000..928d794671 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/TenantFeatureManagementProvider.cs @@ -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) + { + } + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/TenantFeatureManagerExtensions.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/TenantFeatureManagerExtensions.cs new file mode 100644 index 0000000000..6a26def9a6 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/TenantFeatureManagerExtensions.cs @@ -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 GetOrNullForTenantAsync(this IFeatureManager featureManager, [NotNull] string name, Guid tenantId, bool fallback = true) + { + return featureManager.GetOrNullAsync(name, TenantFeatureValueProvider.ProviderName, tenantId.ToString(), fallback); + } + + public static Task GetOrNullForCurrentTenantAsync(this IFeatureManager featureManager, [NotNull] string name, bool fallback = true) + { + return featureManager.GetOrNullAsync(name, TenantFeatureValueProvider.ProviderName, null, fallback); + } + + public static Task> GetAllForTenantAsync(this IFeatureManager featureManager, Guid tenantId, bool fallback = true) + { + return featureManager.GetAllAsync(TenantFeatureValueProvider.ProviderName, tenantId.ToString(), fallback); + } + + public static Task> 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); + } + } +} diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/Setting.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/Setting.cs index 78e7b3e70c..49c2769237 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/Setting.cs +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/Setting.cs @@ -4,8 +4,7 @@ using Volo.Abp.Domain.Entities; namespace Volo.Abp.SettingManagement { - //TODO: Convert to AggregateRoot - public class Setting : Entity + public class Setting : Entity, IAggregateRoot { [NotNull] public virtual string Name { get; protected set; } diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManagementProvider.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManagementProvider.cs index d350cc28d7..2859b2e7f4 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManagementProvider.cs +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManagementProvider.cs @@ -7,6 +7,7 @@ namespace Volo.Abp.SettingManagement { public abstract string Name { get; } + //TODO: Rename to Store protected ISettingManagementStore SettingManagementStore { get; } protected SettingManagementProvider(ISettingManagementStore settingManagementStore)