diff --git a/Volo.Abp.sln b/Volo.Abp.sln index 259de7b7cf..b709a43440 100644 --- a/Volo.Abp.sln +++ b/Volo.Abp.sln @@ -244,7 +244,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.MultiTenancy.Appli EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.MultiTenancy.Web", "src\Volo.Abp.MultiTenancy.Web\Volo.Abp.MultiTenancy.Web.csproj", "{0808036D-065B-4525-BF30-CBCEA062F8E9}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.MultiTenancy.HttpApi", "src\Volo.Abp.MultiTenancy.HttpApi\Volo.Abp.MultiTenancy.HttpApi.csproj", "{06423FAC-67E2-4B7C-B29F-39578F460C4F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.MultiTenancy.HttpApi", "src\Volo.Abp.MultiTenancy.HttpApi\Volo.Abp.MultiTenancy.HttpApi.csproj", "{06423FAC-67E2-4B7C-B29F-39578F460C4F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Settings", "src\Volo.Abp.Settings\Volo.Abp.Settings.csproj", "{CB6FD800-B6C5-4C2A-8920-B8A29C74AEF6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Settings.EntityFrameworkCore", "src\Volo.Abp.Settings.EntityFrameworkCore\Volo.Abp.Settings.EntityFrameworkCore.csproj", "{1DCD9600-215A-4A94-AEFD-577568D62633}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Settings.Domain", "src\Volo.Abp.Settings.Domain\Volo.Abp.Settings.Domain.csproj", "{837BC537-3AEC-4F6D-9D64-9704F64D0E1E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Settings.Tests", "test\Volo.Abp.Settings.Tests\Volo.Abp.Settings.Tests.csproj", "{61422EC8-8FFB-48EA-B4B5-527E29077930}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Settings.Domain.Shared", "src\Volo.Abp.Settings.Domain.Shared\Volo.Abp.Settings.Domain.Shared.csproj", "{27F990E2-6820-4159-B54A-59C8ADED8D28}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -640,6 +650,26 @@ Global {06423FAC-67E2-4B7C-B29F-39578F460C4F}.Debug|Any CPU.Build.0 = Debug|Any CPU {06423FAC-67E2-4B7C-B29F-39578F460C4F}.Release|Any CPU.ActiveCfg = Release|Any CPU {06423FAC-67E2-4B7C-B29F-39578F460C4F}.Release|Any CPU.Build.0 = Release|Any CPU + {CB6FD800-B6C5-4C2A-8920-B8A29C74AEF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CB6FD800-B6C5-4C2A-8920-B8A29C74AEF6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CB6FD800-B6C5-4C2A-8920-B8A29C74AEF6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CB6FD800-B6C5-4C2A-8920-B8A29C74AEF6}.Release|Any CPU.Build.0 = Release|Any CPU + {1DCD9600-215A-4A94-AEFD-577568D62633}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1DCD9600-215A-4A94-AEFD-577568D62633}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1DCD9600-215A-4A94-AEFD-577568D62633}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1DCD9600-215A-4A94-AEFD-577568D62633}.Release|Any CPU.Build.0 = Release|Any CPU + {837BC537-3AEC-4F6D-9D64-9704F64D0E1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {837BC537-3AEC-4F6D-9D64-9704F64D0E1E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {837BC537-3AEC-4F6D-9D64-9704F64D0E1E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {837BC537-3AEC-4F6D-9D64-9704F64D0E1E}.Release|Any CPU.Build.0 = Release|Any CPU + {61422EC8-8FFB-48EA-B4B5-527E29077930}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {61422EC8-8FFB-48EA-B4B5-527E29077930}.Debug|Any CPU.Build.0 = Debug|Any CPU + {61422EC8-8FFB-48EA-B4B5-527E29077930}.Release|Any CPU.ActiveCfg = Release|Any CPU + {61422EC8-8FFB-48EA-B4B5-527E29077930}.Release|Any CPU.Build.0 = Release|Any CPU + {27F990E2-6820-4159-B54A-59C8ADED8D28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {27F990E2-6820-4159-B54A-59C8ADED8D28}.Debug|Any CPU.Build.0 = Debug|Any CPU + {27F990E2-6820-4159-B54A-59C8ADED8D28}.Release|Any CPU.ActiveCfg = Release|Any CPU + {27F990E2-6820-4159-B54A-59C8ADED8D28}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -759,6 +789,11 @@ Global {B65973F1-9EE0-4914-B6F2-D88F753E1D21} = {3CE22CAC-4B27-4EEC-A35E-C01219ED6E99} {0808036D-065B-4525-BF30-CBCEA062F8E9} = {CA154803-3589-47B3-B7CB-B18F94FE1EB6} {06423FAC-67E2-4B7C-B29F-39578F460C4F} = {CA154803-3589-47B3-B7CB-B18F94FE1EB6} + {CB6FD800-B6C5-4C2A-8920-B8A29C74AEF6} = {4C753F64-0C93-4D65-96C2-A40893AFC1E8} + {1DCD9600-215A-4A94-AEFD-577568D62633} = {4C753F64-0C93-4D65-96C2-A40893AFC1E8} + {837BC537-3AEC-4F6D-9D64-9704F64D0E1E} = {4C753F64-0C93-4D65-96C2-A40893AFC1E8} + {61422EC8-8FFB-48EA-B4B5-527E29077930} = {37087D1B-3693-4E96-983D-A69F210BDE53} + {27F990E2-6820-4159-B54A-59C8ADED8D28} = {4C753F64-0C93-4D65-96C2-A40893AFC1E8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/src/AbpDesk/AbpDesk.EntityFrameworkCore/AbpDesk/EntityFrameworkCore/AbpDeskDbContext.cs b/src/AbpDesk/AbpDesk.EntityFrameworkCore/AbpDesk/EntityFrameworkCore/AbpDeskDbContext.cs index e53b60966f..8a583e93f2 100644 --- a/src/AbpDesk/AbpDesk.EntityFrameworkCore/AbpDesk/EntityFrameworkCore/AbpDeskDbContext.cs +++ b/src/AbpDesk/AbpDesk.EntityFrameworkCore/AbpDesk/EntityFrameworkCore/AbpDeskDbContext.cs @@ -26,7 +26,7 @@ namespace AbpDesk.EntityFrameworkCore { base.OnModelCreating(modelBuilder); - modelBuilder.ConfigureMultiTenancy(MultiTenancyDbContext.TablePrefix, MultiTenancyDbContext.Schema); + modelBuilder.ConfigureMultiTenancy(); //Use different classes to map each entity type, as a better practice? modelBuilder.Entity(b => diff --git a/src/Volo.Abp.Ddd/Volo/Abp/AbpServiceBase.cs b/src/Volo.Abp.Ddd/Volo/Abp/AbpServiceBase.cs index 7911c7a366..01b7aecaf1 100644 --- a/src/Volo.Abp.Ddd/Volo/Abp/AbpServiceBase.cs +++ b/src/Volo.Abp.Ddd/Volo/Abp/AbpServiceBase.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Volo.Abp.Guids; +using Volo.Abp.MultiTenancy; using Volo.Abp.ObjectMapping; using Volo.Abp.Uow; @@ -17,6 +18,8 @@ namespace Volo.Abp public ILoggerFactory LoggerFactory { get; set; } + public ICurrentTenant CurrentTenant { get; set; } + protected IUnitOfWork CurrentUnitOfWork => UnitOfWorkManager?.Current; protected ILogger Logger => _lazyLogger.Value; diff --git a/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContext.cs b/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContext.cs index 18aa2952c1..03b5dea2af 100644 --- a/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContext.cs +++ b/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContext.cs @@ -59,12 +59,6 @@ namespace Volo.Abp.Identity.EntityFrameworkCore } - /// - /// Configures the schema needed for the identity framework. - /// - /// - /// The builder being used to construct the model for this context. - /// protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); diff --git a/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/AbpIdentityServerEntityFrameworkCoreModule.cs b/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/AbpIdentityServerEntityFrameworkCoreModule.cs index e7c787a206..9594451956 100644 --- a/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/AbpIdentityServerEntityFrameworkCoreModule.cs +++ b/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/AbpIdentityServerEntityFrameworkCoreModule.cs @@ -1,6 +1,9 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.IdentityServer.ApiResources; using Volo.Abp.IdentityServer.Clients; +using Volo.Abp.IdentityServer.Grants; +using Volo.Abp.IdentityServer.IdentityResources; using Volo.Abp.Modularity; namespace Volo.Abp.IdentityServer.EntityFrameworkCore @@ -14,7 +17,11 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore services.AddAbpDbContext(options => { options.AddDefaultRepositories(); + options.AddRepository(); + options.AddRepository(); + options.AddRepository(); + options.AddRepository(); }); services.AddAssemblyOf(); diff --git a/src/Volo.Abp.Localization/Volo/Abp/Localization/IInheritedResourceTypesProvider.cs b/src/Volo.Abp.Localization/Volo/Abp/Localization/IInheritedResourceTypesProvider.cs index c9e41774cd..ca3325f808 100644 --- a/src/Volo.Abp.Localization/Volo/Abp/Localization/IInheritedResourceTypesProvider.cs +++ b/src/Volo.Abp.Localization/Volo/Abp/Localization/IInheritedResourceTypesProvider.cs @@ -6,6 +6,6 @@ namespace Volo.Abp.Localization public interface IInheritedResourceTypesProvider { [NotNull] - Type[] GetInheritedModuleTypes(); + Type[] GetInheritedResourceTypes(); } } \ No newline at end of file diff --git a/src/Volo.Abp.Localization/Volo/Abp/Localization/InheritResourceAttribute.cs b/src/Volo.Abp.Localization/Volo/Abp/Localization/InheritResourceAttribute.cs index 1ee8aa85e9..3783ac0da3 100644 --- a/src/Volo.Abp.Localization/Volo/Abp/Localization/InheritResourceAttribute.cs +++ b/src/Volo.Abp.Localization/Volo/Abp/Localization/InheritResourceAttribute.cs @@ -12,7 +12,7 @@ namespace Volo.Abp.Localization ResourceTypes = resourceTypes ?? new Type[0]; } - public Type[] GetInheritedModuleTypes() + public virtual Type[] GetInheritedResourceTypes() { return ResourceTypes; } diff --git a/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResource.cs b/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResource.cs index 82f876d6e0..547035fbaf 100644 --- a/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResource.cs +++ b/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResource.cs @@ -82,7 +82,7 @@ namespace Volo.Abp.Localization foreach (var descriptor in descriptors) { - foreach (var baseResourceType in descriptor.GetInheritedModuleTypes()) + foreach (var baseResourceType in descriptor.GetInheritedResourceTypes()) { BaseResourceTypes.AddIfNotContains(baseResourceType); } diff --git a/src/Volo.Abp.MultiTenancy.Abstractions/Volo.Abp.MultiTenancy.Abstractions.csproj b/src/Volo.Abp.MultiTenancy.Abstractions/Volo.Abp.MultiTenancy.Abstractions.csproj index 13c109ad2a..07f95cb1ee 100644 --- a/src/Volo.Abp.MultiTenancy.Abstractions/Volo.Abp.MultiTenancy.Abstractions.csproj +++ b/src/Volo.Abp.MultiTenancy.Abstractions/Volo.Abp.MultiTenancy.Abstractions.csproj @@ -16,6 +16,7 @@ + diff --git a/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/AbpMultiTenancyAbstractionsModule.cs b/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/AbpMultiTenancyAbstractionsModule.cs index 767fd2a6a0..efb8d4cff4 100644 --- a/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/AbpMultiTenancyAbstractionsModule.cs +++ b/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/AbpMultiTenancyAbstractionsModule.cs @@ -1,14 +1,21 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Data; using Volo.Abp.Modularity; +using Volo.Abp.Settings; namespace Volo.Abp.MultiTenancy { [DependsOn(typeof(AbpDataModule))] + [DependsOn(typeof(AbpSettingsModule))] public class AbpMultiTenancyAbstractionsModule : AbpModule { public override void ConfigureServices(IServiceCollection services) { + services.Configure(options => + { + options.ValueProviders.Add(); + }); + services.AddAssemblyOf(); } } diff --git a/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/CurrentTenant.cs b/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/CurrentTenant.cs index 1cee215eea..743355d415 100644 --- a/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/CurrentTenant.cs +++ b/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/CurrentTenant.cs @@ -21,7 +21,7 @@ namespace Volo.Abp.MultiTenancy return SetCurrent(id); } - public IDisposable Clear() + public IDisposable Clear() //TODO: Remove { return Change(null); } diff --git a/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/TenantSettingValueProvider.cs b/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/TenantSettingValueProvider.cs new file mode 100644 index 0000000000..2f5ff72a6d --- /dev/null +++ b/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/TenantSettingValueProvider.cs @@ -0,0 +1,45 @@ +using System.Threading.Tasks; +using Volo.Abp.Settings; + +namespace Volo.Abp.MultiTenancy +{ + public class TenantSettingValueProvider : SettingValueProvider + { + public const string ProviderName = "Tenant"; + + public override string Name => ProviderName; + + protected ICurrentTenant CurrentTenant { get; } + + public TenantSettingValueProvider(ISettingStore settingStore, ICurrentTenant currentTenant) + : base(settingStore) + { + CurrentTenant = currentTenant; + } + + public override async Task GetOrNullAsync(SettingDefinition setting, string providerKey) + { + if (providerKey == null) + { + if (CurrentTenant.Id == null) + { + return null; + } + + providerKey = CurrentTenant.Id.ToString(); + } + + return await SettingStore.GetOrNullAsync(setting.Name, Name, providerKey); + } + + public override Task SetAsync(SettingDefinition setting, string value, string providerKey) + { + return SettingStore.SetAsync(setting.Name, value, Name, providerKey); + } + + public override Task ClearAsync(SettingDefinition setting, string providerKey) + { + return SettingStore.DeleteAsync(setting.Name, Name, providerKey); + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/Settings/TenantSettingManagerExtensions.cs b/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/Settings/TenantSettingManagerExtensions.cs new file mode 100644 index 0000000000..f3500bf80a --- /dev/null +++ b/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/Settings/TenantSettingManagerExtensions.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.Settings +{ + public static class TenantSettingManagerExtensions + { + public static Task GetOrNullForTenantAsync(this ISettingManager settingManager, [NotNull] string name, Guid tenantId, bool fallback = true) + { + return settingManager.GetOrNullAsync(name, TenantSettingValueProvider.ProviderName, tenantId.ToString(), fallback); + } + + public static Task GetOrNullForCurrentTenantAsync(this ISettingManager settingManager, [NotNull] string name, bool fallback = true) + { + return settingManager.GetOrNullAsync(name, TenantSettingValueProvider.ProviderName, null, fallback); + } + + public static Task> GetAllForTenantAsync(this ISettingManager settingManager, Guid tenantId, bool fallback = true) + { + return settingManager.GetAllAsync(TenantSettingValueProvider.ProviderName, tenantId.ToString(), fallback); + } + + public static Task> GetAllForCurrentTenantAsync(this ISettingManager settingManager, bool fallback = true) + { + return settingManager.GetAllAsync(TenantSettingValueProvider.ProviderName, null, fallback); + } + + public static Task SetForTenantAsync(this ISettingManager settingManager, Guid tenantId, [NotNull] string name, [CanBeNull] string value, bool forceToSet = false) + { + return settingManager.SetAsync(name, value, TenantSettingValueProvider.ProviderName, tenantId.ToString(), forceToSet); + } + + public static Task SetForCurrentTenantAsync(this ISettingManager settingManager, [NotNull] string name, [CanBeNull] string value, bool forceToSet = false) + { + return settingManager.SetAsync(name, value, TenantSettingValueProvider.ProviderName, null, forceToSet); + } + } +} diff --git a/src/Volo.Abp.MultiTenancy.Domain/Volo/Abp/MultiTenancy/AbpIdentityConsts.cs b/src/Volo.Abp.MultiTenancy.Domain/Volo/Abp/MultiTenancy/AbpMultiTenancyConsts.cs similarity index 100% rename from src/Volo.Abp.MultiTenancy.Domain/Volo/Abp/MultiTenancy/AbpIdentityConsts.cs rename to src/Volo.Abp.MultiTenancy.Domain/Volo/Abp/MultiTenancy/AbpMultiTenancyConsts.cs diff --git a/src/Volo.Abp.Session/Volo.Abp.Session.csproj b/src/Volo.Abp.Session/Volo.Abp.Session.csproj index dde6a11a58..6aef78ea80 100644 --- a/src/Volo.Abp.Session/Volo.Abp.Session.csproj +++ b/src/Volo.Abp.Session/Volo.Abp.Session.csproj @@ -16,6 +16,7 @@ + diff --git a/src/Volo.Abp.Session/Volo/Abp/Session/AbpSessionModule.cs b/src/Volo.Abp.Session/Volo/Abp/Session/AbpSessionModule.cs index b547f74663..33cc5e2a88 100644 --- a/src/Volo.Abp.Session/Volo/Abp/Session/AbpSessionModule.cs +++ b/src/Volo.Abp.Session/Volo/Abp/Session/AbpSessionModule.cs @@ -1,14 +1,21 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Modularity; using Volo.Abp.Security; +using Volo.Abp.Settings; namespace Volo.Abp.Session { [DependsOn(typeof(AbpSecurityModule))] + [DependsOn(typeof(AbpSettingsModule))] public class AbpSessionModule : AbpModule { public override void ConfigureServices(IServiceCollection services) { + services.Configure(options => + { + options.ValueProviders.Add(); + }); + services.AddAssemblyOf(); } } diff --git a/src/Volo.Abp.Session/Volo/Abp/Session/UserSettingValueProvider.cs b/src/Volo.Abp.Session/Volo/Abp/Session/UserSettingValueProvider.cs new file mode 100644 index 0000000000..87217d3478 --- /dev/null +++ b/src/Volo.Abp.Session/Volo/Abp/Session/UserSettingValueProvider.cs @@ -0,0 +1,47 @@ +using System.Threading.Tasks; +using Volo.Abp.Settings; + +namespace Volo.Abp.Session +{ + //TODO: Optimization: Get all settings and cache it! + + public class UserSettingValueProvider : SettingValueProvider + { + public const string ProviderName = "User"; + + public override string Name => ProviderName; + + protected ICurrentUser CurrentUser { get; } + + public UserSettingValueProvider(ISettingStore settingStore, ICurrentUser currentUser) + : base(settingStore) + { + CurrentUser = currentUser; + } + + public override async Task GetOrNullAsync(SettingDefinition setting, string providerKey) + { + if (providerKey == null) + { + if (CurrentUser.Id == null) + { + return null; + } + + providerKey = CurrentUser.Id.ToString(); + } + + return await SettingStore.GetOrNullAsync(setting.Name, Name, providerKey); + } + + public override Task SetAsync(SettingDefinition setting, string value, string providerKey) + { + return SettingStore.SetAsync(setting.Name, value, Name, providerKey); + } + + public override Task ClearAsync(SettingDefinition setting, string providerKey) + { + return SettingStore.DeleteAsync(setting.Name, Name, providerKey); + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Session/Volo/Abp/Settings/UserSettingManagerExtensions.cs b/src/Volo.Abp.Session/Volo/Abp/Settings/UserSettingManagerExtensions.cs new file mode 100644 index 0000000000..9387c5c607 --- /dev/null +++ b/src/Volo.Abp.Session/Volo/Abp/Settings/UserSettingManagerExtensions.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Volo.Abp.Session; + +namespace Volo.Abp.Settings +{ + public static class UserSettingManagerExtensions + { + public static Task GetOrNullForUserAsync(this ISettingManager settingManager, [NotNull] string name, Guid userId, bool fallback = true) + { + return settingManager.GetOrNullAsync(name, UserSettingValueProvider.ProviderName, userId.ToString(), fallback); + } + + public static Task GetOrNullForCurrentUserAsync(this ISettingManager settingManager, [NotNull] string name, bool fallback = true) + { + return settingManager.GetOrNullAsync(name, UserSettingValueProvider.ProviderName, null, fallback); + } + + public static Task> GetAllForUserAsync(this ISettingManager settingManager, Guid userId, bool fallback = true) + { + return settingManager.GetAllAsync(UserSettingValueProvider.ProviderName, userId.ToString(), fallback); + } + + public static Task> GetAllForCurrentUserAsync(this ISettingManager settingManager, bool fallback = true) + { + return settingManager.GetAllAsync(UserSettingValueProvider.ProviderName, null, fallback); + } + + public static Task SetForUserAsync(this ISettingManager settingManager, Guid userId, [NotNull] string name, [CanBeNull] string value, bool forceToSet = false) + { + return settingManager.SetAsync(name, value, UserSettingValueProvider.ProviderName, userId.ToString(), forceToSet); + } + + public static Task SetForCurrentUserAsync(this ISettingManager settingManager, [NotNull] string name, [CanBeNull] string value, bool forceToSet = false) + { + return settingManager.SetAsync(name, value, UserSettingValueProvider.ProviderName, null, forceToSet); + } + } +} diff --git a/src/Volo.Abp.Settings.Domain.Shared/Volo.Abp.Settings.Domain.Shared.csproj b/src/Volo.Abp.Settings.Domain.Shared/Volo.Abp.Settings.Domain.Shared.csproj new file mode 100644 index 0000000000..1ae08840a7 --- /dev/null +++ b/src/Volo.Abp.Settings.Domain.Shared/Volo.Abp.Settings.Domain.Shared.csproj @@ -0,0 +1,20 @@ + + + + + + netstandard2.0 + Volo.Abp.Settings.Domain.Shared + Volo.Abp.Settings.Domain.Shared + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + diff --git a/src/Volo.Abp.Settings.Domain.Shared/Volo/Abp/Settings/AbpSettingsDomainSharedModule.cs b/src/Volo.Abp.Settings.Domain.Shared/Volo/Abp/Settings/AbpSettingsDomainSharedModule.cs new file mode 100644 index 0000000000..fdf5f9e41c --- /dev/null +++ b/src/Volo.Abp.Settings.Domain.Shared/Volo/Abp/Settings/AbpSettingsDomainSharedModule.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; + +namespace Volo.Abp.Settings +{ + public class AbpSettingsDomainSharedModule : AbpModule + { + public override void ConfigureServices(IServiceCollection services) + { + services.AddAssemblyOf(); + } + } +} diff --git a/src/Volo.Abp.Settings.Domain.Shared/Volo/Abp/Settings/SettingsConsts.cs b/src/Volo.Abp.Settings.Domain.Shared/Volo/Abp/Settings/SettingsConsts.cs new file mode 100644 index 0000000000..0b25a74bf8 --- /dev/null +++ b/src/Volo.Abp.Settings.Domain.Shared/Volo/Abp/Settings/SettingsConsts.cs @@ -0,0 +1,10 @@ +namespace Volo.Abp.Settings +{ + public static class SettingsConsts + { + public const int MaxNameLength = 128; + public const int MaxValueLength = 2048; + public const int MaxProviderNameLength = 64; + public const int MaxProviderKeyLength = 64; + } +} diff --git a/src/Volo.Abp.Settings.Domain/Volo.Abp.Settings.Domain.csproj b/src/Volo.Abp.Settings.Domain/Volo.Abp.Settings.Domain.csproj new file mode 100644 index 0000000000..b980a78f55 --- /dev/null +++ b/src/Volo.Abp.Settings.Domain/Volo.Abp.Settings.Domain.csproj @@ -0,0 +1,22 @@ + + + + + + netstandard2.0 + Volo.Abp.Settings.Domain + Volo.Abp.Settings.Domain + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + + diff --git a/src/Volo.Abp.Settings.Domain/Volo/Abp/Settings/AbpSettingsConsts.cs b/src/Volo.Abp.Settings.Domain/Volo/Abp/Settings/AbpSettingsConsts.cs new file mode 100644 index 0000000000..f3a9f4653c --- /dev/null +++ b/src/Volo.Abp.Settings.Domain/Volo/Abp/Settings/AbpSettingsConsts.cs @@ -0,0 +1,9 @@ +namespace Volo.Abp.Settings +{ + public static class AbpSettingsConsts + { + public const string DefaultDbTablePrefix = "Abp"; + + public const string DefaultDbSchema = null; + } +} diff --git a/src/Volo.Abp.Settings.Domain/Volo/Abp/Settings/AbpSettingsDomainModule.cs b/src/Volo.Abp.Settings.Domain/Volo/Abp/Settings/AbpSettingsDomainModule.cs new file mode 100644 index 0000000000..7bbb6659b8 --- /dev/null +++ b/src/Volo.Abp.Settings.Domain/Volo/Abp/Settings/AbpSettingsDomainModule.cs @@ -0,0 +1,16 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; + +namespace Volo.Abp.Settings +{ + [DependsOn(typeof(AbpSettingsModule))] + [DependsOn(typeof(AbpDddModule))] + [DependsOn(typeof(AbpSettingsDomainSharedModule))] + public class AbpSettingsDomainModule : AbpModule + { + public override void ConfigureServices(IServiceCollection services) + { + services.AddAssemblyOf(); + } + } +} diff --git a/src/Volo.Abp.Settings.Domain/Volo/Abp/Settings/ISettingRepository.cs b/src/Volo.Abp.Settings.Domain/Volo/Abp/Settings/ISettingRepository.cs new file mode 100644 index 0000000000..5084e51ec5 --- /dev/null +++ b/src/Volo.Abp.Settings.Domain/Volo/Abp/Settings/ISettingRepository.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; + +namespace Volo.Abp.Settings +{ + public interface ISettingRepository : IBasicRepository + { + Task FindAsync(string name, string providerName, string providerKey); + + Task> GetListAsync(string providerName, string providerKey); + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Settings.Domain/Volo/Abp/Settings/Setting.cs b/src/Volo.Abp.Settings.Domain/Volo/Abp/Settings/Setting.cs new file mode 100644 index 0000000000..9a04000f50 --- /dev/null +++ b/src/Volo.Abp.Settings.Domain/Volo/Abp/Settings/Setting.cs @@ -0,0 +1,43 @@ +using System; +using JetBrains.Annotations; +using Volo.Abp.Domain.Entities; + +namespace Volo.Abp.Settings +{ + public class Setting : Entity + { + [NotNull] + public virtual string Name { get; protected set; } + + [NotNull] + public virtual string Value { get; internal set; } + + [CanBeNull] + public virtual string ProviderName { get; protected set; } + + [CanBeNull] + public virtual string ProviderKey { get; protected set; } + + protected Setting() + { + + } + + public Setting( + Guid id, + [NotNull] string name, + [NotNull] string value, + [CanBeNull] string providerName = null, + [CanBeNull] string providerKey = null) + { + Check.NotNull(name, nameof(name)); + Check.NotNull(value, nameof(value)); + + Id = id; + Name = name; + Value = value; + ProviderName = providerName; + ProviderKey = providerKey; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Settings.Domain/Volo/Abp/Settings/SettingStore.cs b/src/Volo.Abp.Settings.Domain/Volo/Abp/Settings/SettingStore.cs new file mode 100644 index 0000000000..a1bb9b3c3d --- /dev/null +++ b/src/Volo.Abp.Settings.Domain/Volo/Abp/Settings/SettingStore.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Settings +{ + public class SettingStore : AbpServiceBase, ISettingStore, ITransientDependency + { + private readonly ISettingRepository _settingRepository; + + public SettingStore(ISettingRepository settingRepository) + { + _settingRepository = settingRepository; + } + + public async Task GetOrNullAsync(string name, string providerName, string providerKey) + { + var setting = await _settingRepository.FindAsync(name, providerName, providerKey); + return setting?.Value; + } + + public async Task SetAsync(string name, string value, string providerName, string providerKey) + { + var setting = await _settingRepository.FindAsync(name, providerName, providerKey); + if (setting == null) + { + setting = new Setting(GuidGenerator.Create(), name, value, providerName, providerKey); + await _settingRepository.InsertAsync(setting); + } + + setting.Value = value; + await _settingRepository.UpdateAsync(setting); + } + + public async Task> GetListAsync(string providerName, string providerKey) + { + var setting = await _settingRepository.GetListAsync(providerName, providerKey); + return setting.Select(s => new SettingValue(s.Name, s.Value)).ToList(); + } + + public async Task DeleteAsync(string name, string providerName, string providerKey) + { + var setting = await _settingRepository.FindAsync(name, providerName, providerKey); + if (setting != null) + { + await _settingRepository.DeleteAsync(setting); + } + } + } +} diff --git a/src/Volo.Abp.Settings.EntityFrameworkCore/Volo.Abp.Settings.EntityFrameworkCore.csproj b/src/Volo.Abp.Settings.EntityFrameworkCore/Volo.Abp.Settings.EntityFrameworkCore.csproj new file mode 100644 index 0000000000..8ca5ce8156 --- /dev/null +++ b/src/Volo.Abp.Settings.EntityFrameworkCore/Volo.Abp.Settings.EntityFrameworkCore.csproj @@ -0,0 +1,21 @@ + + + + + + netstandard2.0 + Volo.Abp.Settings.EntityFrameworkCore + Volo.Abp.Settings.EntityFrameworkCore + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + diff --git a/src/Volo.Abp.Settings.EntityFrameworkCore/Volo/Abp/Settings/EntityFrameworkCore/AbpSettingsDbContext.cs b/src/Volo.Abp.Settings.EntityFrameworkCore/Volo/Abp/Settings/EntityFrameworkCore/AbpSettingsDbContext.cs new file mode 100644 index 0000000000..0ce713d764 --- /dev/null +++ b/src/Volo.Abp.Settings.EntityFrameworkCore/Volo/Abp/Settings/EntityFrameworkCore/AbpSettingsDbContext.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore; +using Volo.Abp.Data; +using Volo.Abp.EntityFrameworkCore; + +namespace Volo.Abp.Settings.EntityFrameworkCore +{ + [ConnectionStringName("AbpSettings")] + public class AbpSettingsDbContext : AbpDbContext, IAbpSettingsDbContext + { + public static string TablePrefix { get; set; } = AbpSettingsConsts.DefaultDbTablePrefix; + + public static string Schema { get; set; } = AbpSettingsConsts.DefaultDbSchema; + + public DbSet Settings { get; set; } + + public AbpSettingsDbContext(DbContextOptions options) + : base(options) + { + + } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + builder.ConfigureAbpSettings(TablePrefix, Schema); + } + } +} diff --git a/src/Volo.Abp.Settings.EntityFrameworkCore/Volo/Abp/Settings/EntityFrameworkCore/AbpSettingsDbContextModelBuilderExtensions.cs b/src/Volo.Abp.Settings.EntityFrameworkCore/Volo/Abp/Settings/EntityFrameworkCore/AbpSettingsDbContextModelBuilderExtensions.cs new file mode 100644 index 0000000000..15b91d3cf7 --- /dev/null +++ b/src/Volo.Abp.Settings.EntityFrameworkCore/Volo/Abp/Settings/EntityFrameworkCore/AbpSettingsDbContextModelBuilderExtensions.cs @@ -0,0 +1,33 @@ +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Volo.Abp.Settings.EntityFrameworkCore +{ + public static class AbpSettingsDbContextModelBuilderExtensions + { + public static void ConfigureAbpSettings( + [NotNull] this ModelBuilder builder, + [CanBeNull] string tablePrefix = AbpSettingsConsts.DefaultDbTablePrefix, + [CanBeNull] string schema = AbpSettingsConsts.DefaultDbSchema) + { + Check.NotNull(builder, nameof(builder)); + + if (tablePrefix == null) + { + tablePrefix = ""; + } + + builder.Entity(b => + { + b.ToTable(tablePrefix + "Settings", schema); + + b.Property(x => x.Name).HasMaxLength(SettingsConsts.MaxNameLength).IsRequired(); + b.Property(x => x.Value).HasMaxLength(SettingsConsts.MaxValueLength).IsRequired(); + b.Property(x => x.ProviderName).HasMaxLength(SettingsConsts.MaxProviderNameLength); + b.Property(x => x.ProviderKey).HasMaxLength(SettingsConsts.MaxProviderKeyLength); + + b.HasIndex(x => new {x.Name, x.ProviderName, x.ProviderKey}); + }); + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Settings.EntityFrameworkCore/Volo/Abp/Settings/EntityFrameworkCore/AbpSettingsEntityFrameworkCoreModule.cs b/src/Volo.Abp.Settings.EntityFrameworkCore/Volo/Abp/Settings/EntityFrameworkCore/AbpSettingsEntityFrameworkCoreModule.cs new file mode 100644 index 0000000000..013c32d16f --- /dev/null +++ b/src/Volo.Abp.Settings.EntityFrameworkCore/Volo/Abp/Settings/EntityFrameworkCore/AbpSettingsEntityFrameworkCoreModule.cs @@ -0,0 +1,23 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Modularity; + +namespace Volo.Abp.Settings.EntityFrameworkCore +{ + [DependsOn(typeof(AbpSettingsDomainModule))] + [DependsOn(typeof(AbpEntityFrameworkCoreModule))] + public class AbpSettingsEntityFrameworkCoreModule : AbpModule + { + public override void ConfigureServices(IServiceCollection services) + { + services.AddAbpDbContext(options => + { + options.AddDefaultRepositories(); + + options.AddRepository(); + }); + + services.AddAssemblyOf(); + } + } +} diff --git a/src/Volo.Abp.Settings.EntityFrameworkCore/Volo/Abp/Settings/EntityFrameworkCore/EfCoreSettingRepository.cs b/src/Volo.Abp.Settings.EntityFrameworkCore/Volo/Abp/Settings/EntityFrameworkCore/EfCoreSettingRepository.cs new file mode 100644 index 0000000000..3f31147390 --- /dev/null +++ b/src/Volo.Abp.Settings.EntityFrameworkCore/Volo/Abp/Settings/EntityFrameworkCore/EfCoreSettingRepository.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace Volo.Abp.Settings.EntityFrameworkCore +{ + public class EfCoreSettingRepository : EfCoreRepository, ISettingRepository + { + public EfCoreSettingRepository(IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } + + public async Task FindAsync(string name, string providerName, string providerKey) + { + return await DbSet.FirstOrDefaultAsync(s => s.Name == name && s.ProviderName == providerName && s.ProviderKey == providerKey); + } + + public async Task> GetListAsync(string providerName, string providerKey) + { + return await DbSet.Where(s => s.ProviderName == providerName && s.ProviderKey == providerKey).ToListAsync(); + } + } +} diff --git a/src/Volo.Abp.Settings.EntityFrameworkCore/Volo/Abp/Settings/EntityFrameworkCore/IAbpSettingsDbContext.cs b/src/Volo.Abp.Settings.EntityFrameworkCore/Volo/Abp/Settings/EntityFrameworkCore/IAbpSettingsDbContext.cs new file mode 100644 index 0000000000..1ef5e93867 --- /dev/null +++ b/src/Volo.Abp.Settings.EntityFrameworkCore/Volo/Abp/Settings/EntityFrameworkCore/IAbpSettingsDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace Volo.Abp.Settings.EntityFrameworkCore +{ + public interface IAbpSettingsDbContext : IEfCoreDbContext + { + DbSet Settings { get; set; } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Settings/Volo.Abp.Settings.csproj b/src/Volo.Abp.Settings/Volo.Abp.Settings.csproj new file mode 100644 index 0000000000..a26679d049 --- /dev/null +++ b/src/Volo.Abp.Settings/Volo.Abp.Settings.csproj @@ -0,0 +1,20 @@ + + + + + + netstandard2.0 + Volo.Abp.Settings + Volo.Abp.Settings + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/AbpSettingsModule.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/AbpSettingsModule.cs new file mode 100644 index 0000000000..0018d495a4 --- /dev/null +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/AbpSettingsModule.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; + +namespace Volo.Abp.Settings +{ + public class AbpSettingsModule : AbpModule + { + public override void ConfigureServices(IServiceCollection services) + { + services.AddAssemblyOf(); + } + } +} diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/DefaultValueSettingValueProvider.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/DefaultValueSettingValueProvider.cs new file mode 100644 index 0000000000..0d236a4336 --- /dev/null +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/DefaultValueSettingValueProvider.cs @@ -0,0 +1,34 @@ +using System.Threading.Tasks; + +namespace Volo.Abp.Settings +{ + public class DefaultValueSettingValueProvider : SettingValueProvider + { + public const string ProviderName = null; + + public override string Name => ProviderName; + + public DefaultValueSettingValueProvider(ISettingStore settingStore) + : base(settingStore) + { + + } + + public override Task GetOrNullAsync(SettingDefinition setting, string providerKey) + { + return Task.FromResult(setting.DefaultValue); + } + + public override Task SetAsync(SettingDefinition setting, string value, string providerKey) + { + setting.DefaultValue = value; + return Task.CompletedTask; + } + + public override Task ClearAsync(SettingDefinition setting, string providerKey) + { + setting.DefaultValue = null; + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/GlobalSettingManagerExtensions.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/GlobalSettingManagerExtensions.cs new file mode 100644 index 0000000000..d5dabccfe8 --- /dev/null +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/GlobalSettingManagerExtensions.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using JetBrains.Annotations; + +namespace Volo.Abp.Settings +{ + public static class GlobalSettingManagerExtensions + { + public static Task GetOrNullGlobalAsync(this ISettingManager settingManager, [NotNull] string name, bool fallback = true) + { + return settingManager.GetOrNullAsync(name, GlobalSettingValueProvider.ProviderName, null, fallback); + } + + public static Task> GetAllGlobalAsync(this ISettingManager settingManager, bool fallback = true) + { + return settingManager.GetAllAsync(GlobalSettingValueProvider.ProviderName, null, fallback); + } + + public static Task SetGlobalAsync(this ISettingManager settingManager, [NotNull] string name, [CanBeNull] string value) + { + return settingManager.SetAsync(name, value, GlobalSettingValueProvider.ProviderName, null); + } + } +} diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/GlobalSettingValueProvider.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/GlobalSettingValueProvider.cs new file mode 100644 index 0000000000..78a38cc703 --- /dev/null +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/GlobalSettingValueProvider.cs @@ -0,0 +1,31 @@ +using System.Threading.Tasks; + +namespace Volo.Abp.Settings +{ + public class GlobalSettingValueProvider : SettingValueProvider + { + public const string ProviderName = "Global"; + + public override string Name => ProviderName; + + public GlobalSettingValueProvider(ISettingStore settingStore) + : base(settingStore) + { + } + + public override Task GetOrNullAsync(SettingDefinition setting, string providerKey) + { + return SettingStore.GetOrNullAsync(setting.Name, Name, null); + } + + public override Task SetAsync(SettingDefinition setting, string value, string providerKey) + { + return SettingStore.SetAsync(setting.Name, value, Name, null); + } + + public override Task ClearAsync(SettingDefinition setting, string providerKey) + { + return SettingStore.DeleteAsync(setting.Name, Name, null); + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingDefinitionContext.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingDefinitionContext.cs new file mode 100644 index 0000000000..220e40fe59 --- /dev/null +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingDefinitionContext.cs @@ -0,0 +1,9 @@ +namespace Volo.Abp.Settings +{ + public interface ISettingDefinitionContext + { + SettingDefinition GetOrNull(string name); + + void Add(params SettingDefinition[] definitions); + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingDefinitionManager.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingDefinitionManager.cs new file mode 100644 index 0000000000..176f5b314c --- /dev/null +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingDefinitionManager.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using JetBrains.Annotations; + +namespace Volo.Abp.Settings +{ + public interface ISettingDefinitionManager + { + [NotNull] + SettingDefinition Get([NotNull] string name); + + IReadOnlyList GetAll(); + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingDefinitionProvider.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingDefinitionProvider.cs new file mode 100644 index 0000000000..383b54088f --- /dev/null +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingDefinitionProvider.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.Settings +{ + public interface ISettingDefinitionProvider + { + void Define(ISettingDefinitionContext context); + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingManager.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingManager.cs new file mode 100644 index 0000000000..9c794a2c9b --- /dev/null +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingManager.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using JetBrains.Annotations; + +namespace Volo.Abp.Settings +{ + public interface ISettingManager + { + Task GetOrNullAsync([NotNull]string name); + + Task GetOrNullAsync([NotNull]string name, [NotNull] string providerName, [CanBeNull] string providerKey, bool fallback = true); + + Task> GetAllAsync(); + + 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/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingStore.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingStore.cs new file mode 100644 index 0000000000..40ee68f462 --- /dev/null +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingStore.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using JetBrains.Annotations; + +namespace Volo.Abp.Settings +{ + public interface ISettingStore + { + Task GetOrNullAsync([NotNull] string name, [CanBeNull] string providerName, [CanBeNull] string providerKey); + + Task SetAsync([NotNull] string name, [NotNull] string value, [CanBeNull] string providerName, [CanBeNull] string providerKey); + + Task> GetListAsync([CanBeNull] string providerName, [CanBeNull] string providerKey); + + Task DeleteAsync([NotNull] string name, [CanBeNull]string providerName, [CanBeNull]string providerKey); + } +} diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingValueProvider.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingValueProvider.cs new file mode 100644 index 0000000000..830546b23d --- /dev/null +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingValueProvider.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; +using JetBrains.Annotations; + +namespace Volo.Abp.Settings +{ + public interface ISettingValueProvider + { + string Name { get; } + + Task GetOrNullAsync([NotNull] SettingDefinition setting, [CanBeNull] string providerKey); + + Task SetAsync([NotNull] SettingDefinition setting, [NotNull] string value, [CanBeNull] string providerKey); + + Task ClearAsync([NotNull] SettingDefinition setting, [CanBeNull] string providerKey); + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/NullSettingStore.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/NullSettingStore.cs new file mode 100644 index 0000000000..ade721431d --- /dev/null +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/NullSettingStore.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Settings +{ + public class NullSettingStore : ISettingStore, ISingletonDependency + { + public Task GetOrNullAsync(string name, string providerName, string providerKey) + { + return Task.FromResult((string) null); + } + + public Task SetAsync(string name, string value, string providerName, string providerKey) + { + return Task.CompletedTask; + } + + public Task> GetListAsync(string providerName, string providerKey) + { + return Task.FromResult(new List()); + } + + public Task DeleteAsync(string name, string providerName, string providerKey) + { + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinition.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinition.cs new file mode 100644 index 0000000000..2de5a690a7 --- /dev/null +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinition.cs @@ -0,0 +1,47 @@ +namespace Volo.Abp.Settings +{ + public class SettingDefinition + { + /// + /// Unique name of the setting. + /// + public string Name { get; } + + /// + /// Default value of the setting. + /// + public string DefaultValue { get; set; } + + /// + /// Can clients see this setting and it's value. + /// It maybe dangerous for some settings to be visible to clients (such as an email server password). + /// Default: false. + /// + public bool IsVisibleToClients { get; set; } + + /// + /// Is this setting inherited from parent scopes. + /// Default: True. + /// + public bool IsInherited { get; set; } + + /// + /// Can be used to store a custom object related to this setting. + /// + public object CustomData { get; set; } + + public SettingDefinition( + string name, + string defaultValue = null, + bool isVisibleToClients = false, + bool isInherited = true, + object customData = null) + { + Name = name; + DefaultValue = defaultValue; + IsVisibleToClients = isVisibleToClients; + IsInherited = isInherited; + CustomData = customData; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionContext.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionContext.cs new file mode 100644 index 0000000000..f7d993945e --- /dev/null +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionContext.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Collections.Immutable; + +namespace Volo.Abp.Settings +{ + public class SettingDefinitionContext : ISettingDefinitionContext + { + protected Dictionary Settings { get; } + + public SettingDefinitionContext(Dictionary settings) + { + Settings = settings; + } + + public virtual SettingDefinition GetOrNull(string name) + { + return Settings.GetOrDefault(name); + } + + public virtual IReadOnlyList GetAll() + { + return Settings.Values.ToImmutableList(); + } + + public virtual void Add(params SettingDefinition[] definitions) + { + if (definitions.IsNullOrEmpty()) + { + return; + } + + foreach (var definition in definitions) + { + Settings[definition.Name] = definition; + } + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionManager.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionManager.cs new file mode 100644 index 0000000000..4615d1c147 --- /dev/null +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionManager.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Settings +{ + public class SettingDefinitionManager : ISettingDefinitionManager, ISingletonDependency + { + protected Lazy> Providers { get; } + + protected Lazy> SettingDefinitions { get; } + + protected SettingOptions Options { get; } + + private readonly IServiceProvider _serviceProvider; + + public SettingDefinitionManager( + IOptions options, + IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + Options = options.Value; + + Providers = new Lazy>(CreateSettingProviders, true); + SettingDefinitions = new Lazy>(CreateSettingDefinitions, true); + } + + public virtual SettingDefinition Get(string name) + { + Check.NotNull(name, nameof(name)); + + var settingDefinition = GetOrNull(name); + + if (settingDefinition == null) + { + throw new AbpException("Undefined setting: " + name); + } + + return settingDefinition; + } + + public IReadOnlyList GetAll() + { + return SettingDefinitions.Value.Values.ToImmutableList(); + } + + public virtual SettingDefinition GetOrNull(string name) + { + return SettingDefinitions.Value.GetOrDefault(name); + } + + private List CreateSettingProviders() + { + return Options + .DefinitionProviders + .Select(p => _serviceProvider.GetRequiredService(p) as ISettingDefinitionProvider) + .ToList(); + } + + private IDictionary CreateSettingDefinitions() + { + var settings = new Dictionary(); + + foreach (var provider in Providers.Value) + { + provider.Define(new SettingDefinitionContext(settings)); + } + + return settings; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionProvider.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionProvider.cs new file mode 100644 index 0000000000..04e6fc7eb2 --- /dev/null +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionProvider.cs @@ -0,0 +1,9 @@ +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Settings +{ + public abstract class SettingDefinitionProvider : ISettingDefinitionProvider, ISingletonDependency + { + public abstract void Define(ISettingDefinitionContext context); + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingManager.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingManager.cs new file mode 100644 index 0000000000..fb3f6cca0f --- /dev/null +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingManager.cs @@ -0,0 +1,194 @@ +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; + +namespace Volo.Abp.Settings +{ + public class SettingManager : ISettingManager, ISingletonDependency + { + protected ISettingDefinitionManager SettingDefinitionManager { get; } + + protected Lazy> Providers { get; } + + protected SettingOptions Options { get; } + + protected ISettingStore SettingStore { get; } + + public SettingManager( + IOptions options, + IServiceProvider serviceProvider, + ISettingDefinitionManager settingDefinitionManager, + ISettingStore settingStore) + { + SettingStore = settingStore; + SettingDefinitionManager = settingDefinitionManager; + Options = options.Value; + + Providers = new Lazy>( + () => Options + .ValueProviders + .Select(c => serviceProvider.GetRequiredService(c) as ISettingValueProvider) + .ToList(), + true + ); + } + + public virtual Task GetOrNullAsync(string name) + { + Check.NotNull(name, nameof(name)); + + return GetOrNullInternalAsync(name, null, null); + } + + 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 GetOrNullInternalAsync(string name, string providerName, string providerKey, bool fallback = true) + { + var setting = SettingDefinitionManager.Get(name); + var providers = Enumerable + .Reverse(Providers.Value); + + if (providerName != null) + { + providers = providers.SkipWhile(c => c.Name != providerName); + } + + if (!fallback || !setting.IsInherited) + { + providers = providers.TakeWhile(c => c.Name == providerName); + } + + foreach (var provider in providers) + { + var value = await provider.GetOrNullAsync(setting, providerKey); + if (value != null) + { + return value; + } + } + + return null; + } + + public virtual async Task> GetAllAsync() + { + var settingValues = new Dictionary(); + var settingDefinitions = SettingDefinitionManager.GetAll(); + + foreach (var provider in Providers.Value) + { + foreach (var setting in settingDefinitions) + { + var value = await provider.GetOrNullAsync(setting, null); + if (value != null) + { + settingValues[setting.Name] = new SettingValue(setting.Name, value); + } + } + } + + return settingValues.Values.ToList(); + } + + public virtual async Task> GetAllAsync(string providerName, string providerKey, bool fallback = true) + { + Check.NotNull(providerName, nameof(providerName)); + + var settingValues = new Dictionary(); + var settingDefinitions = SettingDefinitionManager.GetAll(); + var providers = Enumerable.Reverse(Providers.Value) + .SkipWhile(c => c.Name != providerName); + + if (!fallback) + { + providers = providers.TakeWhile(c => c.Name == providerName); + } + + var providerList = providers.Reverse().ToList(); + + if (providerList.Any()) + { + foreach (var setting in settingDefinitions) + { + if (setting.IsInherited) + { + foreach (var provider in providerList) + { + var value = await provider.GetOrNullAsync(setting, providerKey); + if (value != null) + { + settingValues[setting.Name] = new SettingValue(setting.Name, value); + } + } + } + else + { + settingValues[setting.Name] = new SettingValue( + setting.Name, + await providerList[0].GetOrNullAsync(setting, providerKey) + ); + } + } + } + + return settingValues.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 setting = SettingDefinitionManager.Get(name); + + var providers = Enumerable + .Reverse(Providers.Value) + .SkipWhile(p => p.Name != providerName) + .ToList(); + + if (!providers.Any()) + { + return; + } + + if (providers.Count > 1 && !forceToSet && setting.IsInherited && value != null) + { + //Clear the value if it's same as it's fallback value + var fallbackValue = await GetOrNullInternalAsync(name, providers[1].Name, providerKey); + if (fallbackValue == value) + { + value = null; + } + } + + providers = providers + .TakeWhile(p => p.Name == providerName) + .ToList(); //Getting list for case of there are more than one provider with same EntityType + + if (value == null) + { + foreach (var provider in providers) + { + await provider.ClearAsync(setting, providerKey); + } + } + else + { + foreach (var provider in providers) + { + await provider.SetAsync(setting, value, providerKey); + } + } + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingManagerSyncExtensions.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingManagerSyncExtensions.cs new file mode 100644 index 0000000000..f9fa53ea4c --- /dev/null +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingManagerSyncExtensions.cs @@ -0,0 +1,8 @@ +namespace Volo.Abp.Settings +{ + public static class SettingManagerSyncExtensions + { + //TODO: Add sync extension methods for all setting manager methods. + //TODO: Also add sync extension methods for all value provider extensions (like GlobalSettingManagerExtensions). + } +} diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingOptions.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingOptions.cs new file mode 100644 index 0000000000..66b4fa6c05 --- /dev/null +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingOptions.cs @@ -0,0 +1,21 @@ +using Volo.Abp.Collections; + +namespace Volo.Abp.Settings +{ + public class SettingOptions + { + public ITypeList DefinitionProviders { get; } + + public ITypeList ValueProviders { get; } + + public SettingOptions() + { + DefinitionProviders = new TypeList(); + ValueProviders = new TypeList + { + typeof(DefaultValueSettingValueProvider), + typeof(GlobalSettingValueProvider) + }; + } + } +} diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingValue.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingValue.cs new file mode 100644 index 0000000000..99b2de33f1 --- /dev/null +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingValue.cs @@ -0,0 +1,22 @@ +namespace Volo.Abp.Settings +{ + public class SettingValue : NameValue + { + /// + /// Creates a new . + /// + public SettingValue() + { + + } + + /// + /// Creates a new . + /// + public SettingValue(string name, string value) + { + Name = name; + Value = value; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingValueProvider.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingValueProvider.cs new file mode 100644 index 0000000000..dcd98945e4 --- /dev/null +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingValueProvider.cs @@ -0,0 +1,23 @@ +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Settings +{ + public abstract class SettingValueProvider : ISettingValueProvider, ISingletonDependency + { + public abstract string Name { get; } + + protected ISettingStore SettingStore { get; } + + protected SettingValueProvider(ISettingStore settingStore) + { + SettingStore = settingStore; + } + + public abstract Task GetOrNullAsync(SettingDefinition setting, string providerKey); + + public abstract Task SetAsync(SettingDefinition setting, string value, string providerKey); + + public abstract Task ClearAsync(SettingDefinition setting, string providerKey); + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo.Abp.csproj b/src/Volo.Abp/Volo.Abp.csproj index f241708cd0..168637c9e8 100644 --- a/src/Volo.Abp/Volo.Abp.csproj +++ b/src/Volo.Abp/Volo.Abp.csproj @@ -24,6 +24,7 @@ + diff --git a/src/Volo.Abp/Volo/Abp/AbpCommonModule.cs b/src/Volo.Abp/Volo/Abp/AbpCommonModule.cs index ca9b4d9c66..12ea477ed8 100644 --- a/src/Volo.Abp/Volo/Abp/AbpCommonModule.cs +++ b/src/Volo.Abp/Volo/Abp/AbpCommonModule.cs @@ -9,6 +9,7 @@ using Volo.Abp.MultiTenancy; using Volo.Abp.ObjectMapping; using Volo.Abp.Security; using Volo.Abp.Session; +using Volo.Abp.Settings; using Volo.Abp.Validation; using Volo.Abp.VirtualFileSystem; @@ -20,6 +21,7 @@ namespace Volo.Abp [DependsOn(typeof(AbpObjectMappingModule))] [DependsOn(typeof(AbpSecurityModule))] [DependsOn(typeof(AbpSessionModule))] + [DependsOn(typeof(AbpSettingsModule))] [DependsOn(typeof(AbpDataModule))] [DependsOn(typeof(AbpJsonModule))] [DependsOn(typeof(AbpVirtualFileSystemModule))] diff --git a/test/Volo.Abp.MultiTenancy.Tests/Volo.Abp.MultiTenancy.Tests.csproj b/test/Volo.Abp.MultiTenancy.Tests/Volo.Abp.MultiTenancy.Tests.csproj index a76a44b67d..8967428388 100644 --- a/test/Volo.Abp.MultiTenancy.Tests/Volo.Abp.MultiTenancy.Tests.csproj +++ b/test/Volo.Abp.MultiTenancy.Tests/Volo.Abp.MultiTenancy.Tests.csproj @@ -19,9 +19,5 @@ - - - - diff --git a/test/Volo.Abp.Settings.Tests/Volo.Abp.Settings.Tests.csproj b/test/Volo.Abp.Settings.Tests/Volo.Abp.Settings.Tests.csproj new file mode 100644 index 0000000000..df86c0a460 --- /dev/null +++ b/test/Volo.Abp.Settings.Tests/Volo.Abp.Settings.Tests.csproj @@ -0,0 +1,25 @@ + + + + netcoreapp2.0 + Volo.Abp.MultiTenancy.Tests + Volo.Abp.MultiTenancy.Tests + true + false + false + false + + + + + + + + + + + + + + + diff --git a/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/AbpIdentityApplicationTestBase.cs b/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/AbpIdentityApplicationTestBase.cs new file mode 100644 index 0000000000..d30568aca4 --- /dev/null +++ b/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/AbpIdentityApplicationTestBase.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Volo.Abp.Session; +using Volo.Abp.Settings.EntityFrameworkCore; +using Volo.Abp.TestBase; + +namespace Volo.Abp.Settings +{ + public class AbpSettingsTestBase : AbpIntegratedTest + { + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + + protected virtual void UsingDbContext(Action action) + { + using (var dbContext = GetRequiredService()) + { + action.Invoke(dbContext); + } + } + + protected virtual T UsingDbContext(Func action) + { + using (var dbContext = GetRequiredService()) + { + return action.Invoke(dbContext); + } + } + + protected List GetSettingsFromDbContext(string entityType, string entityId, string name) + { + return UsingDbContext(context => + context.Settings.Where( + s => + s.ProviderName == UserSettingValueProvider.ProviderName && + s.ProviderKey == AbpIdentityTestDataBuilder.User1Id.ToString() && + s.Name == "MySetting2" + ).ToList() + ); + } + } +} diff --git a/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/AbpIdentityApplicationTestModule.cs b/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/AbpIdentityApplicationTestModule.cs new file mode 100644 index 0000000000..a231bab412 --- /dev/null +++ b/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/AbpIdentityApplicationTestModule.cs @@ -0,0 +1,61 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Autofac; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Modularity; +using Volo.Abp.Session; +using Volo.Abp.Settings.EntityFrameworkCore; +using Volo.Abp.Uow; + +namespace Volo.Abp.Settings +{ + [DependsOn( + typeof(AbpSettingsEntityFrameworkCoreModule), + typeof(AbpSessionModule), + typeof(AbpAutofacModule))] + public class AbpSettingsTestModule : AbpModule + { + public override void ConfigureServices(IServiceCollection services) + { + services.AddAssemblyOf(); + + services.AddEntityFrameworkInMemoryDatabase(); + + var databaseName = Guid.NewGuid().ToString(); + + services.Configure(options => + { + options.Configure(context => + { + context.DbContextOptions.UseInMemoryDatabase(databaseName); + }); + }); + + services.Configure(options => + { + options.TransactionBehavior = UnitOfWorkTransactionBehavior.Disabled; //EF in-memory database does not support transactions + }); + + services.Configure(options => + { + options.DefinitionProviders.Add(); + }); + } + + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + SeedTestData(context); + } + + private static void SeedTestData(ApplicationInitializationContext context) + { + using (var scope = context.ServiceProvider.CreateScope()) + { + scope.ServiceProvider + .GetRequiredService() + .Build(); + } + } + } +} diff --git a/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/AbpIdentityTestDataBuilder.cs b/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/AbpIdentityTestDataBuilder.cs new file mode 100644 index 0000000000..73488df1b4 --- /dev/null +++ b/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/AbpIdentityTestDataBuilder.cs @@ -0,0 +1,34 @@ +using System; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Guids; +using Volo.Abp.Session; + +namespace Volo.Abp.Settings +{ + public class AbpIdentityTestDataBuilder : ITransientDependency + { + public static Guid User1Id = Guid.NewGuid(); + public static Guid User2Id = Guid.NewGuid(); + + private readonly ISettingRepository _settingRepository; + private readonly IGuidGenerator _guidGenerator; + + public AbpIdentityTestDataBuilder(ISettingRepository settingRepository, IGuidGenerator guidGenerator) + { + _settingRepository = settingRepository; + _guidGenerator = guidGenerator; + } + + public void Build() + { + _settingRepository.InsertAsync(new Setting(_guidGenerator.Create(), "MySetting1", "42", GlobalSettingValueProvider.ProviderName)); + + _settingRepository.InsertAsync(new Setting(_guidGenerator.Create(), "MySetting2", "default-store-value", GlobalSettingValueProvider.ProviderName)); + _settingRepository.InsertAsync(new Setting(_guidGenerator.Create(), "MySetting2", "user1-store-value", UserSettingValueProvider.ProviderName, User1Id.ToString())); + _settingRepository.InsertAsync(new Setting(_guidGenerator.Create(), "MySetting2", "user2-store-value", UserSettingValueProvider.ProviderName, User2Id.ToString())); + + _settingRepository.InsertAsync(new Setting(_guidGenerator.Create(), "MySettingWithoutInherit", "default-store-value", GlobalSettingValueProvider.ProviderName)); + _settingRepository.InsertAsync(new Setting(_guidGenerator.Create(), "MySettingWithoutInherit", "user1-store-value", UserSettingValueProvider.ProviderName, User1Id.ToString())); + } + } +} \ No newline at end of file diff --git a/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/SettingManager_Basic_Tests.cs b/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/SettingManager_Basic_Tests.cs new file mode 100644 index 0000000000..7ac6205346 --- /dev/null +++ b/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/SettingManager_Basic_Tests.cs @@ -0,0 +1,55 @@ +using System.Threading.Tasks; +using Shouldly; +using Xunit; + +namespace Volo.Abp.Settings +{ + public class SettingManager_Basic_Tests : AbpSettingsTestBase + { + private readonly ISettingManager _settingManager; + + public SettingManager_Basic_Tests() + { + _settingManager = GetRequiredService(); + } + + [Fact] + public async Task Should_Throw_Exception_When_Try_To_Get_An_Undefined_Setting() + { + await Assert.ThrowsAsync( + async () => await _settingManager.GetOrNullAsync("UndefinedSetting") + ); + } + + [Fact] + public async Task Should_Get_Default_Value_If_Not_Set_In_Store() + { + var value = await _settingManager.GetOrNullAsync("SettingNotSetInStore"); + value.ShouldBe("default-value"); + } + + [Fact] + public async Task Should_Get_Base_Store_Value() + { + (await _settingManager.GetOrNullAsync("MySetting1")).ShouldBe("42"); + } + + [Fact] + public async Task Should_Get_All_Base_Store_Values() + { + var settingValues = await _settingManager.GetAllAsync(); + settingValues.ShouldContain(sv => sv.Name == "MySetting1" && sv.Value == "42"); + settingValues.ShouldContain(sv => sv.Name == "MySetting2" && sv.Value == "default-store-value"); + settingValues.ShouldContain(sv => sv.Name == "SettingNotSetInStore" && sv.Value == "default-value"); + } + + [Fact] + public async Task Should_Set_Global_Value() + { + await _settingManager.SetGlobalAsync("MySetting1", "43"); + + (await _settingManager.GetOrNullGlobalAsync("MySetting1")).ShouldBe("43"); + (await _settingManager.GetOrNullAsync("MySetting1")).ShouldBe("43"); + } + } +} diff --git a/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/SettingManager_User_Tests.cs b/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/SettingManager_User_Tests.cs new file mode 100644 index 0000000000..767a10bd94 --- /dev/null +++ b/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/SettingManager_User_Tests.cs @@ -0,0 +1,185 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using NSubstitute; +using Shouldly; +using Volo.Abp.Session; +using Xunit; + +namespace Volo.Abp.Settings +{ + public class SettingManager_User_Tests: AbpSettingsTestBase + { + private Guid _currentUserId; + + private readonly ISettingManager _settingManager; + + public SettingManager_User_Tests() + { + _settingManager = GetRequiredService(); + } + + protected override void AfterAddApplication(IServiceCollection services) + { + var currentUser = Substitute.For(); + currentUser.Id.Returns(ci => _currentUserId); + services.AddSingleton(currentUser); + } + + [Fact] + public async Task Should_Get_From_Store_For_Given_User() + { + (await _settingManager.GetOrNullForUserAsync("MySetting2", AbpIdentityTestDataBuilder.User1Id)).ShouldBe("user1-store-value"); + (await _settingManager.GetOrNullForUserAsync("MySetting2", AbpIdentityTestDataBuilder.User2Id)).ShouldBe("user2-store-value"); + } + + [Fact] + public async Task Should_Fallback_To_Default_Store_Value_When_No_Value_For_Given_User() + { + (await _settingManager.GetOrNullForUserAsync("MySetting2", Guid.NewGuid())).ShouldBe("default-store-value"); + } + + [Fact] + public async Task Should_Not_Fallback_To_Default_Store_Value_When_No_Value_For_Given_User_But_Specified_Fallback_As_False() + { + (await _settingManager.GetOrNullForUserAsync("MySetting2", Guid.NewGuid(), fallback: false)).ShouldBeNull(); + } + + [Fact] + public async Task Should_Get_From_Store_For_Current_User() + { + _currentUserId = AbpIdentityTestDataBuilder.User1Id; + (await _settingManager.GetOrNullAsync("MySetting2")).ShouldBe("user1-store-value"); + + _currentUserId = AbpIdentityTestDataBuilder.User2Id; + (await _settingManager.GetOrNullAsync("MySetting2")).ShouldBe("user2-store-value"); + } + + [Fact] + public async Task Should_Fallback_To_Default_Store_Value_When_No_Value_For_Current_User() + { + _currentUserId = Guid.NewGuid(); + (await _settingManager.GetOrNullAsync("MySetting2")).ShouldBe("default-store-value"); + } + + [Fact] + public async Task Should_Get_From_Store_For_Current_User_With_GetOrNullForCurrentUserAsync() + { + _currentUserId = AbpIdentityTestDataBuilder.User1Id; + (await _settingManager.GetOrNullForCurrentUserAsync("MySetting2")).ShouldBe("user1-store-value"); + + _currentUserId = AbpIdentityTestDataBuilder.User2Id; + (await _settingManager.GetOrNullForCurrentUserAsync("MySetting2")).ShouldBe("user2-store-value"); + } + + [Fact] + public async Task Should_Fallback_To_Default_Store_Value_When_No_Value_For_Current_User_With_GetOrNullForCurrentUserAsync() + { + _currentUserId = Guid.NewGuid(); + (await _settingManager.GetOrNullAsync("MySetting2")).ShouldBe("default-store-value"); + } + + [Fact] + public async Task Should_Not_Fallback_To_Default_Store_Value_When_No_Value_For_Current_User_But_Specified_Fallback_As_False() + { + _currentUserId = Guid.NewGuid(); + (await _settingManager.GetOrNullForCurrentUserAsync("MySetting2", fallback: false)).ShouldBeNull(); + } + + [Fact] + public async Task Should_Get_All_From_Store_For_Given_User() + { + var settingValues = await _settingManager.GetAllForUserAsync(AbpIdentityTestDataBuilder.User1Id); + settingValues.ShouldContain(sv => sv.Name == "MySetting1" && sv.Value == "42"); + settingValues.ShouldContain(sv => sv.Name == "MySetting2" && sv.Value == "user1-store-value"); + settingValues.ShouldContain(sv => sv.Name == "SettingNotSetInStore" && sv.Value == "default-value"); + } + + [Fact] + public async Task Should_Get_All_From_Store_For_Given_User_Without_Fallback() + { + var settingValues = await _settingManager.GetAllForUserAsync(AbpIdentityTestDataBuilder.User1Id, fallback: false); + settingValues.ShouldContain(sv => sv.Name == "MySetting2" && sv.Value == "user1-store-value"); + settingValues.ShouldContain(sv => sv.Name == "MySettingWithoutInherit" && sv.Value == "user1-store-value"); + settingValues.ShouldNotContain(sv => sv.Name == "MySetting1"); + } + + [Fact] + public async Task Should_Delete_Setting_Record_When_Set_To_Null() + { + await _settingManager.SetForUserAsync(AbpIdentityTestDataBuilder.User1Id, "MySetting2", null); + + GetSettingsFromDbContext( + UserSettingValueProvider.ProviderName, + AbpIdentityTestDataBuilder.User1Id.ToString(), + "MySetting2" + ).Count.ShouldBe(0); + } + + [Fact] + public async Task Should_Change_User_Setting() + { + (await _settingManager.GetOrNullForUserAsync("MySetting2", AbpIdentityTestDataBuilder.User1Id)) + .ShouldBe("user1-store-value"); + + await _settingManager.SetForUserAsync(AbpIdentityTestDataBuilder.User1Id, "MySetting2", "user1-new-store-value"); + + (await _settingManager.GetOrNullForUserAsync("MySetting2", AbpIdentityTestDataBuilder.User1Id)) + .ShouldBe("user1-new-store-value"); + + GetSettingsFromDbContext( + UserSettingValueProvider.ProviderName, + AbpIdentityTestDataBuilder.User1Id.ToString(), + "MySetting2" + ).Single().Value.ShouldBe("user1-new-store-value"); + } + + [Fact] + public async Task Should_Delete_Setting_Record_When_Set_To_Fallback_Value() + { + await _settingManager.SetForUserAsync( + AbpIdentityTestDataBuilder.User1Id, + "MySetting2", + "default-store-value" + ); + + GetSettingsFromDbContext( + UserSettingValueProvider.ProviderName, + AbpIdentityTestDataBuilder.User1Id.ToString(), + "MySetting2" + ).Count.ShouldBe(0); + + (await _settingManager.GetOrNullForUserAsync("MySetting2", AbpIdentityTestDataBuilder.User1Id)) + .ShouldBe("default-store-value"); + } + + [Fact] + public async Task Should_Not_Delete_Setting_Record_When_Set_To_Fallback_Value_If_Forced() + { + await _settingManager.SetForUserAsync( + AbpIdentityTestDataBuilder.User1Id, + "MySetting2", + "default-store-value", + forceToSet: true + ); + + GetSettingsFromDbContext( + UserSettingValueProvider.ProviderName, + AbpIdentityTestDataBuilder.User1Id.ToString(), + "MySetting2" + ).Single().Value.ShouldBe("default-store-value"); + + (await _settingManager.GetOrNullForUserAsync("MySetting2", AbpIdentityTestDataBuilder.User1Id)) + .ShouldBe("default-store-value"); + } + + [Fact] + public async Task Should_Get_For_Given_User_For_Non_Inherited_Setting() + { + (await _settingManager.GetOrNullForUserAsync("MySettingWithoutInherit", AbpIdentityTestDataBuilder.User1Id)).ShouldBe("user1-store-value"); + (await _settingManager.GetOrNullForUserAsync("MySettingWithoutInherit", AbpIdentityTestDataBuilder.User2Id)).ShouldBeNull(); //Does not inherit! + (await _settingManager.GetOrNullGlobalAsync("MySettingWithoutInherit")).ShouldBe("default-store-value"); + } + } +} \ No newline at end of file diff --git a/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/TestSettingDefinitionProvider.cs b/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/TestSettingDefinitionProvider.cs new file mode 100644 index 0000000000..761b383448 --- /dev/null +++ b/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/TestSettingDefinitionProvider.cs @@ -0,0 +1,13 @@ +namespace Volo.Abp.Settings +{ + public class TestSettingDefinitionProvider : SettingDefinitionProvider + { + public override void Define(ISettingDefinitionContext context) + { + context.Add(new SettingDefinition("MySetting1")); + context.Add(new SettingDefinition("MySetting2")); + context.Add(new SettingDefinition("MySettingWithoutInherit", isInherited: false)); + context.Add(new SettingDefinition("SettingNotSetInStore", defaultValue: "default-value")); + } + } +} \ No newline at end of file