diff --git a/abp_io/src/Volo.AbpWebSite.Web/AbpWebSiteWebModule.cs b/abp_io/src/Volo.AbpWebSite.Web/AbpWebSiteWebModule.cs index e1c30e0a26..8031142779 100644 --- a/abp_io/src/Volo.AbpWebSite.Web/AbpWebSiteWebModule.cs +++ b/abp_io/src/Volo.AbpWebSite.Web/AbpWebSiteWebModule.cs @@ -69,7 +69,6 @@ namespace Volo.AbpWebSite Configure(options => { options.FileUploadLocalFolder = Path.Combine(hostingEnvironment.WebRootPath, "files"); - options.FileUploadUrlRoot = "/files/"; }); } diff --git a/abp_io/src/Volo.AbpWebSite.Web/Templates/MvcApplicationTemplate.cs b/abp_io/src/Volo.AbpWebSite.Web/Templates/MvcApplicationTemplate.cs index 6868720b20..63e81a7b25 100644 --- a/abp_io/src/Volo.AbpWebSite.Web/Templates/MvcApplicationTemplate.cs +++ b/abp_io/src/Volo.AbpWebSite.Web/Templates/MvcApplicationTemplate.cs @@ -37,6 +37,7 @@ namespace Volo.AbpWebSite.Templates if (context.Request.DatabaseProvider != DatabaseProvider.EntityFrameworkCore) { steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.EntityFrameworkCore")); + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations")); steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Application.Tests", projectFolderPath: "test/MyCompanyName.MyProjectName.Application.Tests")); steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Web.Tests", projectFolderPath: "test/MyCompanyName.MyProjectName.Web.Tests")); } diff --git a/abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/ProjectBuildContextExtensions.cs b/abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/ProjectBuildContextExtensions.cs new file mode 100644 index 0000000000..2aa568ba20 --- /dev/null +++ b/abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/ProjectBuildContextExtensions.cs @@ -0,0 +1,20 @@ +using System; +using System.Linq; +using Volo.Utils.SolutionTemplating.Files; + +namespace Volo.Utils.SolutionTemplating.Building +{ + public static class ProjectBuildContextExtensions + { + public static FileEntry GetFile(this ProjectBuildContext context, string filePath) + { + var file = context.Files.FirstOrDefault(f => f.Name == filePath); + if (file == null) + { + throw new ApplicationException("Could not find file: " + filePath); + } + + return file; + } + } +} \ No newline at end of file diff --git a/abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/ProjectBuildPipelineStep.cs b/abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/ProjectBuildPipelineStep.cs index 7a94ca6ee1..ed6f613dbf 100644 --- a/abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/ProjectBuildPipelineStep.cs +++ b/abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/ProjectBuildPipelineStep.cs @@ -1,22 +1,7 @@ -using System; -using System.Linq; -using Volo.Utils.SolutionTemplating.Files; - -namespace Volo.Utils.SolutionTemplating.Building +namespace Volo.Utils.SolutionTemplating.Building { public abstract class ProjectBuildPipelineStep { public abstract void Execute(ProjectBuildContext context); - - protected FileEntry GetFile(ProjectBuildContext context, string filePath) - { - var file = context.Files.FirstOrDefault(f => f.Name == filePath); - if (file == null) - { - throw new ApplicationException("Could not find file: " + filePath); - } - - return file; - } } } \ No newline at end of file diff --git a/abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/Steps/RemoveProjectFromSolutionStep.cs b/abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/Steps/RemoveProjectFromSolutionStep.cs index 41e9b2cd26..eb56699547 100644 --- a/abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/Steps/RemoveProjectFromSolutionStep.cs +++ b/abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/Steps/RemoveProjectFromSolutionStep.cs @@ -23,7 +23,7 @@ namespace Volo.Utils.SolutionTemplating.Building.Steps public override void Execute(ProjectBuildContext context) { context.Files.RemoveAll(file => file.Name.StartsWith(_projectFolderPath)); - var solutionFile = GetFile(context, _solutionFilePath); + var solutionFile = context.GetFile(_solutionFilePath); solutionFile.NormalizeLineEndings(); solutionFile.SetLines(RemoveProject(solutionFile.GetLines().ToList())); } diff --git a/abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/Steps/SwitchEntityFrameworkCoreToMongoDbStep.cs b/abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/Steps/SwitchEntityFrameworkCoreToMongoDbStep.cs index cf7404eccf..0901d0250b 100644 --- a/abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/Steps/SwitchEntityFrameworkCoreToMongoDbStep.cs +++ b/abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/Steps/SwitchEntityFrameworkCoreToMongoDbStep.cs @@ -14,7 +14,7 @@ namespace Volo.Utils.SolutionTemplating.Building.Steps private void ChangeProjectReference(ProjectBuildContext context) { - var file = GetFile(context, "/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj"); + var file = context.GetFile("/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj"); file.NormalizeLineEndings(); @@ -29,12 +29,12 @@ namespace Volo.Utils.SolutionTemplating.Building.Steps } } - throw new ApplicationException("Could not find the 'Default' connection string in appsettings.json file!"); + throw new ApplicationException("Could not find the EntityFrameworkCore reference in the MyCompanyName.MyProjectName.Web.csproj!"); } private void ChangeWebModuleUsage(ProjectBuildContext context) { - var file = GetFile(context, "/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs"); + var file = context.GetFile("/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs"); file.NormalizeLineEndings(); @@ -59,7 +59,7 @@ namespace Volo.Utils.SolutionTemplating.Building.Steps private void ChangeConnectionString(ProjectBuildContext context) { - var file = GetFile(context, "/src/MyCompanyName.MyProjectName.Web/appsettings.json"); + var file = context.GetFile("/src/MyCompanyName.MyProjectName.Web/appsettings.json"); file.NormalizeLineEndings(); diff --git a/docs/en/Dependency-Injection.md b/docs/en/Dependency-Injection.md index d1ca14950c..5d8ddbf94e 100644 --- a/docs/en/Dependency-Injection.md +++ b/docs/en/Dependency-Injection.md @@ -170,7 +170,7 @@ public class BlogModule : AbpModule There are three common ways of using a service that has already been registered. -### Contructor Injection +### Constructor Injection This is the most common way of injecting a service into a class. For example: diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index f99d6cebbc..1615d66e14 100644 --- a/framework/Volo.Abp.sln +++ b/framework/Volo.Abp.sln @@ -5,23 +5,8 @@ VisualStudioVersion = 15.0.27130.2036 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CBCC288A-53C3-402F-99F7-E468738560F5}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{447C8A77-E5F0-4538-8687-7383196D04EA}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docker-files", "docker-files", "{0BEA55D6-E0B8-40DD-A256-B34C4DD990A5}" - ProjectSection(SolutionItems) = preProject - ..\docker\docker-compose.yml = ..\docker\docker-compose.yml - ..\docker\down.ps1 = ..\docker\down.ps1 - ..\docker\haproxy.cfg = ..\docker\haproxy.cfg - ..\docker\up.ps1 = ..\docker\up.ps1 - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{6D893E48-0739-401A-9AD1-286BCB6E5517}" - ProjectSection(SolutionItems) = preProject - ..\build\build.ps1 = ..\build\build.ps1 - EndProjectSection -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AbpTestBase", "test\AbpTestBase\AbpTestBase.csproj", "{1020F5FD-6A97-40C2-AFCA-EBDF641DF111}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore", "src\Volo.Abp.AspNetCore\Volo.Abp.AspNetCore.csproj", "{02BE03BA-3411-448C-AB61-CB36407CC49A}" @@ -226,7 +211,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.Mvc.Cli EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.Mvc.Contracts", "src\Volo.Abp.AspNetCore.Mvc.Contracts\Volo.Abp.AspNetCore.Mvc.Contracts.csproj", "{88F6D091-CA16-4B71-9499-8D5B8FA2E712}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Features", "src\Volo.Abp.Features\Volo.Abp.Features.csproj", "{01E3D389-8872-4EB1-9D3D-13B6ED54DE0E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Features", "src\Volo.Abp.Features\Volo.Abp.Features.csproj", "{01E3D389-8872-4EB1-9D3D-13B6ED54DE0E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Features.Tests", "test\Volo.Abp.Features.Tests\Volo.Abp.Features.Tests.csproj", "{575BEFA1-19C2-49B1-8D31-B5D4472328DE}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -646,13 +633,15 @@ Global {01E3D389-8872-4EB1-9D3D-13B6ED54DE0E}.Debug|Any CPU.Build.0 = Debug|Any CPU {01E3D389-8872-4EB1-9D3D-13B6ED54DE0E}.Release|Any CPU.ActiveCfg = Release|Any CPU {01E3D389-8872-4EB1-9D3D-13B6ED54DE0E}.Release|Any CPU.Build.0 = Release|Any CPU + {575BEFA1-19C2-49B1-8D31-B5D4472328DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {575BEFA1-19C2-49B1-8D31-B5D4472328DE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {575BEFA1-19C2-49B1-8D31-B5D4472328DE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {575BEFA1-19C2-49B1-8D31-B5D4472328DE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {0BEA55D6-E0B8-40DD-A256-B34C4DD990A5} = {CBCC288A-53C3-402F-99F7-E468738560F5} - {6D893E48-0739-401A-9AD1-286BCB6E5517} = {CBCC288A-53C3-402F-99F7-E468738560F5} {1020F5FD-6A97-40C2-AFCA-EBDF641DF111} = {447C8A77-E5F0-4538-8687-7383196D04EA} {02BE03BA-3411-448C-AB61-CB36407CC49A} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {B1D860BB-6EC6-4BAE-ADAA-C2AEC2FFB510} = {447C8A77-E5F0-4538-8687-7383196D04EA} @@ -756,6 +745,7 @@ Global {E803DDB8-81EA-454B-9A66-9C2941100B67} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {88F6D091-CA16-4B71-9499-8D5B8FA2E712} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {01E3D389-8872-4EB1-9D3D-13B6ED54DE0E} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {575BEFA1-19C2-49B1-8D31-B5D4472328DE} = {447C8A77-E5F0-4538-8687-7383196D04EA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/RemoteFeatureChecker.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/RemoteFeatureChecker.cs new file mode 100644 index 0000000000..0643620ec9 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/RemoteFeatureChecker.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.Features; + +namespace Volo.Abp.AspNetCore.Mvc.Client +{ + public class RemoteFeatureChecker : FeatureCheckerBase + { + protected ICachedApplicationConfigurationClient ConfigurationClient { get; } + + public RemoteFeatureChecker(ICachedApplicationConfigurationClient configurationClient) + { + ConfigurationClient = configurationClient; + } + + public override async Task GetOrNullAsync(string name) + { + var configuration = await ConfigurationClient.GetAsync(); + return configuration.Features.Values.GetOrDefault(name); + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs index 6f41399c29..44e9076751 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs @@ -12,5 +12,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations public ApplicationSettingConfigurationDto Setting { get; set; } public CurrentUserDto CurrentUser { get; set; } + + public ApplicationFeatureConfigurationDto Features { get; set; } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationFeatureConfigurationDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationFeatureConfigurationDto.cs new file mode 100644 index 0000000000..4972d2242d --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationFeatureConfigurationDto.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; + +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations +{ + [Serializable] + public class ApplicationFeatureConfigurationDto + { + public Dictionary Values { get; set; } + + public ApplicationFeatureConfigurationDto() + { + Values = new Dictionary(); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/AbpAspNetCoreMvcUiBootstrapModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/AbpAspNetCoreMvcUiBootstrapModule.cs index 44a00808f8..d038d64789 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/AbpAspNetCoreMvcUiBootstrapModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/AbpAspNetCoreMvcUiBootstrapModule.cs @@ -1,5 +1,4 @@ -using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.Modularity; +using Volo.Abp.Modularity; using Volo.Abp.VirtualFileSystem; namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/AbpAspNetCoreMvcUiThemeSharedModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/AbpAspNetCoreMvcUiThemeSharedModule.cs index 57a03d0d75..fb8619b60a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/AbpAspNetCoreMvcUiThemeSharedModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/AbpAspNetCoreMvcUiThemeSharedModule.cs @@ -1,5 +1,4 @@ -using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap; using Volo.Abp.AspNetCore.Mvc.UI.Bundling; using Volo.Abp.AspNetCore.Mvc.UI.Packages; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Bundling; diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/Layout/ILayoutManager.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/Layout/IPageLayout.cs similarity index 100% rename from framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/Layout/ILayoutManager.cs rename to framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/Layout/IPageLayout.cs diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Http/AbpFormFileExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Http/AbpFormFileExtensions.cs new file mode 100644 index 0000000000..c808250a3e --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Http/AbpFormFileExtensions.cs @@ -0,0 +1,15 @@ +using System.IO; + +namespace Microsoft.AspNetCore.Http +{ + public static class AbpFormFileExtensions + { + public static byte[] GetAllBytes(this IFormFile file) + { + using (var stream = file.OpenReadStream()) + { + return stream.GetAllBytes(); + } + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpController.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpController.cs index e9e535c4be..a5d206cf55 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpController.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpController.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Volo.Abp.Aspects; using Volo.Abp.AspNetCore.Mvc.Validation; +using Volo.Abp.Features; using Volo.Abp.Guids; using Volo.Abp.MultiTenancy; using Volo.Abp.ObjectMapping; @@ -28,12 +30,16 @@ namespace Volo.Abp.AspNetCore.Mvc public ICurrentTenant CurrentTenant { get; set; } + public IAuthorizationService AuthorizationService { get; set; } + protected IUnitOfWork CurrentUnitOfWork => UnitOfWorkManager?.Current; public IClock Clock { get; set; } public IModelStateValidator ModelValidator { get; set; } - + + public IFeatureChecker FeatureChecker { get; set; } + public List AppliedCrossCuttingConcerns { get; } = new List(); protected virtual void ValidateModel() diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs index 4de46b2f0b..59c7de8840 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AspNetCore.Mvc.Auditing; using Volo.Abp.AspNetCore.Mvc.Conventions; using Volo.Abp.AspNetCore.Mvc.ExceptionHandling; +using Volo.Abp.AspNetCore.Mvc.Features; using Volo.Abp.AspNetCore.Mvc.Uow; using Volo.Abp.AspNetCore.Mvc.Validation; @@ -26,6 +27,7 @@ namespace Volo.Abp.AspNetCore.Mvc private static void AddFilters(MvcOptions options) { options.Filters.AddService(typeof(AbpAuditActionFilter)); + options.Filters.AddService(typeof(AbpFeatureActionFilter)); options.Filters.AddService(typeof(AbpValidationActionFilter)); options.Filters.AddService(typeof(AbpUowActionFilter)); options.Filters.AddService(typeof(AbpExceptionFilter)); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs index 086fbf1d72..70eb04c707 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs @@ -4,10 +4,10 @@ using Microsoft.Extensions.Localization; using Microsoft.Extensions.Options; using System; using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using Volo.Abp.Application.Services; using Volo.Abp.Authorization; +using Volo.Abp.Features; using Volo.Abp.Localization; using Volo.Abp.Settings; using Volo.Abp.Users; @@ -23,6 +23,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations private readonly ICurrentUser _currentUser; private readonly ISettingProvider _settingProvider; private readonly ISettingDefinitionManager _settingDefinitionManager; + private readonly IFeatureDefinitionManager _featureDefinitionManager; public AbpApplicationConfigurationAppService( IOptions localizationOptions, @@ -31,7 +32,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations IAuthorizationService authorizationService, ICurrentUser currentUser, ISettingProvider settingProvider, - SettingDefinitionManager settingDefinitionManager) + SettingDefinitionManager settingDefinitionManager, + IFeatureDefinitionManager featureDefinitionManager) { _serviceProvider = serviceProvider; _abpAuthorizationPolicyProvider = abpAuthorizationPolicyProvider; @@ -39,16 +41,18 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations _currentUser = currentUser; _settingProvider = settingProvider; _settingDefinitionManager = settingDefinitionManager; + _featureDefinitionManager = featureDefinitionManager; _localizationOptions = localizationOptions.Value; } - public async Task GetAsync() + public virtual async Task GetAsync() { //TODO: Optimize & cache..? return new ApplicationConfigurationDto { Auth = await GetAuthConfigAsync(), + Features = await GetFeaturesConfigAsync(), Localization = GetLocalizationConfig(), CurrentUser = GetCurrentUser(), Setting = await GetSettingConfigAsync() @@ -126,5 +130,22 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations return result; } + + protected virtual async Task GetFeaturesConfigAsync() + { + var result = new ApplicationFeatureConfigurationDto(); + + foreach (var featureDefinition in _featureDefinitionManager.GetAll()) + { + if (!featureDefinition.IsVisibleToClients) + { + continue; + } + + result.Values[featureDefinition.Name] = await FeatureChecker.GetOrNullAsync(featureDefinition.Name); + } + + return result; + } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Features/AbpFeatureActionFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Features/AbpFeatureActionFilter.cs new file mode 100644 index 0000000000..577acda339 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Features/AbpFeatureActionFilter.cs @@ -0,0 +1,42 @@ +using Microsoft.AspNetCore.Mvc.Filters; +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Abstractions; +using Volo.Abp.Aspects; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Features; + +namespace Volo.Abp.AspNetCore.Mvc.Features +{ + public class AbpFeatureActionFilter : IAsyncActionFilter, ITransientDependency + { + private readonly IMethodInvocationFeatureCheckerService _methodInvocationAuthorizationService; + + public AbpFeatureActionFilter(IMethodInvocationFeatureCheckerService methodInvocationAuthorizationService) + { + _methodInvocationAuthorizationService = methodInvocationAuthorizationService; + } + + public async Task OnActionExecutionAsync( + ActionExecutingContext context, + ActionExecutionDelegate next) + { + if (!context.ActionDescriptor.IsControllerAction()) + { + await next(); + return; + } + + var methodInfo = context.ActionDescriptor.GetMethodInfo(); + + using (AbpCrossCuttingConcerns.Applying(context.Controller, AbpCrossCuttingConcerns.FeatureChecking)) + { + await _methodInvocationAuthorizationService.CheckAsync( + new MethodInvocationFeatureCheckerContext(methodInfo) + ); + + await next(); + } + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowActionFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowActionFilter.cs index 5a330eb7a9..0a03a0dc67 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowActionFilter.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowActionFilter.cs @@ -12,7 +12,6 @@ namespace Volo.Abp.AspNetCore.Mvc.Uow { public class AbpUowActionFilter : IAsyncActionFilter, ITransientDependency { - private readonly IUnitOfWorkManager _unitOfWorkManager; private readonly UnitOfWorkDefaultOptions _defaultOptions; diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationModule.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationModule.cs index 57472d7cf1..880673f86b 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationModule.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationModule.cs @@ -1,4 +1,6 @@ -using Microsoft.AspNetCore.Authorization; +using System; +using System.Collections.Generic; +using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Authorization.Permissions; using Volo.Abp.Localization; @@ -16,6 +18,7 @@ namespace Volo.Abp.Authorization public override void PreConfigureServices(ServiceConfigurationContext context) { context.Services.OnRegistred(AuthorizationInterceptorRegistrar.RegisterIfNeeded); + AutoAddDefinitionProviders(context.Services); } public override void ConfigureServices(ServiceConfigurationContext context) @@ -31,5 +34,23 @@ namespace Volo.Abp.Authorization options.ValueProviders.Add(); }); } + + private static void AutoAddDefinitionProviders(IServiceCollection services) + { + var definitionProviders = new List(); + + services.OnRegistred(context => + { + if (typeof(IPermissionDefinitionProvider).IsAssignableFrom(context.ImplementationType)) + { + definitionProviders.Add(context.ImplementationType); + } + }); + + services.Configure(options => + { + options.DefinitionProviders.AddIfNotContains(definitionProviders); + }); + } } } diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AuthorizationInterceptorRegistrar.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AuthorizationInterceptorRegistrar.cs index 76a12b6dfe..69a285ddc8 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AuthorizationInterceptorRegistrar.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AuthorizationInterceptorRegistrar.cs @@ -1,4 +1,8 @@ -using Volo.Abp.DependencyInjection; +using System; +using System.Linq; +using System.Reflection; +using Microsoft.AspNetCore.Authorization; +using Volo.Abp.DependencyInjection; namespace Volo.Abp.Authorization { @@ -6,10 +10,28 @@ namespace Volo.Abp.Authorization { public static void RegisterIfNeeded(IOnServiceRegistredContext context) { - if (typeof(IAuthorizationEnabled).IsAssignableFrom(context.ImplementationType)) + if (ShouldIntercept(context.ImplementationType)) { context.Interceptors.TryAdd(); } } + + private static bool ShouldIntercept(Type type) + { + return type.IsDefined(typeof(AuthorizeAttribute), true) || + AnyMethodHasAuthorizeAttribute(type); + } + + private static bool AnyMethodHasAuthorizeAttribute(Type implementationType) + { + return implementationType + .GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + .Any(HasAuthorizeAttribute); + } + + private static bool HasAuthorizeAttribute(MemberInfo methodInfo) + { + return methodInfo.IsDefined(typeof(AuthorizeAttribute), true); + } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/IAuthorizationEnabled.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/IAuthorizationEnabled.cs deleted file mode 100644 index 8563a98a8c..0000000000 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/IAuthorizationEnabled.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Volo.Abp.Authorization -{ - public interface IAuthorizationEnabled - { - - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs index 1cca959ebe..56baba8ce4 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs @@ -1,4 +1,6 @@ -using System.Linq; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Volo.Abp.Clients; @@ -30,8 +32,7 @@ namespace Volo.Abp.Authorization return; } - var authorizationAttributes = GetAuthorizationDataAttributes(context); - foreach (var authorizationAttribute in authorizationAttributes) + foreach (var authorizationAttribute in GetAuthorizationDataAttributes(context.Method)) { await CheckAsync(authorizationAttribute); } @@ -42,17 +43,23 @@ namespace Volo.Abp.Authorization return context.Method.GetCustomAttributes(true).OfType().Any(); } - protected virtual IAuthorizeData[] GetAuthorizationDataAttributes(MethodInvocationAuthorizationContext context) + protected virtual IEnumerable GetAuthorizationDataAttributes(MethodInfo methodInfo) { - var classAttributes = context.Method.DeclaringType + var attributes = methodInfo .GetCustomAttributes(true) .OfType(); - var methodAttributes = context.Method - .GetCustomAttributes(true) - .OfType(); + if (methodInfo.IsPublic) + { + attributes = attributes + .Union( + methodInfo.DeclaringType + .GetCustomAttributes(true) + .OfType() + ); + } - return classAttributes.Union(methodAttributes).ToArray(); + return attributes; } protected async Task CheckAsync(IAuthorizeData authorizationAttribute) diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IPermissionDefinitionProvider.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IPermissionDefinitionProvider.cs index d0a41b3390..ef92125595 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IPermissionDefinitionProvider.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IPermissionDefinitionProvider.cs @@ -1,8 +1,6 @@ -using Volo.Abp.DependencyInjection; - -namespace Volo.Abp.Authorization.Permissions +namespace Volo.Abp.Authorization.Permissions { - public interface IPermissionDefinitionProvider : ISingletonDependency + public interface IPermissionDefinitionProvider { void Define(IPermissionDefinitionContext context); } diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IPermissionValueProvider.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IPermissionValueProvider.cs index bf19132e23..a9b5e78337 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IPermissionValueProvider.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IPermissionValueProvider.cs @@ -1,9 +1,8 @@ using System.Threading.Tasks; -using Volo.Abp.DependencyInjection; namespace Volo.Abp.Authorization.Permissions { - public interface IPermissionValueProvider : ISingletonDependency + public interface IPermissionValueProvider { string Name { get; } diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionManager.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionManager.cs index 4915721af0..bb58089c82 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionManager.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionManager.cs @@ -10,9 +10,6 @@ namespace Volo.Abp.Authorization.Permissions { public class PermissionDefinitionManager : IPermissionDefinitionManager, ISingletonDependency { - protected List Providers => _lazyProviders.Value; - private readonly Lazy> _lazyProviders; - protected IDictionary PermissionGroupDefinitions => _lazyPermissionGroupDefinitions.Value; private readonly Lazy> _lazyPermissionGroupDefinitions; @@ -30,9 +27,15 @@ namespace Volo.Abp.Authorization.Permissions _serviceProvider = serviceProvider; Options = options.Value; - _lazyProviders = new Lazy>(CreatePermissionProviders, true); - _lazyPermissionDefinitions = new Lazy>(CreatePermissionDefinitions, true); - _lazyPermissionGroupDefinitions = new Lazy>(CreatePermissionGroupDefinitions, true); + _lazyPermissionDefinitions = new Lazy>( + CreatePermissionDefinitions, + isThreadSafe: true + ); + + _lazyPermissionGroupDefinitions = new Lazy>( + CreatePermissionGroupDefinitions, + isThreadSafe: true + ); } public virtual PermissionDefinition Get(string name) @@ -64,14 +67,6 @@ namespace Volo.Abp.Authorization.Permissions return PermissionGroupDefinitions.Values.ToImmutableList(); } - protected virtual List CreatePermissionProviders() - { - return Options - .DefinitionProviders - .Select(p => _serviceProvider.GetRequiredService(p) as IPermissionDefinitionProvider) - .ToList(); - } - protected virtual Dictionary CreatePermissionDefinitions() { var permissions = new Dictionary(); @@ -87,7 +82,9 @@ namespace Volo.Abp.Authorization.Permissions return permissions; } - protected virtual void AddPermissionToDictionaryRecursively(Dictionary permissions, PermissionDefinition permission) + protected virtual void AddPermissionToDictionaryRecursively( + Dictionary permissions, + PermissionDefinition permission) { if (permissions.ContainsKey(permission.Name)) { @@ -106,9 +103,17 @@ namespace Volo.Abp.Authorization.Permissions { var context = new PermissionDefinitionContext(); - foreach (var provider in Providers) + using (var scope = _serviceProvider.CreateScope()) { - provider.Define(context); + var providers = Options + .DefinitionProviders + .Select(p => scope.ServiceProvider.GetRequiredService(p) as IPermissionDefinitionProvider) + .ToList(); + + foreach (var provider in providers) + { + provider.Define(context); + } } return context.Groups; diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionProvider.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionProvider.cs index df1674dc2e..0584ffe426 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionProvider.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionProvider.cs @@ -1,6 +1,8 @@ -namespace Volo.Abp.Authorization.Permissions +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Authorization.Permissions { - public abstract class PermissionDefinitionProvider : IPermissionDefinitionProvider + public abstract class PermissionDefinitionProvider : IPermissionDefinitionProvider, ITransientDependency { public abstract void Define(IPermissionDefinitionContext context); } 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 914bd4af34..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 @@ -1,8 +1,9 @@ using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; namespace Volo.Abp.Authorization.Permissions { - public abstract class PermissionValueProvider : IPermissionValueProvider + public abstract class PermissionValueProvider : IPermissionValueProvider, ISingletonDependency //TODO: to transient? { public abstract string Name { get; } diff --git a/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/AbpBackgroundWorkersModule.cs b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/AbpBackgroundWorkersModule.cs index cae652ad28..1ed2a7f78a 100644 --- a/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/AbpBackgroundWorkersModule.cs +++ b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/AbpBackgroundWorkersModule.cs @@ -10,7 +10,6 @@ namespace Volo.Abp.BackgroundWorkers )] public class AbpBackgroundWorkersModule : AbpModule { - public override void OnApplicationInitialization(ApplicationInitializationContext context) { var options = context.ServiceProvider.GetRequiredService>().Value; diff --git a/framework/src/Volo.Abp.Core/System/Collections/Generic/AbpListExtensions.cs b/framework/src/Volo.Abp.Core/System/Collections/Generic/AbpListExtensions.cs index 171a6ac237..a123236a4e 100644 --- a/framework/src/Volo.Abp.Core/System/Collections/Generic/AbpListExtensions.cs +++ b/framework/src/Volo.Abp.Core/System/Collections/Generic/AbpListExtensions.cs @@ -32,6 +32,18 @@ namespace System.Collections.Generic source.Insert(source.Count, item); } + public static void InsertAfter(this IList source, T existingItem, T item) + { + var index = source.IndexOf(existingItem); + if (index < 0) + { + source.AddFirst(item); + return; + } + + source.Insert(index + 1, item); + } + public static void InsertAfter(this IList source, Predicate selector, T item) { var index = source.FindIndex(selector); @@ -44,6 +56,17 @@ namespace System.Collections.Generic source.Insert(index + 1, item); } + public static void InsertBefore(this IList source, T existingItem, T item) + { + var index = source.IndexOf(existingItem); + if (index < 0) + { + source.AddLast(item); + return; + } + + source.Insert(index, item); + } public static void InsertBefore(this IList source, Predicate selector, T item) { diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Aspects/AbpCrossCuttingConcerns.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Aspects/AbpCrossCuttingConcerns.cs index 35aa6d0811..4c8033a0cf 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Aspects/AbpCrossCuttingConcerns.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Aspects/AbpCrossCuttingConcerns.cs @@ -12,6 +12,7 @@ namespace Volo.Abp.Aspects public const string Validation = "AbpValidation"; public const string UnitOfWork = "AbpUnitOfWork"; public const string Authorization = "AbpAuthorization"; + public const string FeatureChecking = "AbpFeatureChecking"; public static void AddApplied(object obj, params string[] concerns) { diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo.Abp.Ddd.Application.csproj b/framework/src/Volo.Abp.Ddd.Application/Volo.Abp.Ddd.Application.csproj index 57f909667b..243c94e66a 100644 --- a/framework/src/Volo.Abp.Ddd.Application/Volo.Abp.Ddd.Application.csproj +++ b/framework/src/Volo.Abp.Ddd.Application/Volo.Abp.Ddd.Application.csproj @@ -17,6 +17,7 @@ + diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/AbpDddApplicationModule.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/AbpDddApplicationModule.cs index 84b609b99f..e5ade49532 100644 --- a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/AbpDddApplicationModule.cs +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/AbpDddApplicationModule.cs @@ -2,6 +2,7 @@ using Volo.Abp.Application.Services; using Volo.Abp.Authorization; using Volo.Abp.Domain; +using Volo.Abp.Features; using Volo.Abp.Http; using Volo.Abp.Http.Modeling; using Volo.Abp.Modularity; @@ -20,7 +21,8 @@ namespace Volo.Abp.Application typeof(AbpValidationModule), typeof(AbpAuthorizationModule), typeof(AbpHttpAbstractionsModule), - typeof(AbpSettingsModule) + typeof(AbpSettingsModule), + typeof(AbpFeaturesModule) )] public class AbpDddApplicationModule : AbpModule { @@ -30,8 +32,7 @@ namespace Volo.Abp.Application { options.IgnoredInterfaces.AddIfNotContains(typeof(IRemoteService)); options.IgnoredInterfaces.AddIfNotContains(typeof(IApplicationService)); - options.IgnoredInterfaces.AddIfNotContains(typeof(IUnitOfWorkEnabled)); //TODO: Move to it's own module if possible? - options.IgnoredInterfaces.AddIfNotContains(typeof(IAuthorizationEnabled)); //TODO: Move to it's own module if possible? + options.IgnoredInterfaces.AddIfNotContains(typeof(IUnitOfWorkEnabled)); }); } } diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs index 63a749e259..48865f38f2 100644 --- a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs @@ -9,6 +9,7 @@ using Volo.Abp.Aspects; using Volo.Abp.Auditing; using Volo.Abp.Authorization; using Volo.Abp.DependencyInjection; +using Volo.Abp.Features; using Volo.Abp.Guids; using Volo.Abp.MultiTenancy; using Volo.Abp.ObjectMapping; @@ -25,7 +26,6 @@ namespace Volo.Abp.Application.Services IAvoidDuplicateCrossCuttingConcerns, IValidationEnabled, IUnitOfWorkEnabled, - IAuthorizationEnabled, IAuditingEnabled, ITransientDependency { @@ -51,6 +51,8 @@ namespace Volo.Abp.Application.Services public IAuthorizationService AuthorizationService { get; set; } + public IFeatureChecker FeatureChecker { get; set; } + protected IUnitOfWork CurrentUnitOfWork => UnitOfWorkManager?.Current; protected ILogger Logger => _lazyLogger.Value; diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/AbpEmailingModule.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/AbpEmailingModule.cs index 7648ddc910..b861a3898c 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/AbpEmailingModule.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/AbpEmailingModule.cs @@ -18,11 +18,6 @@ namespace Volo.Abp.Emailing { public override void ConfigureServices(ServiceConfigurationContext context) { - Configure(options => - { - options.DefinitionProviders.Add(); - }); - Configure(options => { options.FileSets.AddEmbedded(); diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo.Abp.EntityFrameworkCore.MySQL.csproj b/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo.Abp.EntityFrameworkCore.MySQL.csproj index 52d430bbaa..5e9c7a91fb 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo.Abp.EntityFrameworkCore.MySQL.csproj +++ b/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo.Abp.EntityFrameworkCore.MySQL.csproj @@ -16,9 +16,9 @@ - + - + diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextMySQLExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextMySQLExtensions.cs index 35afda3d1e..542bf9e19e 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextMySQLExtensions.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextMySQLExtensions.cs @@ -1,7 +1,7 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; -using MySql.Data.EntityFrameworkCore.Infraestructure; using System; +using Microsoft.EntityFrameworkCore.Infrastructure; using Volo.Abp.EntityFrameworkCore.DependencyInjection; namespace Volo.Abp.EntityFrameworkCore @@ -10,15 +10,15 @@ namespace Volo.Abp.EntityFrameworkCore { public static DbContextOptionsBuilder UseMySQL( [NotNull] this AbpDbContextConfigurationContext context, - [CanBeNull] Action mySQLOptionsAction = null) + [CanBeNull] Action mySQLOptionsAction = null) { if (context.ExistingConnection != null) { - return context.DbContextOptions.UseMySQL(context.ExistingConnection, mySQLOptionsAction); + return context.DbContextOptions.UseMySql(context.ExistingConnection, mySQLOptionsAction); } else { - return context.DbContextOptions.UseMySQL(context.ConnectionString, mySQLOptionsAction); + return context.DbContextOptions.UseMySql(context.ConnectionString, mySQLOptionsAction); } } } diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/AbpDbContextOptionsMySQLExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/AbpDbContextOptionsMySQLExtensions.cs index f71bc631ec..3e2dd911be 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/AbpDbContextOptionsMySQLExtensions.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/AbpDbContextOptionsMySQLExtensions.cs @@ -1,6 +1,6 @@ using JetBrains.Annotations; -using MySql.Data.EntityFrameworkCore.Infraestructure; using System; +using Microsoft.EntityFrameworkCore.Infrastructure; namespace Volo.Abp.EntityFrameworkCore { @@ -8,7 +8,7 @@ namespace Volo.Abp.EntityFrameworkCore { public static void UseMySQL( [NotNull] this AbpDbContextOptions options, - [CanBeNull] Action mySQLOptionsAction = null) + [CanBeNull] Action mySQLOptionsAction = null) { options.Configure(context => { @@ -18,7 +18,7 @@ namespace Volo.Abp.EntityFrameworkCore public static void UseMySQL( [NotNull] this AbpDbContextOptions options, - [CanBeNull] Action mySQLOptionsAction = null) + [CanBeNull] Action mySQLOptionsAction = null) where TDbContext : AbpDbContext { options.Configure(context => diff --git a/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/AbpEventBusRabbitMqModule.cs b/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/AbpEventBusRabbitMqModule.cs index 4fc74bddec..f13f1ae2fe 100644 --- a/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/AbpEventBusRabbitMqModule.cs +++ b/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/AbpEventBusRabbitMqModule.cs @@ -15,5 +15,13 @@ namespace Volo.Abp.EventBus.RabbitMq Configure(configuration.GetSection("RabbitMQ:EventBus")); } + + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + context + .ServiceProvider + .GetRequiredService() + .Initialize(); + } } } diff --git a/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs b/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs index a8dd36f913..d26e489afe 100644 --- a/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs +++ b/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs @@ -30,7 +30,7 @@ namespace Volo.Abp.EventBus.RabbitMq protected ConcurrentDictionary> HandlerFactories { get; } protected ConcurrentDictionary EventTypes { get; } protected IRabbitMqMessageConsumerFactory MessageConsumerFactory { get; } - protected IRabbitMqMessageConsumer Consumer { get; } + protected IRabbitMqMessageConsumer Consumer { get; private set; } public RabbitMqDistributedEventBus( IOptions options, @@ -50,18 +50,23 @@ namespace Volo.Abp.EventBus.RabbitMq HandlerFactories = new ConcurrentDictionary>(); EventTypes = new ConcurrentDictionary(); + Initialize(); + } + + public void Initialize() + { Consumer = MessageConsumerFactory.Create( new ExchangeDeclareConfiguration( - RabbitMqEventBusOptions.ExchangeName, + RabbitMqEventBusOptions.ExchangeName, type: "direct", durable: true - ), + ), new QueueDeclareConfiguration( RabbitMqEventBusOptions.ClientName, durable: true, exclusive: false, autoDelete: false - ), + ), RabbitMqEventBusOptions.ConnectionName ); diff --git a/framework/src/Volo.Abp.Features/Volo.Abp.Features.csproj b/framework/src/Volo.Abp.Features/Volo.Abp.Features.csproj index e626a34a9f..fc59bfaa9f 100644 --- a/framework/src/Volo.Abp.Features/Volo.Abp.Features.csproj +++ b/framework/src/Volo.Abp.Features/Volo.Abp.Features.csproj @@ -16,6 +16,7 @@ + diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeaturesModule.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeaturesModule.cs index f83797334f..fed872890c 100644 --- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeaturesModule.cs +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeaturesModule.cs @@ -1,18 +1,52 @@ -using Volo.Abp.Localization; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.MultiTenancy; +using Volo.Abp.Validation; namespace Volo.Abp.Features { [DependsOn( typeof(AbpLocalizationAbstractionsModule), - typeof(AbpMultiTenancyAbstractionsModule) + typeof(AbpMultiTenancyAbstractionsModule), + typeof(AbpValidationModule) )] public class AbpFeaturesModule : AbpModule { + public override void PreConfigureServices(ServiceConfigurationContext context) + { + context.Services.OnRegistred(FeatureInterceptorRegistrar.RegisterIfNeeded); + AutoAddDefinitionProviders(context.Services); + } + public override void ConfigureServices(ServiceConfigurationContext context) { - + context.Services.Configure(options => + { + options.ValueProviders.Add(); + options.ValueProviders.Add(); + options.ValueProviders.Add(); + }); + } + + private static void AutoAddDefinitionProviders(IServiceCollection services) + { + var definitionProviders = new List(); + + services.OnRegistred(context => + { + if (typeof(IFeatureDefinitionProvider).IsAssignableFrom(context.ImplementationType)) + { + definitionProviders.Add(context.ImplementationType); + } + }); + + services.Configure(options => + { + options.DefinitionProviders.AddIfNotContains(definitionProviders); + }); } } } diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/DefaultValueSettingValueProvider.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/DefaultValueSettingValueProvider.cs new file mode 100644 index 0000000000..275be89af2 --- /dev/null +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/DefaultValueSettingValueProvider.cs @@ -0,0 +1,22 @@ +using System.Threading.Tasks; + +namespace Volo.Abp.Features +{ + public class DefaultValueFeatureValueProvider : FeatureValueProvider //TODO: Directly implement IFeatureValueProvider + { + public const string ProviderName = "Default"; + + public override string Name => ProviderName; + + public DefaultValueFeatureValueProvider(IFeatureStore settingStore) + : base(settingStore) + { + + } + + public override Task GetOrNullAsync(FeatureDefinition setting) + { + return Task.FromResult(setting.DefaultValue); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/DisableFeatureCheckAttribute.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/DisableFeatureCheckAttribute.cs new file mode 100644 index 0000000000..194f0eaf90 --- /dev/null +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/DisableFeatureCheckAttribute.cs @@ -0,0 +1,10 @@ +using System; + +namespace Volo.Abp.Features +{ + [AttributeUsage(AttributeTargets.Method)] + public class DisableFeatureCheckAttribute : Attribute + { + + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/EditionFeatureValueProvider.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/EditionFeatureValueProvider.cs new file mode 100644 index 0000000000..ef22197f8a --- /dev/null +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/EditionFeatureValueProvider.cs @@ -0,0 +1,32 @@ +using System.Security.Principal; +using System.Threading.Tasks; +using Volo.Abp.Security.Claims; + +namespace Volo.Abp.Features +{ + public class EditionFeatureValueProvider : FeatureValueProvider + { + public const string ProviderName = "Edition"; + + public override string Name => ProviderName; + + protected ICurrentPrincipalAccessor PrincipalAccessor; + + public EditionFeatureValueProvider(IFeatureStore featureStore, ICurrentPrincipalAccessor principalAccessor) + : base(featureStore) + { + PrincipalAccessor = principalAccessor; + } + + public override async Task GetOrNullAsync(FeatureDefinition feature) + { + var editionId = PrincipalAccessor.Principal?.FindEditionId(); + if (editionId == null) + { + return null; + } + + return await FeatureStore.GetOrNullAsync(feature.Name, Name, editionId.Value.ToString("N")); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureChecker.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureChecker.cs index 17bd05db6d..bc9f8565ed 100644 --- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureChecker.cs +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureChecker.cs @@ -4,39 +4,42 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -using Volo.Abp.DependencyInjection; namespace Volo.Abp.Features { - public class FeatureChecker : IFeatureChecker, ITransientDependency + public class FeatureChecker : FeatureCheckerBase { - protected IFeatureDefinitionManager FeatureDefinitionManager { get; } - protected Lazy> Providers { get; } protected FeatureOptions Options { get; } + protected IServiceProvider ServiceProvider { get; } + protected IFeatureDefinitionManager FeatureDefinitionManager { get; } + protected List Providers => _providers.Value; + + private readonly Lazy> _providers; public FeatureChecker( IOptions options, IServiceProvider serviceProvider, IFeatureDefinitionManager featureDefinitionManager) { + ServiceProvider = serviceProvider; FeatureDefinitionManager = featureDefinitionManager; Options = options.Value; - Providers = new Lazy>( + _providers = new Lazy>( () => Options .ValueProviders - .Select(type => serviceProvider.GetRequiredService(type) as IFeatureValueProvider) + .Select(type => ServiceProvider.GetRequiredService(type) as IFeatureValueProvider) .ToList(), true ); } - - public virtual async Task GetOrNullAsync(string name) + + public override async Task GetOrNullAsync(string name) { var featureDefinition = FeatureDefinitionManager.Get(name); var providers = Enumerable - .Reverse(Providers.Value); + .Reverse(Providers); if (featureDefinition.AllowedProviders.Any()) { @@ -46,27 +49,6 @@ namespace Volo.Abp.Features return await GetOrNullValueFromProvidersAsync(providers, featureDefinition); } - public async Task IsEnabledAsync(string name) - { - var value = await GetOrNullAsync(name); - if (value == null) - { - return false; - } - - try - { - return bool.Parse(value); - } - catch (Exception ex) - { - throw new AbpException( - $"The value '{value}' for the feature '{name}' should be a boolean, but was not!", - ex - ); - } - } - protected virtual async Task GetOrNullValueFromProvidersAsync( IEnumerable providers, FeatureDefinition feature) diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureCheckerBase.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureCheckerBase.cs new file mode 100644 index 0000000000..a24e0901f7 --- /dev/null +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureCheckerBase.cs @@ -0,0 +1,32 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Features +{ + public abstract class FeatureCheckerBase : IFeatureChecker, ITransientDependency + { + public abstract Task GetOrNullAsync(string name); + + public virtual async Task IsEnabledAsync(string name) + { + var value = await GetOrNullAsync(name); + if (value == null) + { + return false; + } + + try + { + return bool.Parse(value); + } + catch (Exception ex) + { + throw new AbpException( + $"The value '{value}' for the feature '{name}' should be a boolean, but was not!", + ex + ); + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureCheckerExtensions.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureCheckerExtensions.cs index 05122e8e6a..9d7f608c4b 100644 --- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureCheckerExtensions.cs +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureCheckerExtensions.cs @@ -1,13 +1,18 @@ using System; +using System.Collections.Generic; using System.Threading.Tasks; using JetBrains.Annotations; +using Volo.Abp.Authorization; using Volo.Abp.Threading; namespace Volo.Abp.Features { public static class FeatureCheckerExtensions { - public static async Task GetAsync([NotNull] this IFeatureChecker featureChecker, [NotNull] string name, T defaultValue = default) + public static async Task GetAsync( + [NotNull] this IFeatureChecker featureChecker, + [NotNull] string name, + T defaultValue = default) where T : struct { Check.NotNull(featureChecker, nameof(featureChecker)); @@ -17,21 +22,122 @@ namespace Volo.Abp.Features return value?.To() ?? defaultValue; } - public static string GetOrNull([NotNull] this IFeatureChecker featureChecker, [NotNull] string name) + public static string GetOrNull( + [NotNull] this IFeatureChecker featureChecker, + [NotNull] string name) { Check.NotNull(featureChecker, nameof(featureChecker)); return AsyncHelper.RunSync(() => featureChecker.GetOrNullAsync(name)); } - public static T Get([NotNull] this IFeatureChecker featureChecker, [NotNull] string name, T defaultValue = default) + public static T Get( + [NotNull] this IFeatureChecker featureChecker, + [NotNull] string name, + T defaultValue = default) where T : struct { return AsyncHelper.RunSync(() => featureChecker.GetAsync(name, defaultValue)); } - public static bool IsEnabled([NotNull] this IFeatureChecker featureChecker, [NotNull] string name) + public static bool IsEnabled( + [NotNull] this IFeatureChecker featureChecker, + [NotNull] string name) { return AsyncHelper.RunSync(() => featureChecker.IsEnabledAsync(name)); } + + public static async Task IsEnabledAsync(this IFeatureChecker featureChecker, bool requiresAll, params string[] featureNames) + { + if (featureNames.IsNullOrEmpty()) + { + return true; + } + + if (requiresAll) + { + foreach (var featureName in featureNames) + { + if (!(await featureChecker.IsEnabledAsync(featureName))) + { + return false; + } + } + + return true; + } + + foreach (var featureName in featureNames) + { + if (await featureChecker.IsEnabledAsync(featureName)) + { + return true; + } + } + + return false; + } + + public static bool IsEnabled(this IFeatureChecker featureChecker, bool requiresAll, params string[] featureNames) + { + return AsyncHelper.RunSync(() => featureChecker.IsEnabledAsync(requiresAll, featureNames)); + } + + public static async Task CheckEnabledAsync(this IFeatureChecker featureChecker, string featureName) + { + if (!(await featureChecker.IsEnabledAsync(featureName))) + { + throw new AbpAuthorizationException("Feature is not enabled: " + featureName); + } + } + + public static void CheckEnabled(this IFeatureChecker featureChecker, string featureName) + { + if (!featureChecker.IsEnabled(featureName)) + { + throw new AbpAuthorizationException("Feature is not enabled: " + featureName); + } + } + + public static async Task CheckEnabledAsync(this IFeatureChecker featureChecker, bool requiresAll, params string[] featureNames) + { + if (featureNames.IsNullOrEmpty()) + { + return; + } + + if (requiresAll) + { + foreach (var featureName in featureNames) + { + if (!(await featureChecker.IsEnabledAsync(featureName))) + { + throw new AbpAuthorizationException( + "Required features are not enabled. All of these features must be enabled: " + + string.Join(", ", featureNames) + ); + } + } + } + else + { + foreach (var featureName in featureNames) + { + if (await featureChecker.IsEnabledAsync(featureName)) + { + return; + } + } + + throw new AbpAuthorizationException( + "Required features are not enabled. At least one of these features must be enabled: " + + string.Join(", ", featureNames) + ); + } + } + + public static void CheckEnabled(this IFeatureChecker featureChecker, bool requiresAll, params string[] featureNames) + { + AsyncHelper.RunSync(() => featureChecker.CheckEnabledAsync(requiresAll, featureNames)); + } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinition.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinition.cs index 675697334f..bd79480326 100644 --- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinition.cs +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinition.cs @@ -3,6 +3,7 @@ using System.Collections.Immutable; using System.Linq; using JetBrains.Annotations; using Volo.Abp.Localization; +using Volo.Abp.Validation.StringValues; namespace Volo.Abp.Features { @@ -29,6 +30,7 @@ namespace Volo.Abp.Features /// Parent of this feature, if one exists. /// If set, this feature can be enabled only if the parent is enabled. /// + [CanBeNull] public FeatureDefinition Parent { get; private set; } /// @@ -53,28 +55,52 @@ namespace Volo.Abp.Features /// A list of allowed providers to get/set value of this feature. /// An empty list indicates that all providers are allowed. /// + [NotNull] public List AllowedProviders { get; } + /// + /// Gets/sets a key-value on the . + /// + /// Name of the property + /// + /// Returns the value in the dictionary by given . + /// Returns null if given is not present in the dictionary. + /// + [CanBeNull] + public object this[string name] + { + get => Properties.GetOrDefault(name); + set => Properties[name] = value; + } + /// /// Can be used to get/set custom properties for this feature. /// [NotNull] public Dictionary Properties { get; } - //TODO: Implement input type like old ABP! + /// + /// Input type. + /// This can be used to prepare an input for changing this feature's value. + /// Default: . + /// + [CanBeNull] + public IStringValueType ValueType { get; set; } public FeatureDefinition( string name, string defaultValue = null, ILocalizableString displayName = null, ILocalizableString description = null, + IStringValueType valueType = null, bool isVisibleToClients = true) { Name = name; DefaultValue = defaultValue; - IsVisibleToClients = isVisibleToClients; DisplayName = displayName ?? new FixedLocalizableString(name); Description = description; + ValueType = valueType; + IsVisibleToClients = isVisibleToClients; Properties = new Dictionary(); AllowedProviders = new List(); @@ -114,13 +140,15 @@ namespace Volo.Abp.Features string defaultValue = null, ILocalizableString displayName = null, ILocalizableString description = null, + IStringValueType valueType = null, bool isVisibleToClients = true) { var feature = new FeatureDefinition( name, defaultValue, displayName, - description, + description, + valueType, isVisibleToClients) { Parent = this diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinitionContext.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinitionContext.cs index d14695b30a..be9c1beec9 100644 --- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinitionContext.cs +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinitionContext.cs @@ -1,38 +1,51 @@ using System.Collections.Generic; -using System.Collections.Immutable; +using Volo.Abp.Localization; namespace Volo.Abp.Features { public class FeatureDefinitionContext : IFeatureDefinitionContext { - protected Dictionary Features { get; } + internal Dictionary Groups { get; } - public FeatureDefinitionContext(Dictionary features) + public FeatureDefinitionContext() { - Features = features; + Groups = new Dictionary(); } - public virtual FeatureDefinition GetOrNull(string name) + public FeatureGroupDefinition AddGroup(string name, ILocalizableString displayName = null) { - return Features.GetOrDefault(name); - } + Check.NotNull(name, nameof(name)); - public virtual IReadOnlyList GetAll() - { - return Features.Values.ToImmutableList(); + if (Groups.ContainsKey(name)) + { + throw new AbpException($"There is already an existing permission group with name: {name}"); + } + + return Groups[name] = new FeatureGroupDefinition(name, displayName); } - public virtual void Add(params FeatureDefinition[] definitions) + public FeatureGroupDefinition GetGroupOrNull(string name) { - if (definitions.IsNullOrEmpty()) + Check.NotNull(name, nameof(name)); + + if (!Groups.ContainsKey(name)) { - return; + return null; } - foreach (var definition in definitions) + return Groups[name]; + } + + public void RemoveGroup(string name) + { + Check.NotNull(name, nameof(name)); + + if (!Groups.ContainsKey(name)) { - Features[definition.Name] = definition; + throw new AbpException($"Undefined feature group: '{name}'."); } + + Groups.Remove(name); } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinitionManager.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinitionManager.cs index bf36c114d3..13d15ac95e 100644 --- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinitionManager.cs +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinitionManager.cs @@ -10,9 +10,11 @@ namespace Volo.Abp.Features { public class FeatureDefinitionManager : IFeatureDefinitionManager, ISingletonDependency { - protected Lazy> Providers { get; } + protected IDictionary FeatureGroupDefinitions => _lazyFeatureGroupDefinitions.Value; + private readonly Lazy> _lazyFeatureGroupDefinitions; - protected Lazy> FeatureDefinitions { get; } + protected IDictionary FeatureDefinitions => _lazyFeatureDefinitions.Value; + private readonly Lazy> _lazyFeatureDefinitions; protected FeatureOptions Options { get; } @@ -25,8 +27,15 @@ namespace Volo.Abp.Features _serviceProvider = serviceProvider; Options = options.Value; - Providers = new Lazy>(CreateFeatureProviders, true); - FeatureDefinitions = new Lazy>(CreateFeatureDefinitions, true); + _lazyFeatureDefinitions = new Lazy>( + CreateFeatureDefinitions, + isThreadSafe: true + ); + + _lazyFeatureGroupDefinitions = new Lazy>( + CreateFeatureGroupDefinitions, + isThreadSafe:true + ); } public virtual FeatureDefinition Get(string name) @@ -45,32 +54,64 @@ namespace Volo.Abp.Features public virtual IReadOnlyList GetAll() { - return FeatureDefinitions.Value.Values.ToImmutableList(); + return FeatureDefinitions.Values.ToImmutableList(); } public virtual FeatureDefinition GetOrNull(string name) { - return FeatureDefinitions.Value.GetOrDefault(name); + return FeatureDefinitions.GetOrDefault(name); } - protected virtual List CreateFeatureProviders() + protected virtual Dictionary CreateFeatureDefinitions() { - return Options - .DefinitionProviders - .Select(p => _serviceProvider.GetRequiredService(p) as IFeatureDefinitionProvider) - .ToList(); + var features = new Dictionary(); + + foreach (var groupDefinition in FeatureGroupDefinitions.Values) + { + foreach (var feature in groupDefinition.Features) + { + AddFeatureToDictionaryRecursively(features, feature); + } + } + + return features; } - protected virtual IDictionary CreateFeatureDefinitions() + protected virtual void AddFeatureToDictionaryRecursively( + Dictionary features, + FeatureDefinition feature) { - var features = new Dictionary(); + if (features.ContainsKey(feature.Name)) + { + throw new AbpException("Duplicate feature name: " + feature.Name); + } + + features[feature.Name] = feature; - foreach (var provider in Providers.Value) + foreach (var child in feature.Children) { - provider.Define(new FeatureDefinitionContext(features)); + AddFeatureToDictionaryRecursively(features, child); } + } - return features; + protected virtual Dictionary CreateFeatureGroupDefinitions() + { + var context = new FeatureDefinitionContext(); + + using (var scope = _serviceProvider.CreateScope()) + { + var providers = Options + .DefinitionProviders + .Select(p => scope.ServiceProvider.GetRequiredService(p) as IFeatureDefinitionProvider) + .ToList(); + + foreach (var provider in providers) + { + provider.Define(context); + } + } + + return context.Groups; } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureGroupDefinition.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureGroupDefinition.cs new file mode 100644 index 0000000000..cba38070a7 --- /dev/null +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureGroupDefinition.cs @@ -0,0 +1,111 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using Volo.Abp.Localization; +using Volo.Abp.Validation.StringValues; + +namespace Volo.Abp.Features +{ + public class FeatureGroupDefinition + { + /// + /// Unique name of the group. + /// + public string Name { get; } + + public Dictionary Properties { get; } + + public ILocalizableString DisplayName + { + get => _displayName; + set => _displayName = Check.NotNull(value, nameof(value)); + } + private ILocalizableString _displayName; + + public IReadOnlyList Features => _features.ToImmutableList(); + private readonly List _features; + + /// + /// Gets/sets a key-value on the . + /// + /// Name of the property + /// + /// Returns the value in the dictionary by given . + /// Returns null if given is not present in the dictionary. + /// + public object this[string name] + { + get => Properties.GetOrDefault(name); + set => Properties[name] = value; + } + + protected internal FeatureGroupDefinition( + string name, + ILocalizableString displayName = null) + { + Name = name; + DisplayName = displayName ?? new FixedLocalizableString(Name); + + Properties = new Dictionary(); + _features = new List(); + } + + public virtual FeatureDefinition AddFeature( + string name, + string defaultValue = null, + ILocalizableString displayName = null, + ILocalizableString description = null, + IStringValueType valueType = null, + bool isVisibleToClients = true) + { + var feature = new FeatureDefinition( + name, + defaultValue, + displayName, + description, + valueType, + isVisibleToClients + ); + + _features.Add(feature); + + return feature; + } + + public virtual List GetFeaturesWithChildren() + { + var features = new List(); + + foreach (var feature in _features) + { + AddFeatureToListRecursively(features, feature); + } + + return features; + } + + /// + /// Sets a property in the dictionary. + /// This is a shortcut for nested calls on this object. + /// + public virtual FeatureGroupDefinition WithProperty(string key, object value) + { + Properties[key] = value; + return this; + } + + private void AddFeatureToListRecursively(List features, FeatureDefinition feature) + { + features.Add(feature); + + foreach (var child in feature.Children) + { + AddFeatureToListRecursively(features, child); + } + } + + public override string ToString() + { + return $"[{nameof(FeatureGroupDefinition)} {Name}]"; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureInterceptor.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureInterceptor.cs new file mode 100644 index 0000000000..a0b15674f6 --- /dev/null +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureInterceptor.cs @@ -0,0 +1,56 @@ +using System.Threading.Tasks; +using Volo.Abp.Aspects; +using Volo.Abp.DependencyInjection; +using Volo.Abp.DynamicProxy; +using Volo.Abp.Threading; + +namespace Volo.Abp.Features +{ + public class FeatureInterceptor : AbpInterceptor, ITransientDependency + { + private readonly IMethodInvocationFeatureCheckerService _methodInvocationFeatureCheckerService; + + public FeatureInterceptor( + IMethodInvocationFeatureCheckerService methodInvocationFeatureCheckerService) + { + _methodInvocationFeatureCheckerService = methodInvocationFeatureCheckerService; + } + + public override void Intercept(IAbpMethodInvocation invocation) + { + if (AbpCrossCuttingConcerns.IsApplied( + invocation.TargetObject, + AbpCrossCuttingConcerns.FeatureChecking)) + { + invocation.Proceed(); + return; + } + + AsyncHelper.RunSync(() => CheckFeaturesAsync(invocation)); + invocation.Proceed(); + } + + public override async Task InterceptAsync(IAbpMethodInvocation invocation) + { + if (AbpCrossCuttingConcerns.IsApplied( + invocation.TargetObject, + AbpCrossCuttingConcerns.FeatureChecking)) + { + await invocation.ProceedAsync(); + return; + } + + AsyncHelper.RunSync(() => CheckFeaturesAsync(invocation)); + await invocation.ProceedAsync(); + } + + protected virtual Task CheckFeaturesAsync(IAbpMethodInvocation invocation) + { + return _methodInvocationFeatureCheckerService.CheckAsync( + new MethodInvocationFeatureCheckerContext( + invocation.Method + ) + ); + } + } +} diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureInterceptorRegistrar.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureInterceptorRegistrar.cs new file mode 100644 index 0000000000..8cb6cd7ea5 --- /dev/null +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureInterceptorRegistrar.cs @@ -0,0 +1,36 @@ +using System; +using System.Linq; +using System.Reflection; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Features +{ + public static class FeatureInterceptorRegistrar + { + public static void RegisterIfNeeded(IOnServiceRegistredContext context) + { + if (ShouldIntercept(context.ImplementationType)) + { + context.Interceptors.TryAdd(); + } + } + + private static bool ShouldIntercept(Type type) + { + return type.IsDefined(typeof(RequiresFeatureAttribute), true) || + AnyMethodHasRequiresFeatureAttribute(type); + } + + private static bool AnyMethodHasRequiresFeatureAttribute(Type implementationType) + { + return implementationType + .GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + .Any(HasRequiresFeatureAttribute); + } + + private static bool HasRequiresFeatureAttribute(MemberInfo methodInfo) + { + return methodInfo.IsDefined(typeof(RequiresFeatureAttribute), true); + } + } +} \ No newline at end of file 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.Features/Volo/Abp/Features/IFeatureDefinitionContext.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/IFeatureDefinitionContext.cs index 8762b9d34b..03fc14c579 100644 --- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/IFeatureDefinitionContext.cs +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/IFeatureDefinitionContext.cs @@ -1,9 +1,14 @@ -namespace Volo.Abp.Features +using JetBrains.Annotations; +using Volo.Abp.Localization; + +namespace Volo.Abp.Features { public interface IFeatureDefinitionContext { - FeatureDefinition GetOrNull(string name); + FeatureGroupDefinition AddGroup([NotNull] string name, ILocalizableString displayName = null); - void Add(params FeatureDefinition[] definitions); + FeatureGroupDefinition GetGroupOrNull(string name); + + void RemoveGroup(string name); } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/IMethodInvocationFeatureCheckerService.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/IMethodInvocationFeatureCheckerService.cs new file mode 100644 index 0000000000..7d6be55c47 --- /dev/null +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/IMethodInvocationFeatureCheckerService.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; + +namespace Volo.Abp.Features +{ + public interface IMethodInvocationFeatureCheckerService + { + Task CheckAsync( + MethodInvocationFeatureCheckerContext context + ); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/MethodInvocationFeatureCheckerContext.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/MethodInvocationFeatureCheckerContext.cs new file mode 100644 index 0000000000..81bbecfe68 --- /dev/null +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/MethodInvocationFeatureCheckerContext.cs @@ -0,0 +1,14 @@ +using System.Reflection; + +namespace Volo.Abp.Features +{ + public class MethodInvocationFeatureCheckerContext + { + public MethodInfo Method { get; } + + public MethodInvocationFeatureCheckerContext(MethodInfo method) + { + Method = method; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/MethodInvocationFeatureCheckerService.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/MethodInvocationFeatureCheckerService.cs new file mode 100644 index 0000000000..da7becc61f --- /dev/null +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/MethodInvocationFeatureCheckerService.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Features +{ + public class MethodInvocationFeatureCheckerService : IMethodInvocationFeatureCheckerService, ITransientDependency + { + private readonly IFeatureChecker _featureChecker; + + public MethodInvocationFeatureCheckerService( + IFeatureChecker featureChecker) + { + _featureChecker = featureChecker; + } + + public async Task CheckAsync(MethodInvocationFeatureCheckerContext context) + { + if (IsFeatureCheckDisabled(context)) + { + return; + } + + foreach (var requiresFeatureAttribute in GetRequiredFeatureAttributes(context.Method)) + { + await _featureChecker.CheckEnabledAsync(requiresFeatureAttribute.RequiresAll, requiresFeatureAttribute.Features); + } + } + + protected virtual bool IsFeatureCheckDisabled(MethodInvocationFeatureCheckerContext context) + { + return context.Method + .GetCustomAttributes(true) + .OfType() + .Any(); + } + + protected virtual IEnumerable GetRequiredFeatureAttributes(MethodInfo methodInfo) + { + var attributes = methodInfo + .GetCustomAttributes(true) + .OfType(); + + if (methodInfo.IsPublic) + { + attributes = attributes + .Union( + methodInfo.DeclaringType + .GetCustomAttributes(true) + .OfType() + ); + } + + return attributes; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/NullFeatureStore.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/NullFeatureStore.cs index 2a4fcee0c6..d9b839a664 100644 --- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/NullFeatureStore.cs +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/NullFeatureStore.cs @@ -5,6 +5,7 @@ using Volo.Abp.DependencyInjection; namespace Volo.Abp.Features { + [Dependency(TryRegister = true)] public class NullFeatureStore : IFeatureStore, ISingletonDependency { public ILogger Logger { get; set; } diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/RequiresFeatureAttribute.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/RequiresFeatureAttribute.cs new file mode 100644 index 0000000000..2f5b62c128 --- /dev/null +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/RequiresFeatureAttribute.cs @@ -0,0 +1,33 @@ +using System; + +namespace Volo.Abp.Features +{ + /// + /// This attribute can be used on a class/method to declare that given class/method is available + /// only if required feature(s) are enabled. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public class RequiresFeatureAttribute : Attribute + { + /// + /// A list of features to be checked if they are enabled. + /// + public string[] Features { get; } + + /// + /// If this property is set to true, all of the must be enabled. + /// If it's false, at least one of the must be enabled. + /// Default: false. + /// + public bool RequiresAll { get; set; } + + /// + /// Creates a new instance of class. + /// + /// A list of features to be checked if they are enabled + public RequiresFeatureAttribute(params string[] features) + { + Features = features ?? Array.Empty(); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/TenantFeatureValueProvider.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/TenantFeatureValueProvider.cs index 73b07b6e88..211174516c 100644 --- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/TenantFeatureValueProvider.cs +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/TenantFeatureValueProvider.cs @@ -19,7 +19,7 @@ namespace Volo.Abp.Features public override async Task GetOrNullAsync(FeatureDefinition feature) { - return await FeatureStore.GetOrNullAsync(feature.Name, Name, CurrentTenant.Id?.ToString()); + return await FeatureStore.GetOrNullAsync(feature.Name, Name, CurrentTenant.Id?.ToString("N")); } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs index b108ca92f6..10d2c98126 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs @@ -35,7 +35,6 @@ namespace Volo.Abp.Http.Client.DynamicProxying protected IJsonSerializer JsonSerializer { get; } protected IRemoteServiceHttpClientAuthenticator ClientAuthenticator { get; } - public ILogger> Logger { get; set; } static DynamicHttpProxyInterceptor() diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpDictionaryBasedStringLocalizer.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpDictionaryBasedStringLocalizer.cs index c9428626a8..0cfe90d9ef 100644 --- a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpDictionaryBasedStringLocalizer.cs +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpDictionaryBasedStringLocalizer.cs @@ -8,7 +8,7 @@ using Microsoft.Extensions.Localization; namespace Volo.Abp.Localization { - public class AbpDictionaryBasedStringLocalizer : IStringLocalizer + public class AbpDictionaryBasedStringLocalizer : IStringLocalizer, IStringLocalizerSupportsInheritance { public LocalizationResource Resource { get; } @@ -26,7 +26,20 @@ namespace Volo.Abp.Localization public IEnumerable GetAllStrings(bool includeParentCultures) { - return GetAllStrings(CultureInfo.CurrentUICulture.Name, includeParentCultures); + return GetAllStrings( + CultureInfo.CurrentUICulture.Name, + includeParentCultures + ); + } + + + public IEnumerable GetAllStrings(bool includeParentCultures, bool includeBaseLocalizers) + { + return GetAllStrings( + CultureInfo.CurrentUICulture.Name, + includeParentCultures, + includeBaseLocalizers + ); } public IStringLocalizer WithCulture(CultureInfo culture) @@ -109,26 +122,32 @@ namespace Volo.Abp.Localization return null; } - protected virtual IReadOnlyList GetAllStrings(string cultureName, bool includeParentCultures = true) + protected virtual IReadOnlyList GetAllStrings( + string cultureName, + bool includeParentCultures = true, + bool includeBaseLocalizers = true) { //TODO: Can be optimized (example: if it's already default dictionary, skip overriding) var allStrings = new Dictionary(); - foreach (var baseLocalizer in BaseLocalizers.Select(l => l.WithCulture(CultureInfo.GetCultureInfo(cultureName)))) + if (includeBaseLocalizers) { - //TODO: Try/catch is a workaround here! - try + foreach (var baseLocalizer in BaseLocalizers.Select(l => l.WithCulture(CultureInfo.GetCultureInfo(cultureName)))) { - var baseLocalizedString = baseLocalizer.GetAllStrings(includeParentCultures); - foreach (var localizedString in baseLocalizedString) + //TODO: Try/catch is a workaround here! + try { - allStrings[localizedString.Name] = localizedString; + var baseLocalizedString = baseLocalizer.GetAllStrings(includeParentCultures); + foreach (var localizedString in baseLocalizedString) + { + allStrings[localizedString.Name] = localizedString; + } } - } - catch (MissingManifestResourceException) - { + catch (MissingManifestResourceException) + { + } } } @@ -160,7 +179,7 @@ namespace Volo.Abp.Localization : cultureName; } - public class CultureWrapperStringLocalizer : IStringLocalizer + public class CultureWrapperStringLocalizer : IStringLocalizer, IStringLocalizerSupportsInheritance { private readonly string _cultureName; private readonly AbpDictionaryBasedStringLocalizer _innerLocalizer; @@ -184,6 +203,11 @@ namespace Volo.Abp.Localization { return new CultureWrapperStringLocalizer(culture.Name, _innerLocalizer); } + + public IEnumerable GetAllStrings(bool includeParentCultures, bool includeBaseLocalizers) + { + return _innerLocalizer.GetAllStrings(includeParentCultures, includeBaseLocalizers); + } } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpLocalizationModule.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpLocalizationModule.cs index 3fcd90ac6d..23e61e417f 100644 --- a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpLocalizationModule.cs +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpLocalizationModule.cs @@ -1,4 +1,5 @@ -using Volo.Abp.Localization.Resources.AbpValidation; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Localization.Resources.AbpValidation; using Volo.Abp.Modularity; using Volo.Abp.Settings; using Volo.Abp.VirtualFileSystem; @@ -27,11 +28,6 @@ namespace Volo.Abp.Localization .Add("en") .AddVirtualJson("/Localization/Resources/AbpValidation"); }); - - Configure(options => - { - options.DefinitionProviders.Add(); - }); } } } diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpStringLocalizerExtensions.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpStringLocalizerExtensions.cs new file mode 100644 index 0000000000..218f9c4164 --- /dev/null +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpStringLocalizerExtensions.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; +using System.Reflection; +using JetBrains.Annotations; +using Microsoft.Extensions.Localization; +using Volo.Abp.DynamicProxy; +using Volo.Abp.Reflection; + +namespace Volo.Abp.Localization +{ + public static class AbpStringLocalizerExtensions + { + [NotNull] + public static IStringLocalizer GetInternalLocalizer( + [NotNull] this IStringLocalizer stringLocalizer) + { + Check.NotNull(stringLocalizer, nameof(stringLocalizer)); + + var localizerType = stringLocalizer.GetType(); + if (!ReflectionHelper.IsAssignableToGenericType(localizerType, typeof(StringLocalizer<>))) + { + return stringLocalizer; + } + + var localizerField = localizerType + .GetField( + "_localizer", + BindingFlags.Instance | + BindingFlags.NonPublic + ); + + if (localizerField == null) + { + throw new AbpException($"Could not find the _localizer field inside the {typeof(StringLocalizer<>).FullName} class. Probably its name has changed. Please report this issue to the ABP framework."); + } + + return localizerField.GetValue(stringLocalizer) as IStringLocalizer; + } + + public static IEnumerable GetAllStrings( + this IStringLocalizer stringLocalizer, + bool includeParentCultures, + bool includeBaseLocalizers) + { + var internalLocalizer = (ProxyHelper.UnProxy(stringLocalizer) as IStringLocalizer).GetInternalLocalizer(); + if (internalLocalizer is IStringLocalizerSupportsInheritance stringLocalizerSupportsInheritance) + { + return stringLocalizerSupportsInheritance.GetAllStrings( + includeParentCultures, + includeBaseLocalizers + ); + } + + return stringLocalizer.GetAllStrings( + includeParentCultures + ); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/IStringLocalizerSupportsInheritance.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/IStringLocalizerSupportsInheritance.cs new file mode 100644 index 0000000000..ccd1616a06 --- /dev/null +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/IStringLocalizerSupportsInheritance.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using Microsoft.Extensions.Localization; + +namespace Volo.Abp.Localization +{ + public interface IStringLocalizerSupportsInheritance + { + IEnumerable GetAllStrings(bool includeParentCultures, bool includeBaseLocalizers); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Security/System/Security/Principal/AbpClaimsIdentityExtensions.cs b/framework/src/Volo.Abp.Security/System/Security/Principal/AbpClaimsIdentityExtensions.cs index 45d363a7a7..694fb7baa6 100644 --- a/framework/src/Volo.Abp.Security/System/Security/Principal/AbpClaimsIdentityExtensions.cs +++ b/framework/src/Volo.Abp.Security/System/Security/Principal/AbpClaimsIdentityExtensions.cs @@ -91,5 +91,33 @@ namespace System.Security.Principal return clientIdOrNull.Value; } + + public static Guid? FindEditionId([NotNull] this ClaimsPrincipal principal) + { + Check.NotNull(principal, nameof(principal)); + + var editionIdOrNull = principal.Claims?.FirstOrDefault(c => c.Type == AbpClaimTypes.EditionId); + if (editionIdOrNull == null || editionIdOrNull.Value.IsNullOrWhiteSpace()) + { + return null; + } + + return Guid.Parse(editionIdOrNull.Value); + } + + public static Guid? FindEditionId([NotNull] this IIdentity identity) + { + Check.NotNull(identity, nameof(identity)); + + var claimsIdentity = identity as ClaimsIdentity; + + var editionIdOrNull = claimsIdentity?.Claims?.FirstOrDefault(c => c.Type == AbpClaimTypes.EditionId); + if (editionIdOrNull == null || editionIdOrNull.Value.IsNullOrWhiteSpace()) + { + return null; + } + + return Guid.Parse(editionIdOrNull.Value); + } } } diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationException.cs b/framework/src/Volo.Abp.Security/Volo/Abp/Authorization/AbpAuthorizationException.cs similarity index 100% rename from framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationException.cs rename to framework/src/Volo.Abp.Security/Volo/Abp/Authorization/AbpAuthorizationException.cs diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimTypes.cs b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimTypes.cs index b4abf97080..d0b883e4f3 100644 --- a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimTypes.cs +++ b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimTypes.cs @@ -44,10 +44,16 @@ namespace Volo.Abp.Security.Claims public static string PhoneNumberVerified { get; set; } = "phone_number_verified"; /// - /// Default: "phone_number_verified". + /// Default: "tenantid". /// public static string TenantId { get; set; } = "tenantid"; + + /// + /// Default: "editionid". + /// + public static string EditionId { get; set; } = "editionid"; + /// /// Default: "client_id". /// diff --git a/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/AbpSettingsModule.cs b/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/AbpSettingsModule.cs index d86f6ca524..1951ab61b5 100644 --- a/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/AbpSettingsModule.cs +++ b/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/AbpSettingsModule.cs @@ -1,4 +1,7 @@ -using Volo.Abp.Localization; +using System; +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.MultiTenancy; using Volo.Abp.Security; @@ -12,6 +15,11 @@ namespace Volo.Abp.Settings )] public class AbpSettingsModule : AbpModule { + public override void PreConfigureServices(ServiceConfigurationContext context) + { + AutoAddDefinitionProviders(context.Services); + } + public override void ConfigureServices(ServiceConfigurationContext context) { Configure(options => @@ -22,5 +30,23 @@ namespace Volo.Abp.Settings options.ValueProviders.Add(); }); } + + private static void AutoAddDefinitionProviders(IServiceCollection services) + { + var definitionProviders = new List(); + + services.OnRegistred(context => + { + if (typeof(ISettingDefinitionProvider).IsAssignableFrom(context.ImplementationType)) + { + definitionProviders.Add(context.ImplementationType); + } + }); + + services.Configure(options => + { + options.DefinitionProviders.AddIfNotContains(definitionProviders); + }); + } } } diff --git a/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/NullSettingStore.cs b/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/NullSettingStore.cs index 5906306e93..8699fae972 100644 --- a/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/NullSettingStore.cs +++ b/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/NullSettingStore.cs @@ -5,6 +5,7 @@ using Volo.Abp.DependencyInjection; namespace Volo.Abp.Settings { + [Dependency(TryRegister = true)] public class NullSettingStore : ISettingStore, ISingletonDependency { public ILogger Logger { get; set; } diff --git a/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionManager.cs b/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionManager.cs index 7241d3654d..387ea77d05 100644 --- a/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionManager.cs +++ b/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionManager.cs @@ -10,22 +10,19 @@ 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; + protected IServiceProvider ServiceProvider { get; } public SettingDefinitionManager( IOptions options, IServiceProvider serviceProvider) { - _serviceProvider = serviceProvider; + ServiceProvider = serviceProvider; Options = options.Value; - Providers = new Lazy>(CreateSettingProviders, true); SettingDefinitions = new Lazy>(CreateSettingDefinitions, true); } @@ -53,21 +50,21 @@ namespace Volo.Abp.Settings return SettingDefinitions.Value.GetOrDefault(name); } - protected virtual List CreateSettingProviders() - { - return Options - .DefinitionProviders - .Select(p => _serviceProvider.GetRequiredService(p) as ISettingDefinitionProvider) - .ToList(); - } - protected virtual IDictionary CreateSettingDefinitions() { var settings = new Dictionary(); - foreach (var provider in Providers.Value) + using (var scope = ServiceProvider.CreateScope()) { - provider.Define(new SettingDefinitionContext(settings)); + var providers = Options + .DefinitionProviders + .Select(p => scope.ServiceProvider.GetRequiredService(p) as ISettingDefinitionProvider) + .ToList(); + + foreach (var provider in providers) + { + provider.Define(new SettingDefinitionContext(settings)); + } } return settings; diff --git a/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionProvider.cs b/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionProvider.cs index 04e6fc7eb2..8cd4b28003 100644 --- a/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionProvider.cs +++ b/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionProvider.cs @@ -2,7 +2,7 @@ namespace Volo.Abp.Settings { - public abstract class SettingDefinitionProvider : ISettingDefinitionProvider, ISingletonDependency + public abstract class SettingDefinitionProvider : ISettingDefinitionProvider, ITransientDependency { public abstract void Define(ISettingDefinitionContext context); } 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/framework/src/Volo.Abp.UI.Navigation/Volo.Abp.UI.Navigation.csproj b/framework/src/Volo.Abp.UI.Navigation/Volo.Abp.UI.Navigation.csproj index 79955d3241..c218698017 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo.Abp.UI.Navigation.csproj +++ b/framework/src/Volo.Abp.UI.Navigation/Volo.Abp.UI.Navigation.csproj @@ -1,4 +1,4 @@ - + @@ -13,6 +13,11 @@ + + + + + diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/AbpUiNavigationModule.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/AbpUiNavigationModule.cs index c0934e0a4a..8f4be85af2 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/AbpUiNavigationModule.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/AbpUiNavigationModule.cs @@ -1,10 +1,32 @@ -using Volo.Abp.Modularity; +using Volo.Abp.Localization; +using Volo.Abp.Modularity; +using Volo.Abp.Ui.Navigation; +using Volo.Abp.Ui.Navigation.Localization.Resource; +using Volo.Abp.VirtualFileSystem; namespace Volo.Abp.UI.Navigation { [DependsOn(typeof(AbpUiModule))] public class AbpUiNavigationModule : AbpModule { + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.FileSets.AddEmbedded(); + }); + Configure(options => + { + options.Resources + .Add("en") + .AddVirtualJson("/Volo/Abp/Ui/Navigation/Localization/Resource"); + }); + + Configure(options => + { + options.MenuContributors.Add(new DefaultMenuContributor()); + }); + } } } diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenu.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenu.cs index fce412c22f..a40c67cdf6 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenu.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenu.cs @@ -1,5 +1,5 @@ -using System.Collections.Generic; using JetBrains.Annotations; +using Volo.Abp.Ui.Navigation; namespace Volo.Abp.UI.Navigation { @@ -29,11 +29,11 @@ namespace Volo.Abp.UI.Navigation /// [NotNull] - public IList Items { get; } //TODO: Create a specialized collection (that can contain AddAfter for example) + public ApplicationMenuItemList Items { get; } /// /// Can be used to store a custom object related to this menu. - /// TODO: Conver to dictionary! + /// TODO: Convert to dictionary! /// [CanBeNull] public object CustomData { get; set; } @@ -47,7 +47,7 @@ namespace Volo.Abp.UI.Navigation Name = name; DisplayName = displayName ?? Name; - Items = new List(); + Items = new ApplicationMenuItemList(); } /// @@ -60,5 +60,10 @@ namespace Volo.Abp.UI.Navigation Items.Add(menuItem); return this; } + + public override string ToString() + { + return $"[ApplicationMenu] Name = {Name}"; + } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuExtensions.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuExtensions.cs new file mode 100644 index 0000000000..e69471e15d --- /dev/null +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuExtensions.cs @@ -0,0 +1,59 @@ +using JetBrains.Annotations; +using System.Linq; +using Volo.Abp.Ui.Navigation; + +namespace Volo.Abp.UI.Navigation +{ + public static class ApplicationMenuExtensions + { + [NotNull] + public static ApplicationMenuItem GetAdministration( + [NotNull] this ApplicationMenu applicationMenu) + { + return applicationMenu.GetMenuItem( + DefaultMenuNames.Application.Main.Administration + ); + } + + [NotNull] + public static ApplicationMenuItem GetMenuItem( + [NotNull] this IHasMenuItems menuWithItems, + string menuItemName) + { + var menuItem = menuWithItems.GetMenuItemOrNull(menuItemName); + if (menuItem == null) + { + throw new AbpException($"Could not find a menu item with given name: {menuItemName}"); + } + + return menuItem; + } + + [CanBeNull] + public static ApplicationMenuItem GetMenuItemOrNull( + [NotNull] this IHasMenuItems menuWithItems, + string menuItemName) + { + Check.NotNull(menuWithItems, nameof(menuWithItems)); + + return menuWithItems.Items.FirstOrDefault(mi => mi.Name == menuItemName); + } + + [NotNull] + public static IHasMenuItems SetSubItemOrder( + [NotNull] this IHasMenuItems menuWithItems, + string menuItemName, + int order) + { + Check.NotNull(menuWithItems, nameof(menuWithItems)); + + var menuItem = menuWithItems.GetMenuItemOrNull(menuItemName); + if (menuItem != null) + { + menuItem.Order = order; + } + + return menuWithItems; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItem.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItem.cs index de19f6db7b..e09c065b00 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItem.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItem.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using JetBrains.Annotations; +using Volo.Abp.Ui.Navigation; namespace Volo.Abp.UI.Navigation { @@ -68,7 +69,7 @@ namespace Volo.Abp.UI.Navigation /// [NotNull] - public IList Items { get; } + public ApplicationMenuItemList Items { get; } /// /// Can be used to store a custom object related to this menu item. Optional. @@ -109,7 +110,7 @@ namespace Volo.Abp.UI.Navigation ElementId = elementId ?? GetDefaultElementId(); CssClass = cssClass; - Items = new List(); + Items = new ApplicationMenuItemList(); } /// @@ -127,5 +128,10 @@ namespace Volo.Abp.UI.Navigation { return "MenuItem_" + Name; } + + public override string ToString() + { + return $"[ApplicationMenuItem] Name = {Name}"; + } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItemList.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItemList.cs new file mode 100644 index 0000000000..0e9c845664 --- /dev/null +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItemList.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Volo.Abp.UI.Navigation; + +namespace Volo.Abp.Ui.Navigation +{ + public class ApplicationMenuItemList : List + { + public ApplicationMenuItemList() + { + + } + + public ApplicationMenuItemList(int capacity) + : base(capacity) + { + + } + + public ApplicationMenuItemList(IEnumerable collection) + : base(collection) + { + + } + + public void Normalize() + { + RemoveEmptyItems(); + Order(); + } + + private void RemoveEmptyItems() + { + RemoveAll(item => item.IsLeaf && item.Url.IsNullOrEmpty()); + } + + private void Order() + { + //TODO: Is there any way that is more performant? + var orderedItems = this.OrderBy(item => item.Order).ToArray(); + Clear(); + AddRange(orderedItems); + } + } +} diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/DefaultMenuContributor.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/DefaultMenuContributor.cs new file mode 100644 index 0000000000..c1f035e292 --- /dev/null +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/DefaultMenuContributor.cs @@ -0,0 +1,34 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; +using System.Threading.Tasks; +using Volo.Abp.Ui.Navigation.Localization.Resource; +using Volo.Abp.UI.Navigation; + +namespace Volo.Abp.Ui.Navigation +{ + public class DefaultMenuContributor : IMenuContributor + { + public virtual Task ConfigureMenuAsync(MenuConfigurationContext context) + { + Configure(context); + return Task.CompletedTask; + } + + protected virtual void Configure(MenuConfigurationContext context) + { + var l = context.ServiceProvider + .GetRequiredService>(); + + if (context.Menu.Name == StandardMenus.Main) + { + context.Menu.AddItem( + new ApplicationMenuItem( + DefaultMenuNames.Application.Main.Administration, + l["Menu:Administration"], + icon: "fa fa-wrench" + ) + ); + } + } + } +} diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/DefaultMenuNames.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/DefaultMenuNames.cs new file mode 100644 index 0000000000..96032f48fa --- /dev/null +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/DefaultMenuNames.cs @@ -0,0 +1,13 @@ +namespace Volo.Abp.Ui.Navigation +{ + public static class DefaultMenuNames + { + public static class Application + { + public static class Main + { + public const string Administration = "Abp.Application.Main.Administration"; + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/IHasMenuItems.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/IHasMenuItems.cs index adfa8f2f40..f33ddcd4c2 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/IHasMenuItems.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/IHasMenuItems.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using Volo.Abp.Ui.Navigation; namespace Volo.Abp.UI.Navigation { @@ -7,6 +7,6 @@ namespace Volo.Abp.UI.Navigation /// /// Menu items. /// - IList Items { get; } + ApplicationMenuItemList Items { get; } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/AbpUiNavigationResource.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/AbpUiNavigationResource.cs new file mode 100644 index 0000000000..96d09e9507 --- /dev/null +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/AbpUiNavigationResource.cs @@ -0,0 +1,10 @@ +using Volo.Abp.Localization; + +namespace Volo.Abp.Ui.Navigation.Localization.Resource +{ + [LocalizationResourceName("AbpUiNavigation")] + public class AbpUiNavigationResource + { + + } +} diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/en.json b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/en.json new file mode 100644 index 0000000000..96ca0c5603 --- /dev/null +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/en.json @@ -0,0 +1,6 @@ +{ + "culture": "en", + "texts": { + "Menu:Administration": "Administration" + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/tr.json b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/tr.json new file mode 100644 index 0000000000..67c6da0197 --- /dev/null +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/tr.json @@ -0,0 +1,6 @@ +{ + "culture": "tr", + "texts": { + "Menu:Administration": "Yönetim" + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/MenuManager.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/MenuManager.cs index 93b206a2b4..9c96006b02 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/MenuManager.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/MenuManager.cs @@ -1,7 +1,7 @@ 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; @@ -9,26 +9,26 @@ namespace Volo.Abp.UI.Navigation { public class MenuManager : IMenuManager, ITransientDependency { - private readonly NavigationOptions _options; - private readonly IServiceProvider _serviceProvider; + protected NavigationOptions Options { get; } + protected IHybridServiceScopeFactory ServiceScopeFactory { get; } public MenuManager( IOptions options, - IServiceProvider serviceProvider) + IHybridServiceScopeFactory serviceScopeFactory) { - _serviceProvider = serviceProvider; - _options = options.Value; + ServiceScopeFactory = serviceScopeFactory; + Options = options.Value; } public async Task GetAsync(string name) { var menu = new ApplicationMenu(name); - using (var scope = _serviceProvider.CreateScope()) + using (var scope = ServiceScopeFactory.CreateScope()) { var context = new MenuConfigurationContext(menu, scope.ServiceProvider); - foreach (var contributor in _options.MenuContributors) + foreach (var contributor in Options.MenuContributors) { await contributor.ConfigureMenuAsync(context); } @@ -39,9 +39,14 @@ namespace Volo.Abp.UI.Navigation return menu; } - protected virtual void NormalizeMenu(ApplicationMenu menu) + protected virtual void NormalizeMenu(IHasMenuItems menuWithItems) { - menu.Items.RemoveAll(item => item.IsLeaf && item.Url.IsNullOrEmpty()); + foreach (var menuItem in menuWithItems.Items) + { + NormalizeMenu(menuItem); + } + + menuWithItems.Items.Normalize(); } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/StandardMenus.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/StandardMenus.cs index b8d63a104d..57a8d7ee41 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/StandardMenus.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/StandardMenus.cs @@ -2,6 +2,12 @@ namespace Volo.Abp.UI.Navigation { public static class StandardMenus { + /* TODO: Consider to create nested class like + * StandardMenus.Application.Main + * StandardMenus.Application.User + * StandardMenus.Application.Shortcut + */ + public const string Main = "Main"; public const string User = "User"; public const string Shortcut = "Shortcut"; diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/AbpUiResource.cs b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/AbpUiResource.cs index 9eb90832e5..fb4600be6e 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/AbpUiResource.cs +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/AbpUiResource.cs @@ -2,6 +2,7 @@ namespace Localization.Resources.AbpUi { + //TODO: Move to the Volo.Abp.UI.Localization.Resource folder [LocalizationResourceName("AbpUi")] public class AbpUiResource { diff --git a/framework/src/Volo.Abp.UI/Volo.Abp.UI.csproj b/framework/src/Volo.Abp.UI/Volo.Abp.UI.csproj index 1a7ab915c9..8716d7a210 100644 --- a/framework/src/Volo.Abp.UI/Volo.Abp.UI.csproj +++ b/framework/src/Volo.Abp.UI/Volo.Abp.UI.csproj @@ -1,4 +1,4 @@ - + @@ -15,6 +15,7 @@ + diff --git a/framework/src/Volo.Abp.UI/Volo/Abp/Ui/AbpUiModule.cs b/framework/src/Volo.Abp.UI/Volo/Abp/Ui/AbpUiModule.cs index fb173ee882..0a9206c309 100644 --- a/framework/src/Volo.Abp.UI/Volo/Abp/Ui/AbpUiModule.cs +++ b/framework/src/Volo.Abp.UI/Volo/Abp/Ui/AbpUiModule.cs @@ -20,7 +20,9 @@ namespace Volo.Abp.UI Configure(options => { - options.Resources.Add("en").AddVirtualJson("/Localization/Resources/AbpUi"); + options.Resources + .Add("en") + .AddVirtualJson("/Localization/Resources/AbpUi"); }); } } diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/AlwaysValidValueValidator.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/AlwaysValidValueValidator.cs new file mode 100644 index 0000000000..ec444bd62b --- /dev/null +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/AlwaysValidValueValidator.cs @@ -0,0 +1,14 @@ +using System; + +namespace Volo.Abp.Validation.StringValues +{ + [Serializable] + [ValueValidator("NULL")] + public class AlwaysValidValueValidator : ValueValidatorBase + { + public override bool IsValid(object value) + { + return true; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/BooleanValueValidator.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/BooleanValueValidator.cs new file mode 100644 index 0000000000..41c52894cd --- /dev/null +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/BooleanValueValidator.cs @@ -0,0 +1,24 @@ +using System; + +namespace Volo.Abp.Validation.StringValues +{ + [Serializable] + [ValueValidator("BOOLEAN")] + public class BooleanValueValidator : ValueValidatorBase + { + public override bool IsValid(object value) + { + if (value == null) + { + return false; + } + + if (value is bool) + { + return true; + } + + return bool.TryParse(value.ToString(), out _); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/FreeTextStringValueType.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/FreeTextStringValueType.cs new file mode 100644 index 0000000000..c02f617b48 --- /dev/null +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/FreeTextStringValueType.cs @@ -0,0 +1,19 @@ +using System; + +namespace Volo.Abp.Validation.StringValues +{ + [Serializable] + [StringValueType("FREE_TEXT")] + public class FreeTextStringValueType : StringValueTypeBase + { + public FreeTextStringValueType() + { + + } + + public FreeTextStringValueType(IValueValidator validator) + : base(validator) + { + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/ISelectionStringValueItem.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/ISelectionStringValueItem.cs new file mode 100644 index 0000000000..1dd69158c7 --- /dev/null +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/ISelectionStringValueItem.cs @@ -0,0 +1,9 @@ +namespace Volo.Abp.Validation.StringValues +{ + public interface ISelectionStringValueItem + { + string Value { get; set; } + + LocalizableStringInfo DisplayText { get; set; } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/ISelectionStringValueItemSource.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/ISelectionStringValueItemSource.cs new file mode 100644 index 0000000000..563503a05d --- /dev/null +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/ISelectionStringValueItemSource.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Volo.Abp.Validation.StringValues +{ + public interface ISelectionStringValueItemSource + { + ICollection Items { get; } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/IStringValueType.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/IStringValueType.cs new file mode 100644 index 0000000000..674b6e55f9 --- /dev/null +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/IStringValueType.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using JetBrains.Annotations; + +namespace Volo.Abp.Validation.StringValues +{ + public interface IStringValueType + { + string Name { get; } + + [CanBeNull] + object this[string key] { get; set; } + + [NotNull] + Dictionary Properties { get; } + + IValueValidator Validator { get; set; } + } +} diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/IValueValidator.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/IValueValidator.cs new file mode 100644 index 0000000000..afcacb3e34 --- /dev/null +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/IValueValidator.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using JetBrains.Annotations; + +namespace Volo.Abp.Validation.StringValues +{ + public interface IValueValidator + { + string Name { get; } + + [CanBeNull] + object this[string key] { get; set; } + + [NotNull] + IDictionary Properties { get; } + + bool IsValid(object value); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/LocalizableSelectionStringValueItem.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/LocalizableSelectionStringValueItem.cs new file mode 100644 index 0000000000..716c9e0e1c --- /dev/null +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/LocalizableSelectionStringValueItem.cs @@ -0,0 +1,9 @@ +namespace Volo.Abp.Validation.StringValues +{ + public class LocalizableSelectionStringValueItem : ISelectionStringValueItem + { + public string Value { get; set; } + + public LocalizableStringInfo DisplayText { get; set; } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/LocalizableStringInfo.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/LocalizableStringInfo.cs new file mode 100644 index 0000000000..f4359c967f --- /dev/null +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/LocalizableStringInfo.cs @@ -0,0 +1,15 @@ +namespace Volo.Abp.Validation.StringValues +{ + public class LocalizableStringInfo + { + public string ResourceName { get; } + + public string Name { get; } + + public LocalizableStringInfo(string resourceName, string name) + { + ResourceName = resourceName; + Name = name; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/NumericValueValidator.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/NumericValueValidator.cs new file mode 100644 index 0000000000..e1b1972258 --- /dev/null +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/NumericValueValidator.cs @@ -0,0 +1,61 @@ +using System; + +namespace Volo.Abp.Validation.StringValues +{ + [Serializable] + [ValueValidator("NUMERIC")] + public class NumericValueValidator : ValueValidatorBase + { + public int MinValue + { + get => (this["MinValue"] ?? "0").To(); + set => this["MinValue"] = value; + } + + public int MaxValue + { + get => (this["MaxValue"] ?? "0").To(); + set => this["MaxValue"] = value; + } + + public NumericValueValidator() + { + + } + + public NumericValueValidator(int minValue = int.MinValue, int maxValue = int.MaxValue) + { + MinValue = minValue; + MaxValue = maxValue; + } + + public override bool IsValid(object value) + { + if (value == null) + { + return false; + } + + if (value is int) + { + return IsValidInternal((int)value); + } + + if (value is string) + { + int intValue; + if (int.TryParse(value as string, out intValue)) + { + return IsValidInternal(intValue); + } + } + + return false; + } + + protected virtual bool IsValidInternal(int value) + { + return value.IsBetween(MinValue, MaxValue); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/SelectionStringValueType.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/SelectionStringValueType.cs new file mode 100644 index 0000000000..e2d4ecbc99 --- /dev/null +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/SelectionStringValueType.cs @@ -0,0 +1,22 @@ +using System; + +namespace Volo.Abp.Validation.StringValues +{ + [Serializable] + [StringValueType("SELECTION")] + public class SelectionStringValueType : StringValueTypeBase + { + public ISelectionStringValueItemSource ItemSource { get; set; } + + public SelectionStringValueType() + { + + } + + public SelectionStringValueType(IValueValidator validator) + : base(validator) + { + + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/StaticSelectionStringValueItemSource.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/StaticSelectionStringValueItemSource.cs new file mode 100644 index 0000000000..de0fed9879 --- /dev/null +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/StaticSelectionStringValueItemSource.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace Volo.Abp.Validation.StringValues +{ + public class StaticSelectionStringValueItemSource : ISelectionStringValueItemSource + { + public ICollection Items { get; } + + public StaticSelectionStringValueItemSource(params ISelectionStringValueItem[] items) + { + Items = Check.NotNullOrEmpty(items, nameof(items)); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/StringValueTypeAttribute.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/StringValueTypeAttribute.cs new file mode 100644 index 0000000000..5fa8bef1ae --- /dev/null +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/StringValueTypeAttribute.cs @@ -0,0 +1,27 @@ +using System; +using System.Linq; +using System.Reflection; + +namespace Volo.Abp.Validation.StringValues +{ + [AttributeUsage(AttributeTargets.Class)] + public class StringValueTypeAttribute : Attribute + { + public string Name { get; set; } + + public StringValueTypeAttribute(string name) + { + Name = name; + } + + public static string GetName(Type type) + { + if (type.IsDefined(typeof(StringValueTypeAttribute))) + { + return type.GetCustomAttributes(typeof(StringValueTypeAttribute)).Cast().First().Name; + } + + return type.Name; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/StringValueTypeBase.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/StringValueTypeBase.cs new file mode 100644 index 0000000000..c6fd80fc13 --- /dev/null +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/StringValueTypeBase.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; + +namespace Volo.Abp.Validation.StringValues +{ + public abstract class StringValueTypeBase : IStringValueType + { + public virtual string Name => ValueValidatorAttribute.GetName(GetType()); + + public object this[string key] + { + get => Properties.GetOrDefault(key); + set => Properties[key] = value; + } + + public Dictionary Properties { get; } + + public IValueValidator Validator { get; set; } + + protected StringValueTypeBase() + : this(new AlwaysValidValueValidator()) + { + + } + + protected StringValueTypeBase(IValueValidator validator) + { + Validator = validator; + Properties = new Dictionary(); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/StringValueValidator.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/StringValueValidator.cs new file mode 100644 index 0000000000..3edc176b0c --- /dev/null +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/StringValueValidator.cs @@ -0,0 +1,79 @@ +using System; +using System.Text.RegularExpressions; + +namespace Volo.Abp.Validation.StringValues +{ + [Serializable] + [ValueValidator("STRING")] + public class StringValueValidator : ValueValidatorBase + { + public bool AllowNull + { + get => (this["AllowNull"] ?? "false").To(); + set => this["AllowNull"] = value.ToString().ToLowerInvariant(); + } + + public int MinLength + { + get => (this["MinLength"] ?? "0").To(); + set => this["MinLength"] = value; + } + + public int MaxLength + { + get => (this["MaxLength"] ?? "0").To(); + set => this["MaxLength"] = value; + } + + public string RegularExpression + { + get => this["RegularExpression"] as string; + set => this["RegularExpression"] = value; + } + + public StringValueValidator() + { + + } + + public StringValueValidator(int minLength = 0, int maxLength = 0, string regularExpression = null, bool allowNull = false) + { + MinLength = minLength; + MaxLength = maxLength; + RegularExpression = regularExpression; + AllowNull = allowNull; + } + + public override bool IsValid(object value) + { + if (value == null) + { + return AllowNull; + } + + if (!(value is string)) + { + return false; + } + + var strValue = value as string; + + if (MinLength > 0 && strValue.Length < MinLength) + { + return false; + } + + if (MaxLength > 0 && strValue.Length > MaxLength) + { + return false; + } + + if (!RegularExpression.IsNullOrEmpty()) + { + return Regex.IsMatch(strValue, RegularExpression); + } + + return true; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/ToggleStringValueType.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/ToggleStringValueType.cs new file mode 100644 index 0000000000..a262630766 --- /dev/null +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/ToggleStringValueType.cs @@ -0,0 +1,21 @@ +using System; + +namespace Volo.Abp.Validation.StringValues +{ + [Serializable] + [StringValueType("TOGGLE")] + public class ToggleStringValueType : StringValueTypeBase + { + public ToggleStringValueType() + : this(new BooleanValueValidator()) + { + + } + + public ToggleStringValueType(IValueValidator validator) + : base(validator) + { + + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/ValueValidatorAttribute.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/ValueValidatorAttribute.cs new file mode 100644 index 0000000000..6f40ad0651 --- /dev/null +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/ValueValidatorAttribute.cs @@ -0,0 +1,27 @@ +using System; +using System.Linq; +using System.Reflection; + +namespace Volo.Abp.Validation.StringValues +{ + [AttributeUsage(AttributeTargets.Class)] + public class ValueValidatorAttribute : Attribute + { + public string Name { get; set; } + + public ValueValidatorAttribute(string name) + { + Name = name; + } + + public static string GetName(Type type) + { + if (type.IsDefined(typeof(ValueValidatorAttribute))) + { + return type.GetCustomAttributes(typeof(ValueValidatorAttribute)).Cast().First().Name; + } + + return type.Name; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/ValueValidatorBase.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/ValueValidatorBase.cs new file mode 100644 index 0000000000..ee91089fd4 --- /dev/null +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/ValueValidatorBase.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; + +namespace Volo.Abp.Validation.StringValues +{ + [Serializable] + public abstract class ValueValidatorBase : IValueValidator + { + public virtual string Name => ValueValidatorAttribute.GetName(GetType()); + + public object this[string key] + { + get => Properties.GetOrDefault(key); + set => Properties[key] = value; + } + + public IDictionary Properties { get; } + + protected ValueValidatorBase() + { + Properties = new Dictionary(); + } + + public abstract bool IsValid(object value); + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs index b079b38188..eb0fc1ef48 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs @@ -6,7 +6,6 @@ using Volo.Abp.AspNetCore.Mvc.Authorization; using Volo.Abp.AspNetCore.Mvc.Localization; using Volo.Abp.AspNetCore.Mvc.Localization.Resource; using Volo.Abp.AspNetCore.TestBase; -using Volo.Abp.Authorization.Permissions; using Volo.Abp.Autofac; using Volo.Abp.Localization; using Volo.Abp.Localization.Resources.AbpValidation; @@ -57,11 +56,6 @@ namespace Volo.Abp.AspNetCore.Mvc }); }); - Configure(options => - { - options.DefinitionProviders.Add(); - }); - Configure(options => { options.FileSets.AddEmbedded(); diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FakeFeatureChecker.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FakeFeatureChecker.cs new file mode 100644 index 0000000000..04e7fa908a --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FakeFeatureChecker.cs @@ -0,0 +1,27 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.Features; + +namespace Volo.Abp.AspNetCore.Mvc.Features +{ + public class FakeFeatureChecker : FeatureCheckerBase + { + public override Task GetOrNullAsync(string name) + { + return Task.FromResult(GetOrNull(name)); + } + + private static string GetOrNull(string name) + { + switch (name) + { + case "AllowedFeature": + return true.ToString(); + case "NotAllowedFeature": + return null; //or false, doesn't matter + } + + throw new ApplicationException($"Unknown feature: '{name}'"); + } + } +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FeatureTestController.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FeatureTestController.cs new file mode 100644 index 0000000000..1d2549323d --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FeatureTestController.cs @@ -0,0 +1,33 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Features; + +namespace Volo.Abp.AspNetCore.Mvc.Features +{ + [Route("api/feature-test")] + public class FeatureTestController : AbpController + { + [HttpGet] + [Route("allowed-feature")] + [RequiresFeature("AllowedFeature")] + public Task AllowedFeatureAsync() + { + return Task.CompletedTask; + } + + [HttpGet] + [Route("not-allowed-feature")] + [RequiresFeature("NotAllowedFeature")] + public void NotAllowedFeature() + { + + } + + [HttpGet] + [Route("no-feature")] + public int NoFeature() + { + return 42; + } + } +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FeatureTestController_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FeatureTestController_Tests.cs new file mode 100644 index 0000000000..637dbe3d2b --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FeatureTestController_Tests.cs @@ -0,0 +1,34 @@ +using System.Net; +using System.Threading.Tasks; +using Xunit; + +namespace Volo.Abp.AspNetCore.Mvc.Features +{ + public class FeatureTestController_Tests : AspNetCoreMvcTestBase + { + [Fact] + public async Task Should_Allow_Enabled_Features() + { + await GetResponseAsStringAsync( + "/api/feature-test/allowed-feature" + ); + } + + [Fact] + public async Task Should_Not_Allow_Not_Enabled_Features() + { + await GetResponseAsStringAsync( + "/api/feature-test/not-allowed-feature", + HttpStatusCode.Unauthorized + ); + } + + [Fact] + public async Task Should_Allow_Actions_With_No_Feature() + { + await GetResponseAsStringAsync( + "/api/feature-test/no-feature" + ); + } + } +} diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AbpAuthorizationTestModule.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AbpAuthorizationTestModule.cs index 9b539e85be..51337ae871 100644 --- a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AbpAuthorizationTestModule.cs +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AbpAuthorizationTestModule.cs @@ -1,5 +1,4 @@ using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.Authorization.Permissions; using Volo.Abp.Authorization.TestServices; using Volo.Abp.Autofac; using Volo.Abp.Modularity; @@ -20,13 +19,5 @@ namespace Volo.Abp.Authorization } }); } - - public override void ConfigureServices(ServiceConfigurationContext context) - { - Configure(options => - { - options.DefinitionProviders.TryAdd(); - }); - } } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.Core.Tests/System/Collections/Generic/AbpListExtensions_Tests.cs b/framework/test/Volo.Abp.Core.Tests/System/Collections/Generic/AbpListExtensions_Tests.cs index cc65099a77..93e2ef5824 100644 --- a/framework/test/Volo.Abp.Core.Tests/System/Collections/Generic/AbpListExtensions_Tests.cs +++ b/framework/test/Volo.Abp.Core.Tests/System/Collections/Generic/AbpListExtensions_Tests.cs @@ -11,6 +11,29 @@ namespace System.Collections.Generic { var list = Enumerable.Range(1, 3).ToList(); + list.InsertAfter(2, 42); + + list.Count.ShouldBe(4); + list[0].ShouldBe(1); + list[1].ShouldBe(2); + list[2].ShouldBe(42); + list[3].ShouldBe(3); + + list.InsertAfter(3, 43); + + list.Count.ShouldBe(5); + list[0].ShouldBe(1); + list[1].ShouldBe(2); + list[2].ShouldBe(42); + list[3].ShouldBe(3); + list[4].ShouldBe(43); + } + + [Fact] + public void InsertAfter_With_Predicate() + { + var list = Enumerable.Range(1, 3).ToList(); + list.InsertAfter(i => i == 2, 42); list.Count.ShouldBe(4); @@ -30,7 +53,7 @@ namespace System.Collections.Generic } [Fact] - public void InsertAfter_Should_Insert_To_First_If_Not_Found() + public void InsertAfter_With_Predicate_Should_Insert_To_First_If_Not_Found() { var list = Enumerable.Range(1, 3).ToList(); @@ -48,6 +71,29 @@ namespace System.Collections.Generic { var list = Enumerable.Range(1, 3).ToList(); + list.InsertBefore(2, 42); + + list.Count.ShouldBe(4); + list[0].ShouldBe(1); + list[1].ShouldBe(42); + list[2].ShouldBe(2); + list[3].ShouldBe(3); + + list.InsertBefore(1, 43); + + list.Count.ShouldBe(5); + list[0].ShouldBe(43); + list[1].ShouldBe(1); + list[2].ShouldBe(42); + list[3].ShouldBe(2); + list[4].ShouldBe(3); + } + + [Fact] + public void InsertBefore_With_Predicate() + { + var list = Enumerable.Range(1, 3).ToList(); + list.InsertBefore(i => i == 2, 42); list.Count.ShouldBe(4); diff --git a/framework/test/Volo.Abp.Features.Tests/Volo.Abp.Features.Tests.csproj b/framework/test/Volo.Abp.Features.Tests/Volo.Abp.Features.Tests.csproj new file mode 100644 index 0000000000..3632aa2bf3 --- /dev/null +++ b/framework/test/Volo.Abp.Features.Tests/Volo.Abp.Features.Tests.csproj @@ -0,0 +1,22 @@ + + + + netcoreapp2.2 + latest + Volo.Abp.Features.Tests + Volo.Abp.Features.Tests + true + false + false + false + + + + + + + + + + + diff --git a/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/AbpFeaturesTestModule.cs b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/AbpFeaturesTestModule.cs new file mode 100644 index 0000000000..7b0927b80d --- /dev/null +++ b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/AbpFeaturesTestModule.cs @@ -0,0 +1,18 @@ +using Volo.Abp.Autofac; +using Volo.Abp.Modularity; + +namespace Volo.Abp.Features +{ + [DependsOn( + typeof(AbpFeaturesModule), + typeof(AbpTestBaseModule), + typeof(AbpAutofacModule) + )] + public class AbpFeaturesTestModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + + } + } +} diff --git a/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/ClassFeatureTestService.cs b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/ClassFeatureTestService.cs new file mode 100644 index 0000000000..425f69337f --- /dev/null +++ b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/ClassFeatureTestService.cs @@ -0,0 +1,23 @@ +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Features +{ + [RequiresFeature("BooleanTestFeature1")] + public class ClassFeatureTestService : ITransientDependency + { + /* Since this class is used with the class reference, + * need to virtual keywords, otherwise dynamic proxy can not work. + */ + + [RequiresFeature("BooleanTestFeature2")] + public virtual int Feature2() + { + return 42; + } + + public virtual void NoAdditionalFeature() + { + + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/FeatureChecker_Tests.cs b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/FeatureChecker_Tests.cs new file mode 100644 index 0000000000..7ea01268a0 --- /dev/null +++ b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/FeatureChecker_Tests.cs @@ -0,0 +1,53 @@ +using Shouldly; +using System.Threading.Tasks; +using Volo.Abp.MultiTenancy; +using Xunit; + +namespace Volo.Abp.Features +{ + public class FeatureChecker_Tests : FeatureTestBase + { + private readonly IFeatureChecker _featureChecker; + private readonly ICurrentTenant _currentTenant; + + public FeatureChecker_Tests() + { + _featureChecker = GetRequiredService(); + _currentTenant = GetRequiredService(); + } + + [Fact] + public async Task IsEnabledAsync() + { + //Tenant is unknown + (await _featureChecker.IsEnabledAsync("BooleanTestFeature1")).ShouldBeFalse(); + + using (_currentTenant.Change(TestFeatureStore.Tenant1Id)) + { + (await _featureChecker.IsEnabledAsync("BooleanTestFeature1")).ShouldBeTrue(); + } + + using (_currentTenant.Change(TestFeatureStore.Tenant2Id)) + { + (await _featureChecker.IsEnabledAsync("BooleanTestFeature1")).ShouldBeFalse(); + } + } + + [Fact] + public async Task GetOrNullAsync() + { + //Tenant is unknown + (await _featureChecker.GetOrNullAsync("IntegerTestFeature1")).ShouldBe("1"); + + using (_currentTenant.Change(TestFeatureStore.Tenant1Id)) + { + (await _featureChecker.GetOrNullAsync("IntegerTestFeature1")).ShouldBe("1"); + } + + using (_currentTenant.Change(TestFeatureStore.Tenant2Id)) + { + (await _featureChecker.GetOrNullAsync("IntegerTestFeature1")).ShouldBe("34"); + } + } + } +} diff --git a/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/FeatureDefinitionManager_Tests.cs b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/FeatureDefinitionManager_Tests.cs new file mode 100644 index 0000000000..5a4bbc9ea1 --- /dev/null +++ b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/FeatureDefinitionManager_Tests.cs @@ -0,0 +1,35 @@ +using Shouldly; +using Xunit; + +namespace Volo.Abp.Features +{ + public class FeatureDefinitionManager_Tests : FeatureTestBase + { + private readonly IFeatureDefinitionManager _featureDefinitionManager; + + public FeatureDefinitionManager_Tests() + { + _featureDefinitionManager = GetRequiredService(); + } + + [Fact] + public void Should_Get_Defined_Features() + { + _featureDefinitionManager.GetOrNull("BooleanTestFeature1").ShouldNotBeNull(); + _featureDefinitionManager.Get("BooleanTestFeature1").Name.ShouldBe("BooleanTestFeature1"); + + _featureDefinitionManager.GetOrNull("IntegerTestFeature1").ShouldNotBeNull(); + _featureDefinitionManager.Get("IntegerTestFeature1").Name.ShouldBe("IntegerTestFeature1"); + } + + [Fact] + public void Should_Not_Get_Undefined_Features() + { + _featureDefinitionManager.GetOrNull("UndefinedFeature").ShouldBeNull(); + Assert.Throws(() => + { + _featureDefinitionManager.Get("UndefinedFeature"); + }); + } + } +} diff --git a/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/FeatureInterceptor_Tests.cs b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/FeatureInterceptor_Tests.cs new file mode 100644 index 0000000000..159e2ff241 --- /dev/null +++ b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/FeatureInterceptor_Tests.cs @@ -0,0 +1,76 @@ +using Shouldly; +using System; +using System.Threading.Tasks; +using Volo.Abp.Authorization; +using Volo.Abp.MultiTenancy; +using Xunit; + +namespace Volo.Abp.Features +{ + public class FeatureInterceptor_Tests : FeatureTestBase + { + private readonly ClassFeatureTestService _classFeatureTestService; + private readonly IMethodFeatureTestService _methodFeatureTestService; + private readonly ICurrentTenant _currentTenant; + + public FeatureInterceptor_Tests() + { + _classFeatureTestService = GetRequiredService(); + _methodFeatureTestService = GetRequiredService(); + _currentTenant = GetRequiredService(); + } + + [Theory] + [InlineData(null)] //Features were not enabled for null tenantid + [InlineData(TestFeatureStore.Tenant2IdValue)] //Features were not enabled for Tenant 2 + public async Task Should_Not_Allow_To_Method_Calls_If_Related_Features_Were_Not_Enabled(string tenantIdValue) + { + using (_currentTenant.Change(ParseNullableGuid(tenantIdValue))) + { + Assert.Throws(() => + { + _classFeatureTestService.NoAdditionalFeature(); + }); + + Assert.Throws(() => + { + _classFeatureTestService.Feature2(); + }); + + await Assert.ThrowsAsync(async () => + { + await _methodFeatureTestService.Feature1Async(); + }); + } + } + + [Fact] + public async Task Should_Allow_To_Method_Calls_If_Related_Features_Were_Enabled() + { + //Features were enabled for Tenant 1 + using (_currentTenant.Change(TestFeatureStore.Tenant1Id)) + { + _classFeatureTestService.NoAdditionalFeature(); + _classFeatureTestService.Feature2().ShouldBe(42); + (await _methodFeatureTestService.Feature1Async()).ShouldBe(42); + } + } + + [Theory] + [InlineData(null)] + [InlineData(TestFeatureStore.Tenant1IdValue)] + [InlineData(TestFeatureStore.Tenant2IdValue)] + public async Task Should_Allow_To_Method_Calls_For_Those_Have_No_RequiresFeature_Attributes(string tenantIdValue) + { + using (_currentTenant.Change(ParseNullableGuid(tenantIdValue))) + { + await _methodFeatureTestService.NonFeatureAsync(); + } + } + + private static Guid? ParseNullableGuid(string tenantIdValue) + { + return tenantIdValue == null ? (Guid?)null : new Guid(tenantIdValue); + } + } +} diff --git a/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/FeatureTestBase.cs b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/FeatureTestBase.cs new file mode 100644 index 0000000000..b12aa9f526 --- /dev/null +++ b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/FeatureTestBase.cs @@ -0,0 +1,10 @@ +namespace Volo.Abp.Features +{ + public class FeatureTestBase : AbpIntegratedTest + { + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + } +} diff --git a/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/IMethodFeatureTestService.cs b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/IMethodFeatureTestService.cs new file mode 100644 index 0000000000..7bc16b7e04 --- /dev/null +++ b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/IMethodFeatureTestService.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; + +namespace Volo.Abp.Features +{ + public interface IMethodFeatureTestService + { + Task Feature1Async(); + + Task NonFeatureAsync(); + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/MethodFeatureTestService.cs b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/MethodFeatureTestService.cs new file mode 100644 index 0000000000..e627e0c5f1 --- /dev/null +++ b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/MethodFeatureTestService.cs @@ -0,0 +1,23 @@ +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Features +{ + public class MethodFeatureTestService : ITransientDependency, IMethodFeatureTestService + { + /* Since this class is used over an interface (IMethodFeatureTestService), + * no need to virtual keywords, dynamic proxy can work. + */ + + [RequiresFeature("BooleanTestFeature1")] + public Task Feature1Async() + { + return Task.FromResult(42); + } + + public Task NonFeatureAsync() + { + return Task.CompletedTask; + } + } +} diff --git a/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/TestFeatureDefinitionProvider.cs b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/TestFeatureDefinitionProvider.cs new file mode 100644 index 0000000000..0554120309 --- /dev/null +++ b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/TestFeatureDefinitionProvider.cs @@ -0,0 +1,13 @@ +namespace Volo.Abp.Features +{ + public class TestFeatureDefinitionProvider : FeatureDefinitionProvider + { + public override void Define(IFeatureDefinitionContext context) + { + var group = context.AddGroup("Test Group"); + group.AddFeature("BooleanTestFeature1"); + group.AddFeature("BooleanTestFeature2"); + group.AddFeature("IntegerTestFeature1", defaultValue: "1"); + } + } +} diff --git a/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/TestFeatureStore.cs b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/TestFeatureStore.cs new file mode 100644 index 0000000000..4592a18365 --- /dev/null +++ b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/TestFeatureStore.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Features +{ + public class TestFeatureStore : IFeatureStore, ISingletonDependency + { + public const string Tenant1IdValue = "f460fcf7-f944-469a-967b-3b2463323dfe"; + public const string Tenant2IdValue = "e10428ad-4608-4c34-a304-6f82502156f2"; + + public static Guid Tenant1Id = new Guid(Tenant1IdValue); + public static Guid Tenant2Id = new Guid(Tenant2IdValue); + + private readonly List _settingRecords; + + public TestFeatureStore() + { + _settingRecords = new List + { + new SettingRecord("BooleanTestFeature1", TenantFeatureValueProvider.ProviderName, Tenant1Id.ToString("N"), "true"), + new SettingRecord("BooleanTestFeature2", TenantFeatureValueProvider.ProviderName, Tenant1Id.ToString("N"), "true"), + new SettingRecord("IntegerTestFeature1", TenantFeatureValueProvider.ProviderName, Tenant2Id.ToString("N"), "34") + }; + } + + public Task GetOrNullAsync(string name, string providerName, string providerKey) + { + return Task.FromResult( + _settingRecords.FirstOrDefault(sr => + sr.Name == name && + sr.ProviderName == providerName && + sr.ProviderKey == providerKey + )?.Value + ); + } + + private class SettingRecord + { + public string Name { get; } + public string ProviderName { get; } + public string ProviderKey { get; } + public string Value { get; } + + public SettingRecord(string name, string providerName, string providerKey, string value) + { + Name = name; + ProviderName = providerName; + ProviderKey = providerKey; + Value = value; + } + } + } +} diff --git a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/AbpLocalization_Tests.cs b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/AbpLocalization_Tests.cs index ea6ce3e242..309e317997 100644 --- a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/AbpLocalization_Tests.cs +++ b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/AbpLocalization_Tests.cs @@ -1,4 +1,5 @@ using System.Globalization; +using System.Linq; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; using Shouldly; @@ -14,10 +15,28 @@ namespace Volo.Abp.Localization public class AbpLocalization_Tests : AbpIntegratedTest { private readonly IStringLocalizer _localizer; + private readonly IStringLocalizerFactory _localizerFactory; public AbpLocalization_Tests() { _localizer = GetRequiredService>(); + _localizerFactory = GetRequiredService(); + } + + [Fact] + public void AbpStringLocalizerExtensions_GetInternalLocalizer() + { + var internalLocalizer = _localizer.GetInternalLocalizer(); + internalLocalizer.ShouldNotBeNull(); + internalLocalizer.ShouldBeOfType(); + } + + [Fact] + public void AbpStringLocalizerExtensions_GetInternalLocalizer_Using_LocalizerFactory() + { + var internalLocalizer = _localizerFactory.Create(typeof(LocalizationTestResource)).GetInternalLocalizer(); + internalLocalizer.ShouldNotBeNull(); + internalLocalizer.ShouldBeOfType(); } [Fact] @@ -104,6 +123,102 @@ namespace Volo.Abp.Localization _localizer.WithCulture(CultureInfo.GetCultureInfo("tr"))["CarPlural"].Value.ShouldBe("Araba"); } + [Fact] + public void GetAllStrings_With_Parents() + { + using (AbpCultureHelper.Use("tr")) + { + var localizedStrings = _localizer.GetAllStrings(true).ToList(); + + localizedStrings.ShouldContain( + ls => ls.Name == "FortyTwo" && + ls.Value == "Forty Two" && + ls.ResourceNotFound == false + ); + + localizedStrings.ShouldContain( + ls => ls.Name == "Universe" && + ls.Value == "Evren" && + ls.ResourceNotFound == false + ); + } + } + + [Fact] + public void GetAllStrings_Without_Parents() + { + using (AbpCultureHelper.Use("tr")) + { + var localizedStrings = _localizer.GetAllStrings(false).ToList(); + + localizedStrings.ShouldNotContain( + ls => ls.Name == "FortyTwo" + ); + + localizedStrings.ShouldContain( + ls => ls.Name == "Universe" && + ls.Value == "Evren" && + ls.ResourceNotFound == false + ); + } + } + + [Fact] + public void GetAllStrings_With_Inheritance() + { + using (AbpCultureHelper.Use("tr")) + { + var localizedStrings = _localizer + .GetAllStrings(true, includeBaseLocalizers: true) + .ToList(); + + localizedStrings.ShouldContain( + ls => ls.Name == "USA" && + ls.Value == "Amerika Birleşik Devletleri" && + ls.ResourceNotFound == false + ); + + localizedStrings.ShouldContain( + ls => ls.Name == "Universe" && + ls.Value == "Evren" && + ls.ResourceNotFound == false + ); + + localizedStrings.ShouldContain( + ls => ls.Name == "SeeYou" && + ls.Value == "See you" && + ls.ResourceNotFound == false + ); + } + } + + [Fact] + public void GetAllStrings_Without_Inheritance() + { + using (AbpCultureHelper.Use("tr")) + { + var localizedStrings = _localizer + .GetAllStrings(true, includeBaseLocalizers: false) + .ToList(); + + localizedStrings.ShouldNotContain( + ls => ls.Name == "USA" + ); + + localizedStrings.ShouldContain( + ls => ls.Name == "Universe" && + ls.Value == "Evren" && + ls.ResourceNotFound == false + ); + + localizedStrings.ShouldContain( + ls => ls.Name == "SeeYou" && + ls.Value == "See you" && + ls.ResourceNotFound == false + ); + } + } + [DependsOn(typeof(AbpTestBaseModule))] [DependsOn(typeof(AbpLocalizationModule))] public class TestModule : AbpModule @@ -117,10 +232,21 @@ namespace Volo.Abp.Localization Configure(options => { - options.Resources.Add("en").AddVirtualJson("/Volo/Abp/Localization/TestResources/Base/Validation"); - options.Resources.Add("en").AddVirtualJson("/Volo/Abp/Localization/TestResources/Base/CountryNames"); - options.Resources.Add("en").AddVirtualJson("/Volo/Abp/Localization/TestResources/Source"); - options.Resources.Get().AddVirtualJson("/Volo/Abp/Localization/TestResources/SourceExt"); + options.Resources + .Add("en") + .AddVirtualJson("/Volo/Abp/Localization/TestResources/Base/Validation"); + + options.Resources + .Add("en") + .AddVirtualJson("/Volo/Abp/Localization/TestResources/Base/CountryNames"); + + options.Resources + .Add("en") + .AddVirtualJson("/Volo/Abp/Localization/TestResources/Source"); + + options.Resources + .Get() + .AddVirtualJson("/Volo/Abp/Localization/TestResources/SourceExt"); }); } } diff --git a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/LocalizationTestResource.cs b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/LocalizationTestResource.cs index 04cacc6b20..9344085adf 100644 --- a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/LocalizationTestResource.cs +++ b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/LocalizationTestResource.cs @@ -3,8 +3,10 @@ using Volo.Abp.Localization.TestResources.Base.Validation; namespace Volo.Abp.Localization.TestResources.Source { - [InheritResource(typeof(LocalizationTestValidationResource))] - [InheritResource(typeof(LocalizationTestCountryNamesResource))] + [InheritResource( + typeof(LocalizationTestValidationResource), + typeof(LocalizationTestCountryNamesResource) + )] public sealed class LocalizationTestResource { diff --git a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/en.json b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/en.json index 0388b6efe2..098b3655ee 100644 --- a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/en.json +++ b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/en.json @@ -5,6 +5,7 @@ "Car": "Car", "CarPlural": "Cars", "MaxLenghtErrorMessage": "This field's length can be maximum of '{0}' chars", - "Universe": "Universe" + "Universe": "Universe", + "FortyTwo": "Forty Two" } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.Settings.Tests/Volo.Abp.Settings.Tests.csproj b/framework/test/Volo.Abp.Settings.Tests/Volo.Abp.Settings.Tests.csproj index 47c93c6bf4..7f83c36c9a 100644 --- a/framework/test/Volo.Abp.Settings.Tests/Volo.Abp.Settings.Tests.csproj +++ b/framework/test/Volo.Abp.Settings.Tests/Volo.Abp.Settings.Tests.csproj @@ -13,6 +13,7 @@ + diff --git a/framework/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/AbpSettingsTestModule.cs b/framework/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/AbpSettingsTestModule.cs index dc4ab057a0..09a50daa5d 100644 --- a/framework/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/AbpSettingsTestModule.cs +++ b/framework/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/AbpSettingsTestModule.cs @@ -1,8 +1,10 @@ -using Volo.Abp.Modularity; +using Volo.Abp.Autofac; +using Volo.Abp.Modularity; namespace Volo.Abp.Settings { [DependsOn( + typeof(AbpAutofacModule), typeof(AbpSettingsModule), typeof(AbpTestBaseModule) )] @@ -13,7 +15,6 @@ namespace Volo.Abp.Settings Configure(options => { options.ValueProviders.Add(); - options.DefinitionProviders.Add(); }); } } diff --git a/framework/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/SettingProvider_Tests.cs b/framework/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/SettingProvider_Tests.cs index 6b95b0f755..c3c22f76f9 100644 --- a/framework/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/SettingProvider_Tests.cs +++ b/framework/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/SettingProvider_Tests.cs @@ -13,6 +13,11 @@ namespace Volo.Abp.Settings _settingProvider = GetRequiredService(); } + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + [Fact] public async Task Should_Get_Null_If_No_Value_Provided_And_No_Default_Value() { diff --git a/framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/MenuManager_Tests.cs b/framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/MenuManager_Tests.cs index c7f2093e83..77203962ae 100644 --- a/framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/MenuManager_Tests.cs +++ b/framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/MenuManager_Tests.cs @@ -2,7 +2,7 @@ using Microsoft.Extensions.DependencyInjection; using Shouldly; using Volo.Abp.Modularity; -using System.Collections.Generic; +using Volo.Abp.Ui.Navigation; using Xunit; namespace Volo.Abp.UI.Navigation @@ -25,10 +25,13 @@ namespace Volo.Abp.UI.Navigation mainMenu.DisplayName.ShouldBe("Main Menu"); mainMenu.Items.Count.ShouldBe(2); mainMenu.Items[0].Name.ShouldBe("Dashboard"); - mainMenu.Items[1].Name.ShouldBe("Administration"); + mainMenu.Items[1].Name.ShouldBe(DefaultMenuNames.Application.Main.Administration); mainMenu.Items[1].Items[0].Name.ShouldBe("Administration.UserManagement"); mainMenu.Items[1].Items[1].Name.ShouldBe("Administration.RoleManagement"); mainMenu.Items[1].Items[2].Name.ShouldBe("Administration.DashboardSettings"); + mainMenu.Items[1].Items[3].Name.ShouldBe("Administration.SubMenu1"); + mainMenu.Items[1].Items[3].Items[0].Name.ShouldBe("Administration.SubMenu1.1"); + mainMenu.Items[1].Items[3].Items[1].Name.ShouldBe("Administration.SubMenu1.2"); } [DependsOn(typeof(AbpUiNavigationModule))] @@ -60,10 +63,7 @@ namespace Volo.Abp.UI.Navigation context.Menu.DisplayName = "Main Menu"; - var administration = context.Menu.Items.GetOrAdd( - m => m.Name == "Administration", - () => new ApplicationMenuItem("Administration", "Administration") - ); + var administration = context.Menu.GetAdministration(); administration.AddItem(new ApplicationMenuItem("Administration.UserManagement", "User Management", url: "/admin/users")); administration.AddItem(new ApplicationMenuItem("Administration.RoleManagement", "Role Management", url: "/admin/roles")); @@ -88,13 +88,16 @@ namespace Volo.Abp.UI.Navigation context.Menu.Items.Insert(0, new ApplicationMenuItem("Dashboard", "Dashboard", url: "/dashboard")); - var administration = context.Menu.Items.GetOrAdd( - m => m.Name == "Administration", - () => new ApplicationMenuItem("Administration", "Administration") - ); + var administration = context.Menu.GetAdministration(); administration.AddItem(new ApplicationMenuItem("Administration.DashboardSettings", "Dashboard Settings", url: "/admin/settings/dashboard")); + administration.AddItem( + new ApplicationMenuItem("Administration.SubMenu1", "Sub menu 1") + .AddItem(new ApplicationMenuItem("Administration.SubMenu1.1", "Sub menu 1.1", url: "/submenu1/submenu1_1")) + .AddItem(new ApplicationMenuItem("Administration.SubMenu1.2", "Sub menu 1.2", url: "/submenu1/submenu1_2")) + ); + return Task.CompletedTask; } } diff --git a/modules/account/src/Volo.Abp.Account.Web/AbpAccountWebModule.cs b/modules/account/src/Volo.Abp.Account.Web/AbpAccountWebModule.cs index 5d136bd329..e999cf1450 100644 --- a/modules/account/src/Volo.Abp.Account.Web/AbpAccountWebModule.cs +++ b/modules/account/src/Volo.Abp.Account.Web/AbpAccountWebModule.cs @@ -31,11 +31,6 @@ namespace Volo.Abp.Account.Web public override void ConfigureServices(ServiceConfigurationContext context) { - Configure(options => - { - options.DefinitionProviders.Add(); - }); - Configure(options => { options.FileSets.AddEmbedded("Volo.Abp.Account.Web"); diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/IAuditLogRepository.cs b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/IAuditLogRepository.cs index e746063b49..489a07a0ce 100644 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/IAuditLogRepository.cs +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/IAuditLogRepository.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Net; +using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Repositories; @@ -15,14 +16,25 @@ namespace Volo.Abp.AuditLogging string httpMethod = null, string url = null, string userName = null, + string applicationName = null, + string correlationId = null, + int? maxExecutionDuration = null, + int? minExecutionDuration = null, + bool? hasException = null, HttpStatusCode? httpStatusCode = null, - bool includeDetails = true); + bool includeDetails = false, + CancellationToken cancellationToken = default); Task GetCountAsync( string httpMethod = null, string url = null, string userName = null, + string applicationName = null, + string correlationId = null, + int? maxExecutionDuration = null, + int? minExecutionDuration = null, + bool? hasException = null, HttpStatusCode? httpStatusCode = null, - bool includeDetails = true); + CancellationToken cancellationToken = default); } } diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.EntityFrameworkCore/Volo/Abp/AuditLogging/EntityFrameworkCore/EfCoreAuditLogRepository.cs b/modules/audit-logging/src/Volo.Abp.AuditLogging.EntityFrameworkCore/Volo/Abp/AuditLogging/EntityFrameworkCore/EfCoreAuditLogRepository.cs index 1fb7fcad7f..043c3d8988 100644 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.EntityFrameworkCore/Volo/Abp/AuditLogging/EntityFrameworkCore/EfCoreAuditLogRepository.cs +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.EntityFrameworkCore/Volo/Abp/AuditLogging/EntityFrameworkCore/EfCoreAuditLogRepository.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Dynamic.Core; using System.Net; +using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Volo.Abp.Domain.Repositories.EntityFrameworkCore; @@ -25,10 +26,16 @@ namespace Volo.Abp.AuditLogging.EntityFrameworkCore string httpMethod = null, string url = null, string userName = null, + string applicationName = null, + string correlationId = null, + int? maxExecutionDuration = null, + int? minExecutionDuration = null, + bool? hasException = null, HttpStatusCode? httpStatusCode = null, - bool includeDetails = true) + bool includeDetails = false, + CancellationToken cancellationToken = default) { - var query = GetListQuery(httpMethod, url, userName, httpStatusCode, includeDetails); + var query = GetListQuery(httpMethod, url, userName, applicationName, correlationId, maxExecutionDuration, minExecutionDuration, hasException, httpStatusCode, includeDetails); var auditLogs = await query.OrderBy(sorting ?? "executionTime desc") .PageBy(skipCount, maxResultCount) @@ -41,10 +48,15 @@ namespace Volo.Abp.AuditLogging.EntityFrameworkCore string httpMethod = null, string url = null, string userName = null, + string applicationName = null, + string correlationId = null, + int? maxExecutionDuration = null, + int? minExecutionDuration = null, + bool? hasException = null, HttpStatusCode? httpStatusCode = null, - bool includeDetails = true) + CancellationToken cancellationToken = default) { - var query = GetListQuery(httpMethod, url, userName, httpStatusCode, includeDetails); + var query = GetListQuery(httpMethod, url, userName, applicationName, correlationId, maxExecutionDuration, minExecutionDuration, hasException, httpStatusCode); var totalCount = await query.LongCountAsync(); @@ -55,15 +67,26 @@ namespace Volo.Abp.AuditLogging.EntityFrameworkCore string httpMethod = null, string url = null, string userName = null, + string applicationName = null, + string correlationId = null, + int? maxExecutionDuration = null, + int? minExecutionDuration = null, + bool? hasException = null, HttpStatusCode? httpStatusCode = null, - bool includeDetails = true) + bool includeDetails = false) { return DbSet.AsNoTracking() .IncludeDetails(includeDetails) + .WhereIf(hasException.HasValue && hasException.Value, auditLog => auditLog.Exceptions != null && auditLog.Exceptions.Length > 0) + .WhereIf(hasException.HasValue && !hasException.Value, auditLog => auditLog.Exceptions == null || auditLog.Exceptions.Length == 0) .WhereIf(httpMethod != null, auditLog => auditLog.HttpMethod != null && auditLog.HttpMethod.ToLowerInvariant() == httpMethod.ToLowerInvariant()) .WhereIf(url != null, auditLog => auditLog.Url != null && auditLog.Url.ToLowerInvariant().Contains(url.ToLowerInvariant())) - .WhereIf(userName != null, auditLog => auditLog.UserName != null && auditLog.UserName == userName) - .WhereIf(httpStatusCode != null && httpStatusCode > 0, auditLog => auditLog.HttpStatusCode == (int?)httpStatusCode); + .WhereIf(userName != null, auditLog => auditLog.UserName != null && auditLog.UserName.ToLowerInvariant() == userName.ToLowerInvariant()) + .WhereIf(applicationName != null, auditLog => auditLog.ApplicationName != null && auditLog.ApplicationName.ToLowerInvariant() == applicationName.ToLowerInvariant()) + .WhereIf(correlationId != null, auditLog => auditLog.CorrelationId != null && auditLog.CorrelationId.ToLowerInvariant() == correlationId.ToLowerInvariant()) + .WhereIf(httpStatusCode != null && httpStatusCode > 0, auditLog => auditLog.HttpStatusCode == (int?)httpStatusCode) + .WhereIf(maxExecutionDuration != null && maxExecutionDuration > 0, auditLog => auditLog.ExecutionDuration <= maxExecutionDuration) + .WhereIf(minExecutionDuration != null && minExecutionDuration > 0, auditLog => auditLog.ExecutionDuration >= minExecutionDuration); } public override IQueryable WithDetails() diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.MongoDB/Volo/Abp/AuditLogging/MongoDB/MongoAuditLogRepository.cs b/modules/audit-logging/src/Volo.Abp.AuditLogging.MongoDB/Volo/Abp/AuditLogging/MongoDB/MongoAuditLogRepository.cs index 662cfdbc2e..f70bd1deaf 100644 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.MongoDB/Volo/Abp/AuditLogging/MongoDB/MongoAuditLogRepository.cs +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.MongoDB/Volo/Abp/AuditLogging/MongoDB/MongoAuditLogRepository.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Dynamic.Core; using System.Net; +using System.Threading; using System.Threading.Tasks; using MongoDB.Driver; using MongoDB.Driver.Linq; @@ -19,19 +20,42 @@ namespace Volo.Abp.AuditLogging.MongoDB } - public async Task> GetListAsync(string sorting = null, int maxResultCount = 50, int skipCount = 0, - string httpMethod = null, string url = null, string userName = null, HttpStatusCode? httpStatusCode = null, bool includeDetails = true) + public async Task> GetListAsync( + string sorting = null, + int maxResultCount = 50, + int skipCount = 0, + string httpMethod = null, + string url = null, + string userName = null, + string applicationName = null, + string correlationId = null, + int? maxDuration = null, + int? minDuration = null, + bool? hasException = null, + HttpStatusCode? httpStatusCode = null, + bool includeDetails = false, + CancellationToken cancellationToken = default) { - var query = GetListQuery(httpMethod, url, userName, httpStatusCode, includeDetails); + var query = GetListQuery(httpMethod, url, userName, applicationName, correlationId, maxDuration, minDuration, hasException, httpStatusCode, includeDetails); return await query.OrderBy(sorting ?? "executionTime desc").As>() .PageBy>(skipCount, maxResultCount) .ToListAsync(); } - public async Task GetCountAsync(string httpMethod = null, string url = null, string userName = null, HttpStatusCode? httpStatusCode = null, bool includeDetails = true) + public async Task GetCountAsync( + string httpMethod = null, + string url = null, + string userName = null, + string applicationName = null, + string correlationId = null, + int? maxDuration = null, + int? minDuration = null, + bool? hasException = null, + HttpStatusCode? httpStatusCode = null, + CancellationToken cancellationToken = default) { - var query = GetListQuery(httpMethod, url, userName, httpStatusCode, includeDetails); + var query = GetListQuery(httpMethod, url, userName, applicationName, correlationId, maxDuration, minDuration, hasException, httpStatusCode); var count = await query.As>().LongCountAsync(); @@ -42,14 +66,25 @@ namespace Volo.Abp.AuditLogging.MongoDB string httpMethod = null, string url = null, string userName = null, + string applicationName = null, + string correlationId = null, + int? maxDuration = null, + int? minDuration = null, + bool? hasException = null, HttpStatusCode? httpStatusCode = null, - bool includeDetails = true) + bool includeDetails = false) { return GetMongoQueryable() + .WhereIf(hasException.HasValue && hasException.Value, auditLog => auditLog.Exceptions != null && auditLog.Exceptions.Length > 0) + .WhereIf(hasException.HasValue && !hasException.Value, auditLog => auditLog.Exceptions == null || auditLog.Exceptions.Length == 0) .WhereIf(httpMethod != null, auditLog => auditLog.HttpMethod != null && auditLog.HttpMethod.ToLowerInvariant() == httpMethod.ToLowerInvariant()) .WhereIf(url != null, auditLog => auditLog.Url != null && auditLog.Url.ToLowerInvariant().Contains(url.ToLowerInvariant())) - .WhereIf(userName != null, auditLog => auditLog.UserName != null && auditLog.UserName == userName) - .WhereIf(httpStatusCode != null && httpStatusCode > 0, auditLog => auditLog.HttpStatusCode == (int?)httpStatusCode); + .WhereIf(userName != null, auditLog => auditLog.UserName != null && auditLog.UserName.ToLowerInvariant() == userName.ToLowerInvariant()) + .WhereIf(applicationName != null, auditLog => auditLog.ApplicationName != null && auditLog.ApplicationName.ToLowerInvariant() == applicationName.ToLowerInvariant()) + .WhereIf(correlationId != null, auditLog => auditLog.CorrelationId != null && auditLog.CorrelationId.ToLowerInvariant() == correlationId.ToLowerInvariant()) + .WhereIf(httpStatusCode != null && httpStatusCode > 0, auditLog => auditLog.HttpStatusCode == (int?)httpStatusCode) + .WhereIf(maxDuration != null && maxDuration > 0, auditLog => auditLog.ExecutionDuration <= maxDuration) + .WhereIf(minDuration != null && minDuration > 0, auditLog => auditLog.ExecutionDuration >= minDuration); } } } diff --git a/modules/blogging/app/Volo.BloggingTestApp/BloggingTestAppModule.cs b/modules/blogging/app/Volo.BloggingTestApp/BloggingTestAppModule.cs index 1945c6ac31..aec8044d3b 100644 --- a/modules/blogging/app/Volo.BloggingTestApp/BloggingTestAppModule.cs +++ b/modules/blogging/app/Volo.BloggingTestApp/BloggingTestAppModule.cs @@ -114,7 +114,6 @@ namespace Volo.BloggingTestApp Configure(options => { options.FileUploadLocalFolder = Path.Combine(hostingEnvironment.WebRootPath, "files"); - options.FileUploadUrlRoot = "/files/"; }); } diff --git a/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/BloggingApplicationContractsModule.cs b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/BloggingApplicationContractsModule.cs index 29deef3457..e9ecdbf581 100644 --- a/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/BloggingApplicationContractsModule.cs +++ b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/BloggingApplicationContractsModule.cs @@ -12,11 +12,6 @@ namespace Volo.Blogging { public override void ConfigureServices(ServiceConfigurationContext context) { - Configure(options => - { - options.DefinitionProviders.Add(); - }); - Configure(options => { options.FileSets.AddEmbedded(); diff --git a/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/FileUploadOutputDto.cs b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/FileUploadOutputDto.cs index 1c3b1a1a94..a5e49eee48 100644 --- a/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/FileUploadOutputDto.cs +++ b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/FileUploadOutputDto.cs @@ -2,6 +2,8 @@ { public class FileUploadOutputDto { - public string Url { get; set; } + public string Name { get; set; } + + public string WebUrl { get; set; } } } \ No newline at end of file diff --git a/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/IFileAppService.cs b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/IFileAppService.cs index 6fed8bc62f..a949c848cd 100644 --- a/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/IFileAppService.cs +++ b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/IFileAppService.cs @@ -5,6 +5,8 @@ namespace Volo.Blogging.Files { public interface IFileAppService : IApplicationService { - Task UploadAsync(FileUploadInputDto input); + Task GetAsync(string name); + + Task CreateAsync(FileUploadInputDto input); } } diff --git a/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/RawFileDto.cs b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/RawFileDto.cs new file mode 100644 index 0000000000..56a9653b73 --- /dev/null +++ b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/RawFileDto.cs @@ -0,0 +1,7 @@ +namespace Volo.Blogging.Files +{ + public class RawFileDto + { + public byte[] Bytes { get; set; } + } +} \ No newline at end of file diff --git a/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Files/BlogFileOptions.cs b/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Files/BlogFileOptions.cs index a06bdafc8d..7243595802 100644 --- a/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Files/BlogFileOptions.cs +++ b/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Files/BlogFileOptions.cs @@ -7,7 +7,5 @@ public class BlogFileOptions { public string FileUploadLocalFolder { get; set; } - - public string FileUploadUrlRoot { get; set; } } } diff --git a/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Files/FileAppService.cs b/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Files/FileAppService.cs index 8f18608080..d8b9273f0c 100644 --- a/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Files/FileAppService.cs +++ b/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Files/FileAppService.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.IO; using System.Threading.Tasks; @@ -20,7 +19,21 @@ namespace Volo.Blogging.Files Options = options.Value; } - public virtual Task UploadAsync(FileUploadInputDto input) + public virtual Task GetAsync(string name) + { + Check.NotNullOrWhiteSpace(name, nameof(name)); + + var filePath = Path.Combine(Options.FileUploadLocalFolder, name); + + return Task.FromResult( + new RawFileDto + { + Bytes = File.ReadAllBytes(filePath) + } + ); + } + + public virtual Task CreateAsync(FileUploadInputDto input) { if (input.Bytes.IsNullOrEmpty()) { @@ -44,7 +57,8 @@ namespace Volo.Blogging.Files return Task.FromResult(new FileUploadOutputDto { - Url = Options.FileUploadUrlRoot.EnsureEndsWith('/') + uniqueFileName + Name = uniqueFileName, + WebUrl = "/api/blogging/files/www/" + uniqueFileName }); } diff --git a/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Posts/PostAppService.cs b/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Posts/PostAppService.cs index 8799b4e0d0..916455bc47 100644 --- a/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Posts/PostAppService.cs +++ b/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Posts/PostAppService.cs @@ -36,19 +36,19 @@ namespace Volo.Blogging.Posts var userDictionary = new Dictionary(); var postDtos = new List(ObjectMapper.Map, List>(posts)); - if (tag != null) + foreach (var postDto in postDtos) { - postDtos = await FilterPostsByTag(postDtos, tag); + postDto.Tags = await GetTagsOfPost(postDto.Id); } - foreach (var postDto in postDtos) + if (tag != null) { - postDto.CommentCount = await _commentRepository.GetCommentCountOfPostAsync(postDto.Id); + postDtos = await FilterPostsByTag(postDtos, tag); } foreach (var postDto in postDtos) { - postDto.Tags = await GetTagsOfPost(postDto.Id); + postDto.CommentCount = await _commentRepository.GetCommentCountOfPostAsync(postDto.Id); } foreach (var postDto in postDtos) @@ -228,8 +228,9 @@ namespace Volo.Blogging.Posts { tag.IncreaseUsageCount(); tag = await _tagRepository.UpdateAsync(tag); - post.AddTag(tag.Id); } + + post.AddTag(tag.Id); } } @@ -253,18 +254,8 @@ namespace Volo.Blogging.Posts private Task> FilterPostsByTag(List allPostDtos, Tag tag) { - var filteredPostDtos = new List(); - - foreach (var postDto in allPostDtos) - { - if (postDto.Tags.All(p => p.Id != tag.Id)) - { - continue; - } - - filteredPostDtos.Add(postDto); - } - + var filteredPostDtos = allPostDtos.Where(p => p.Tags?.Any(t => t.Id == tag.Id) ?? false).ToList(); + return Task.FromResult(filteredPostDtos); } } diff --git a/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Users/BlogUserSynchronizer.cs b/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Users/BlogUserSynchronizer.cs index e6e961b8a7..899f5c3d8d 100644 --- a/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Users/BlogUserSynchronizer.cs +++ b/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Users/BlogUserSynchronizer.cs @@ -13,7 +13,9 @@ namespace Volo.Blogging.Users protected IBlogUserRepository UserRepository { get; } protected IBlogUserLookupService UserLookupService { get; } - public BlogUserSynchronizer(IBlogUserRepository userRepository, IBlogUserLookupService userLookupService) + public BlogUserSynchronizer( + IBlogUserRepository userRepository, + IBlogUserLookupService userLookupService) { UserRepository = userRepository; UserLookupService = userLookupService; @@ -24,7 +26,6 @@ namespace Volo.Blogging.Users var user = await UserRepository.FindAsync(eventData.Entity.Id); if (user == null) { - //TODO: Why needed (ask to @ebicoglu)? user = await UserLookupService.FindByIdAsync(eventData.Entity.Id); if (user == null) { diff --git a/modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/BlogFilesController.cs b/modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/BlogFilesController.cs new file mode 100644 index 0000000000..818435a78c --- /dev/null +++ b/modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/BlogFilesController.cs @@ -0,0 +1,81 @@ +using System.IO; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.Http; +using Volo.Blogging.Areas.Blog.Models; +using Volo.Blogging.Files; + +namespace Volo.Blogging +{ + [RemoteService] + [Area("blogging")] + [Route("api/blogging/files")] + public class BlogFilesController : AbpController, IFileAppService + { + private readonly IFileAppService _fileAppService; + + public BlogFilesController(IFileAppService fileAppService) + { + _fileAppService = fileAppService; + } + + [HttpGet] + [Route("{name}")] + public Task GetAsync(string name) //TODO: output cache would be good + { + return _fileAppService.GetAsync(name); + } + + [HttpGet] + [Route("www/{name}")] + public async Task GetForWebAsync(string name) //TODO: output cache would be good + { + var file = await _fileAppService.GetAsync(name); + return File( + file.Bytes, + MimeTypes.GetByExtension(Path.GetExtension(name)) + ); + } + + [HttpPost] + public Task CreateAsync(FileUploadInputDto input) + { + return _fileAppService.CreateAsync(input); + } + + [HttpPost] + [Route("images/upload")] + public async Task UploadImage(IFormFile file) + { + //TODO: localize exception messages + + if (file == null) + { + throw new UserFriendlyException("No file found!"); + } + + if (file.Length <= 0) + { + throw new UserFriendlyException("File is empty!"); + } + + if (!file.ContentType.Contains("image")) + { + throw new UserFriendlyException("Not a valid image!"); + } + + var output = await _fileAppService.CreateAsync( + new FileUploadInputDto + { + Bytes = file.GetAllBytes(), + Name = file.FileName + } + ); + + return Json(new FileUploadResult(output.WebUrl)); + } + } +} diff --git a/modules/blogging/src/Volo.Blogging.Web/Areas/Blog/Models/FileUploadResult.cs b/modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/FileUploadResult.cs similarity index 100% rename from modules/blogging/src/Volo.Blogging.Web/Areas/Blog/Models/FileUploadResult.cs rename to modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/FileUploadResult.cs diff --git a/modules/blogging/src/Volo.Blogging.Web/Areas/Blog/Controllers/CommentsController.cs b/modules/blogging/src/Volo.Blogging.Web/Areas/Blog/Controllers/CommentsController.cs index 89ed0f1968..80ef132c21 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Areas/Blog/Controllers/CommentsController.cs +++ b/modules/blogging/src/Volo.Blogging.Web/Areas/Blog/Controllers/CommentsController.cs @@ -7,6 +7,8 @@ using Volo.Blogging.Comments.Dtos; namespace Volo.Blogging.Areas.Blog.Controllers { + //TODO: Is that being used? + [Area("Blog")] [Route("Blog/[controller]/[action]")] public class CommentsController : AbpController diff --git a/modules/blogging/src/Volo.Blogging.Web/Areas/Blog/Controllers/FilesController.cs b/modules/blogging/src/Volo.Blogging.Web/Areas/Blog/Controllers/FilesController.cs deleted file mode 100644 index 7d3e12f3cd..0000000000 --- a/modules/blogging/src/Volo.Blogging.Web/Areas/Blog/Controllers/FilesController.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Volo.Abp; -using Volo.Abp.AspNetCore.Mvc; -using Volo.Blogging.Areas.Blog.Models; -using Volo.Blogging.Files; -using Volo.Blogging.Hosting; - -namespace Volo.Blogging.Areas.Blog.Controllers -{ - //TODO: This may be moved to HttpApi project since it may be needed by a SPA too. - [Area("Blog")] - [Route("Blog/[controller]/[action]")] - public class FilesController : AbpController - { - private readonly IFileAppService _fileAppService; - - public FilesController(IFileAppService fileAppService) - { - _fileAppService = fileAppService; - } - - [HttpPost] - public async Task UploadImage(IFormFile file) - { - //TODO: localize exception messages - - if (file == null) - { - throw new UserFriendlyException("No file found!"); - } - - if (file.Length <= 0) - { - throw new UserFriendlyException("File is empty!"); - } - - if (!file.ContentType.Contains("image")) - { - throw new UserFriendlyException("Not a valid image!"); - } - - var output = await _fileAppService.UploadAsync( - new FileUploadInputDto - { - Bytes = file.AsBytes(), - Name = file.FileName - } - ); - - return Json(new FileUploadResult(output.Url)); - } - } -} \ No newline at end of file diff --git a/modules/blogging/src/Volo.Blogging.Web/Areas/Blog/Controllers/PostsController.cs b/modules/blogging/src/Volo.Blogging.Web/Areas/Blog/Controllers/PostsController.cs index 55a96f6ccc..383f08284f 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Areas/Blog/Controllers/PostsController.cs +++ b/modules/blogging/src/Volo.Blogging.Web/Areas/Blog/Controllers/PostsController.cs @@ -6,6 +6,8 @@ using Volo.Blogging.Posts; namespace Volo.Blogging.Areas.Blog.Controllers { + //TODO: Is that being used? + [Area("Blog")] [Route("Blog/[controller]/[action]")] public class PostsController : AbpController diff --git a/modules/blogging/src/Volo.Blogging.Web/BloggingMenuContributor.cs b/modules/blogging/src/Volo.Blogging.Web/BloggingMenuContributor.cs index f20c946232..cf1f12ed02 100644 --- a/modules/blogging/src/Volo.Blogging.Web/BloggingMenuContributor.cs +++ b/modules/blogging/src/Volo.Blogging.Web/BloggingMenuContributor.cs @@ -1,7 +1,7 @@ -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; +using System.Threading.Tasks; using Volo.Abp.UI.Navigation; using Volo.Blogging.Localization; @@ -22,23 +22,17 @@ namespace Volo.Blogging var authorizationService = context.ServiceProvider.GetRequiredService(); var l = context.ServiceProvider.GetRequiredService>(); - if (await authorizationService.IsGrantedAsync(BloggingPermissions.Blogs.Default)) + if (await authorizationService.IsGrantedAsync(BloggingPermissions.Blogs.Management)) { - var rootMenuItem = new ApplicationMenuItem("Blogs", l["Menu:Blogs"], "/Blog"); - - context.Menu.AddItem(rootMenuItem); + var managementRootMenuItem = new ApplicationMenuItem("BlogManagement", l["Menu:BlogManagement"]); + //TODO: Using the same permission. Reconsider. if (await authorizationService.IsGrantedAsync(BloggingPermissions.Blogs.Management)) { - var managementRootMenuItem = new ApplicationMenuItem("BlogManagement", l["Menu:BlogManagement"]); - - if (await authorizationService.IsGrantedAsync(BloggingPermissions.Blogs.Management)) - { - managementRootMenuItem.AddItem(new ApplicationMenuItem("BlogsAdmin", l["Menu:Blogs"], "/Admin/Blogs")); - } - - context.Menu.AddItem(managementRootMenuItem); + managementRootMenuItem.AddItem(new ApplicationMenuItem("BlogManagement.Blogs", l["Menu:Blogs"], "/Admin/Blogs")); } + + context.Menu.AddItem(managementRootMenuItem); } } } diff --git a/modules/blogging/src/Volo.Blogging.Web/Hosting/FormFileExtensions.cs b/modules/blogging/src/Volo.Blogging.Web/Hosting/FormFileExtensions.cs deleted file mode 100644 index aab0832bbf..0000000000 --- a/modules/blogging/src/Volo.Blogging.Web/Hosting/FormFileExtensions.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.IO; -using JetBrains.Annotations; -using Microsoft.AspNetCore.Http; -using Volo.Abp; - -namespace Volo.Blogging.Hosting -{ - public static class FormFileExtensions - { - public static byte[] AsBytes(this IFormFile file) //TODO: Move to the framework (rename to GetBytes) - { - using (var stream = file.OpenReadStream()) - { - return stream.GetAllBytes(); - } - } - - public static void ValidateImage([CanBeNull] this IFormFile file) - { - - } - } -} \ No newline at end of file diff --git a/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/en.json b/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/en.json index b9e7b543b9..444c3d80af 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/en.json +++ b/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/en.json @@ -1,7 +1,7 @@ { "culture": "en", "texts": { - "Menu:Blogs": "Blog", + "Menu:Blogs": "Blogs", "Menu:BlogManagement": "Blog Management", "Title": "Title", "Delete": "Delete", diff --git a/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/pt-BR.json b/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/pt-BR.json index 81740c65db..cf98e56b06 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/pt-BR.json +++ b/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/pt-BR.json @@ -1,7 +1,7 @@ { "culture": "pt-BR", "texts": { - "Menu:Blogs": "Blog", + "Menu:Blogs": "Blogs", "Menu:BlogManagement": "Gerenciar Blog", "Title": "Título", "Delete": "Excluir", diff --git a/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/tr.json b/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/tr.json index a5a705d506..7d8386e517 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/tr.json +++ b/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/tr.json @@ -1,7 +1,7 @@ { "culture": "tr", "texts": { - "Menu:Blogs": "Blog", + "Menu:Blogs": "Bloglar", "Title": "Başlık", "Delete": "Sil", "Reply": "Yanıtla", diff --git a/modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/index.js b/modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/index.js index a7604b9d41..c370f10a67 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/index.js +++ b/modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/index.js @@ -7,12 +7,13 @@ var _dataTable = $('#BlogsTable').DataTable(abp.libs.datatables.normalizeConfiguration({ processing: true, serverSide: true, - paging: true, + paging: false, + info: false, searching: false, autoWidth: false, scrollCollapse: true, order: [[3, "desc"]], - ajax: abp.libs.datatables.createAjax(volo.blogging.blogs.getListPaged), + ajax: abp.libs.datatables.createAjax(volo.blogging.blogs.getList), columnDefs: [ { rowAction: { diff --git a/modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/edit.js b/modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/edit.js index 32bd7873ca..9fb4929d30 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/edit.js +++ b/modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/edit.js @@ -17,7 +17,7 @@ $.ajax({ type: "POST", - url: "/Blog/Files/UploadImage", + url: "/api/blogging/files/images/upload", data: formData, contentType: false, processData: false, @@ -42,7 +42,7 @@ $.ajax({ type: "POST", - url: "/Blog/Files/UploadImage", + url: "/api/blogging/files/images/upload", data: formData, contentType: false, processData: false, diff --git a/modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/new.js b/modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/new.js index f28cb48d44..519d3f41f4 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/new.js +++ b/modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/new.js @@ -19,7 +19,7 @@ $.ajax({ type: "POST", - url: "/Blog/Files/UploadImage", + url: "/api/blogging/files/images/upload", data: formData, contentType: false, processData: false, @@ -43,7 +43,7 @@ $.ajax({ type: "POST", - url: "/Blog/Files/UploadImage", + url: "/api/blogging/files/images/upload", data: formData, contentType: false, processData: false, diff --git a/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/DocsAdminApplicationContractsModule.cs b/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/DocsAdminApplicationContractsModule.cs index 545fcb25b8..e37a732a0d 100644 --- a/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/DocsAdminApplicationContractsModule.cs +++ b/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/DocsAdminApplicationContractsModule.cs @@ -15,11 +15,6 @@ namespace Volo.Docs.Admin { public override void ConfigureServices(ServiceConfigurationContext context) { - Configure(options => - { - options.DefinitionProviders.Add(); - }); - Configure(options => { options.FileSets.AddEmbedded(); diff --git a/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/Edit.cshtml.cs b/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/Edit.cshtml.cs index 77c5321ad0..8f9ce3fe4a 100644 --- a/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/Edit.cshtml.cs +++ b/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/Edit.cshtml.cs @@ -38,10 +38,8 @@ namespace Volo.Docs.Admin.Pages.Docs.Admin.Projects SetGithubProjectFromDto(project); return Page(); } - else - { - throw new BusinessException("UnknowDocumentSourceExceptionMessage"); - } + + throw new BusinessException("UnknowDocumentSourceExceptionMessage"); } public async Task OnPostAsync() diff --git a/modules/docs/src/Volo.Docs.Application/Volo/Docs/Documents/DocumentAppService.cs b/modules/docs/src/Volo.Docs.Application/Volo/Docs/Documents/DocumentAppService.cs index 4aaedd579b..3a4474e877 100644 --- a/modules/docs/src/Volo.Docs.Application/Volo/Docs/Documents/DocumentAppService.cs +++ b/modules/docs/src/Volo.Docs.Application/Volo/Docs/Documents/DocumentAppService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Logging; @@ -18,8 +19,8 @@ namespace Volo.Docs.Documents public DocumentAppService( IProjectRepository projectRepository, - IDocumentStoreFactory documentStoreFactory, - IDistributedCache documentCache, + IDocumentStoreFactory documentStoreFactory, + IDistributedCache documentCache, IDistributedCache resourceCache) { _projectRepository = projectRepository; @@ -66,15 +67,22 @@ namespace Volo.Docs.Documents var project = await _projectRepository.GetAsync(input.ProjectId); var cacheKey = $"Resource@{project.ShortName}#{input.Name}#{input.Version}"; + async Task GetResourceAsync() + { + var store = _documentStoreFactory.Create(project.DocumentStoreType); + var documentResource = await store.GetResource(project, input.Name, input.Version); + + return ObjectMapper.Map(documentResource); + } + + if (Debugger.IsAttached) + { + return await GetResourceAsync(); + } + return await ResourceCache.GetOrAddAsync( cacheKey, - async () => - { - var store = _documentStoreFactory.Create(project.DocumentStoreType); - var documentResource = await store.GetResource(project, input.Name, input.Version); - - return ObjectMapper.Map(documentResource); - }, + GetResourceAsync, () => new DistributedCacheEntryOptions { //TODO: Configurable? @@ -85,22 +93,29 @@ namespace Volo.Docs.Documents } protected virtual async Task GetDocumentWithDetailsDto( - Project project, - string documentName, + Project project, + string documentName, string version) { var cacheKey = $"Document@{project.ShortName}#{documentName}#{version}"; + async Task GetDocumentAsync() + { + Logger.LogInformation($"Not found in the cache. Requesting {documentName} from the store..."); + var store = _documentStoreFactory.Create(project.DocumentStoreType); + var document = await store.GetDocumentAsync(project, documentName, version); + Logger.LogInformation($"Document retrieved: {documentName}"); + return CreateDocumentWithDetailsDto(project, document); + } + + if (Debugger.IsAttached) + { + return await GetDocumentAsync(); + } + return await DocumentCache.GetOrAddAsync( cacheKey, - async () => - { - Logger.LogInformation($"Not found in the cache. Requesting {documentName} from the store..."); - var store = _documentStoreFactory.Create(project.DocumentStoreType); - var document = await store.GetDocumentAsync(project, documentName, version); - Logger.LogInformation($"Document retrieved: {documentName}"); - return CreateDocumentWithDetailsDto(project, document); - }, + GetDocumentAsync, () => new DistributedCacheEntryOptions { //TODO: Configurable? diff --git a/modules/feature-management/Abp.FeatureManagement.sln.DotSettings b/modules/feature-management/Abp.FeatureManagement.sln.DotSettings new file mode 100644 index 0000000000..836e6ed4da --- /dev/null +++ b/modules/feature-management/Abp.FeatureManagement.sln.DotSettings @@ -0,0 +1,7 @@ + + True + D:\github\abp\common.DotSettings + ..\..\..\common.DotSettings + True + 1 + \ No newline at end of file diff --git a/modules/feature-management/Volo.Abp.FeatureManagement.sln b/modules/feature-management/Volo.Abp.FeatureManagement.sln new file mode 100644 index 0000000000..34f0c84aa5 --- /dev/null +++ b/modules/feature-management/Volo.Abp.FeatureManagement.sln @@ -0,0 +1,123 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27703.2026 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.FeatureManagement.Domain.Shared", "src\Volo.Abp.FeatureManagement.Domain.Shared\Volo.Abp.FeatureManagement.Domain.Shared.csproj", "{D64C1577-4929-4B60-939E-96DE1534891A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.FeatureManagement.Domain", "src\Volo.Abp.FeatureManagement.Domain\Volo.Abp.FeatureManagement.Domain.csproj", "{F2840BC7-0188-4606-9126-DADD0F5ABF7A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.FeatureManagement.Application.Contracts", "src\Volo.Abp.FeatureManagement.Application.Contracts\Volo.Abp.FeatureManagement.Application.Contracts.csproj", "{BD65D04F-08D5-40C1-8C24-77CA0BACB877}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.FeatureManagement.Application", "src\Volo.Abp.FeatureManagement.Application\Volo.Abp.FeatureManagement.Application.csproj", "{78040F9E-3501-4A40-82DF-00A597710F35}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{649A3FFA-182F-4E56-9717-E6A9A2BEC545}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.FeatureManagement.EntityFrameworkCore", "src\Volo.Abp.FeatureManagement.EntityFrameworkCore\Volo.Abp.FeatureManagement.EntityFrameworkCore.csproj", "{0CE86223-D31D-4315-A1F5-87BA3EE1B844}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.FeatureManagement.MongoDB", "src\Volo.Abp.FeatureManagement.MongoDB\Volo.Abp.FeatureManagement.MongoDB.csproj", "{F1C58097-4C08-4D88-8976-6B3389391481}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.FeatureManagement.HttpApi", "src\Volo.Abp.FeatureManagement.HttpApi\Volo.Abp.FeatureManagement.HttpApi.csproj", "{077AA5F8-8B61-420C-A6B5-0150A66FDB34}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.FeatureManagement.HttpApi.Client", "src\Volo.Abp.FeatureManagement.HttpApi.Client\Volo.Abp.FeatureManagement.HttpApi.Client.csproj", "{36E2735F-CEAB-44C8-A6D1-2CDAFF399751}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.FeatureManagement.Web", "src\Volo.Abp.FeatureManagement.Web\Volo.Abp.FeatureManagement.Web.csproj", "{8C5B0D35-9734-4439-977C-2F1AC9E1D69E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.FeatureManagement.TestBase", "test\Volo.Abp.FeatureManagement.TestBase\Volo.Abp.FeatureManagement.TestBase.csproj", "{6E5B22E7-E2DB-45D4-B828-8019D8FD51E8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.FeatureManagement.Domain.Tests", "test\Volo.Abp.FeatureManagement.Domain.Tests\Volo.Abp.FeatureManagement.Domain.Tests.csproj", "{44FB6636-5427-415D-8883-CB7E42D548F2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests", "test\Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests\Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests.csproj", "{E5906DE1-B2F5-472E-BE1B-1D96A68B834D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.FeatureManagement.MongoDB.Tests", "test\Volo.Abp.FeatureManagement.MongoDB.Tests\Volo.Abp.FeatureManagement.MongoDB.Tests.csproj", "{AA783A34-86E4-41A5-AE21-5D9FBD98D858}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.FeatureManagement.Application.Tests", "test\Volo.Abp.FeatureManagement.Application.Tests\Volo.Abp.FeatureManagement.Application.Tests.csproj", "{13A9EAD6-F3A4-4357-BA4A-A7E8FEB4A264}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D64C1577-4929-4B60-939E-96DE1534891A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D64C1577-4929-4B60-939E-96DE1534891A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D64C1577-4929-4B60-939E-96DE1534891A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D64C1577-4929-4B60-939E-96DE1534891A}.Release|Any CPU.Build.0 = Release|Any CPU + {F2840BC7-0188-4606-9126-DADD0F5ABF7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F2840BC7-0188-4606-9126-DADD0F5ABF7A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F2840BC7-0188-4606-9126-DADD0F5ABF7A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F2840BC7-0188-4606-9126-DADD0F5ABF7A}.Release|Any CPU.Build.0 = Release|Any CPU + {BD65D04F-08D5-40C1-8C24-77CA0BACB877}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BD65D04F-08D5-40C1-8C24-77CA0BACB877}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BD65D04F-08D5-40C1-8C24-77CA0BACB877}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BD65D04F-08D5-40C1-8C24-77CA0BACB877}.Release|Any CPU.Build.0 = Release|Any CPU + {78040F9E-3501-4A40-82DF-00A597710F35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {78040F9E-3501-4A40-82DF-00A597710F35}.Debug|Any CPU.Build.0 = Debug|Any CPU + {78040F9E-3501-4A40-82DF-00A597710F35}.Release|Any CPU.ActiveCfg = Release|Any CPU + {78040F9E-3501-4A40-82DF-00A597710F35}.Release|Any CPU.Build.0 = Release|Any CPU + {0CE86223-D31D-4315-A1F5-87BA3EE1B844}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0CE86223-D31D-4315-A1F5-87BA3EE1B844}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0CE86223-D31D-4315-A1F5-87BA3EE1B844}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0CE86223-D31D-4315-A1F5-87BA3EE1B844}.Release|Any CPU.Build.0 = Release|Any CPU + {F1C58097-4C08-4D88-8976-6B3389391481}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1C58097-4C08-4D88-8976-6B3389391481}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1C58097-4C08-4D88-8976-6B3389391481}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1C58097-4C08-4D88-8976-6B3389391481}.Release|Any CPU.Build.0 = Release|Any CPU + {077AA5F8-8B61-420C-A6B5-0150A66FDB34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {077AA5F8-8B61-420C-A6B5-0150A66FDB34}.Debug|Any CPU.Build.0 = Debug|Any CPU + {077AA5F8-8B61-420C-A6B5-0150A66FDB34}.Release|Any CPU.ActiveCfg = Release|Any CPU + {077AA5F8-8B61-420C-A6B5-0150A66FDB34}.Release|Any CPU.Build.0 = Release|Any CPU + {36E2735F-CEAB-44C8-A6D1-2CDAFF399751}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {36E2735F-CEAB-44C8-A6D1-2CDAFF399751}.Debug|Any CPU.Build.0 = Debug|Any CPU + {36E2735F-CEAB-44C8-A6D1-2CDAFF399751}.Release|Any CPU.ActiveCfg = Release|Any CPU + {36E2735F-CEAB-44C8-A6D1-2CDAFF399751}.Release|Any CPU.Build.0 = Release|Any CPU + {8C5B0D35-9734-4439-977C-2F1AC9E1D69E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8C5B0D35-9734-4439-977C-2F1AC9E1D69E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8C5B0D35-9734-4439-977C-2F1AC9E1D69E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8C5B0D35-9734-4439-977C-2F1AC9E1D69E}.Release|Any CPU.Build.0 = Release|Any CPU + {6E5B22E7-E2DB-45D4-B828-8019D8FD51E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6E5B22E7-E2DB-45D4-B828-8019D8FD51E8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6E5B22E7-E2DB-45D4-B828-8019D8FD51E8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6E5B22E7-E2DB-45D4-B828-8019D8FD51E8}.Release|Any CPU.Build.0 = Release|Any CPU + {44FB6636-5427-415D-8883-CB7E42D548F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {44FB6636-5427-415D-8883-CB7E42D548F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {44FB6636-5427-415D-8883-CB7E42D548F2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {44FB6636-5427-415D-8883-CB7E42D548F2}.Release|Any CPU.Build.0 = Release|Any CPU + {E5906DE1-B2F5-472E-BE1B-1D96A68B834D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E5906DE1-B2F5-472E-BE1B-1D96A68B834D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E5906DE1-B2F5-472E-BE1B-1D96A68B834D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E5906DE1-B2F5-472E-BE1B-1D96A68B834D}.Release|Any CPU.Build.0 = Release|Any CPU + {AA783A34-86E4-41A5-AE21-5D9FBD98D858}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA783A34-86E4-41A5-AE21-5D9FBD98D858}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA783A34-86E4-41A5-AE21-5D9FBD98D858}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA783A34-86E4-41A5-AE21-5D9FBD98D858}.Release|Any CPU.Build.0 = Release|Any CPU + {13A9EAD6-F3A4-4357-BA4A-A7E8FEB4A264}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {13A9EAD6-F3A4-4357-BA4A-A7E8FEB4A264}.Debug|Any CPU.Build.0 = Debug|Any CPU + {13A9EAD6-F3A4-4357-BA4A-A7E8FEB4A264}.Release|Any CPU.ActiveCfg = Release|Any CPU + {13A9EAD6-F3A4-4357-BA4A-A7E8FEB4A264}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {D64C1577-4929-4B60-939E-96DE1534891A} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} + {F2840BC7-0188-4606-9126-DADD0F5ABF7A} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} + {BD65D04F-08D5-40C1-8C24-77CA0BACB877} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} + {78040F9E-3501-4A40-82DF-00A597710F35} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} + {0CE86223-D31D-4315-A1F5-87BA3EE1B844} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} + {F1C58097-4C08-4D88-8976-6B3389391481} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} + {077AA5F8-8B61-420C-A6B5-0150A66FDB34} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} + {36E2735F-CEAB-44C8-A6D1-2CDAFF399751} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} + {8C5B0D35-9734-4439-977C-2F1AC9E1D69E} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} + {6E5B22E7-E2DB-45D4-B828-8019D8FD51E8} = {CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D} + {44FB6636-5427-415D-8883-CB7E42D548F2} = {CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D} + {E5906DE1-B2F5-472E-BE1B-1D96A68B834D} = {CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D} + {AA783A34-86E4-41A5-AE21-5D9FBD98D858} = {CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D} + {13A9EAD6-F3A4-4357-BA4A-A7E8FEB4A264} = {CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {4324B3B4-B60B-4E3C-91D8-59576B4E26DD} + EndGlobalSection +EndGlobal diff --git a/modules/feature-management/Volo.Abp.FeatureManagement.sln.DotSettings b/modules/feature-management/Volo.Abp.FeatureManagement.sln.DotSettings new file mode 100644 index 0000000000..d4df574aa0 --- /dev/null +++ b/modules/feature-management/Volo.Abp.FeatureManagement.sln.DotSettings @@ -0,0 +1,6 @@ + + True + D:\github\abp\common.DotSettings + ..\..\..\common.DotSettings + True + 1 \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo.Abp.FeatureManagement.Application.Contracts.csproj b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo.Abp.FeatureManagement.Application.Contracts.csproj new file mode 100644 index 0000000000..00d1f8834d --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo.Abp.FeatureManagement.Application.Contracts.csproj @@ -0,0 +1,26 @@ + + + + + + netstandard2.0 + + + + + + + + + + + + + + + + + + + + diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/AbpFeatureManagementApplicationContractsModule.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/AbpFeatureManagementApplicationContractsModule.cs new file mode 100644 index 0000000000..f5d03e7167 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/AbpFeatureManagementApplicationContractsModule.cs @@ -0,0 +1,30 @@ +using Volo.Abp.Application; +using Volo.Abp.FeatureManagement.Localization; +using Volo.Abp.Localization; +using Volo.Abp.Modularity; +using Volo.Abp.VirtualFileSystem; + +namespace Volo.Abp.FeatureManagement +{ + [DependsOn( + typeof(AbpFeatureManagementDomainSharedModule), + typeof(AbpDddApplicationModule) + )] + public class AbpFeatureManagementApplicationContractsModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.FileSets.AddEmbedded(); + }); + + Configure(options => + { + options.Resources + .Get() + .AddVirtualJson("/Volo/Abp/FeatureManagement/Localization/ApplicationContracts"); + }); + } + } +} diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/Localization/ApplicationContracts/en.json b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/Localization/ApplicationContracts/en.json new file mode 100644 index 0000000000..92e4e9582b --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/Localization/ApplicationContracts/en.json @@ -0,0 +1,6 @@ +{ + "culture": "en", + "texts": { + + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo.Abp.FeatureManagement.Application.csproj b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo.Abp.FeatureManagement.Application.csproj new file mode 100644 index 0000000000..243ec87813 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo.Abp.FeatureManagement.Application.csproj @@ -0,0 +1,16 @@ + + + + + + netstandard2.0 + + + + + + + + + + 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 new file mode 100644 index 0000000000..62ee615a10 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/AbpFeatureManagementApplicationModule.cs @@ -0,0 +1,21 @@ +using Volo.Abp.AutoMapper; +using Volo.Abp.Modularity; + +namespace Volo.Abp.FeatureManagement +{ + [DependsOn( + typeof(AbpFeatureManagementDomainModule), + typeof(AbpFeatureManagementApplicationContractsModule), + typeof(AbpAutoMapperModule) + )] + public class AbpFeatureManagementApplicationModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.AddProfile(validate: true); + }); + } + } +} diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureManagementApplicationAutoMapperProfile.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureManagementApplicationAutoMapperProfile.cs new file mode 100644 index 0000000000..91579601d2 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureManagementApplicationAutoMapperProfile.cs @@ -0,0 +1,12 @@ +using AutoMapper; + +namespace Volo.Abp.FeatureManagement +{ + public class FeatureManagementApplicationAutoMapperProfile : Profile + { + public FeatureManagementApplicationAutoMapperProfile() + { + + } + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo.Abp.FeatureManagement.Domain.Shared.csproj b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo.Abp.FeatureManagement.Domain.Shared.csproj new file mode 100644 index 0000000000..44c36fd1d1 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo.Abp.FeatureManagement.Domain.Shared.csproj @@ -0,0 +1,14 @@ + + + + + + netstandard2.0 + + + + + + + + diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainSharedModule.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainSharedModule.cs new file mode 100644 index 0000000000..7931f91374 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainSharedModule.cs @@ -0,0 +1,20 @@ +using Volo.Abp.FeatureManagement.Localization; +using Volo.Abp.Localization; +using Volo.Abp.Modularity; + +namespace Volo.Abp.FeatureManagement +{ + [DependsOn( + typeof(AbpLocalizationModule) + )] + public class AbpFeatureManagementDomainSharedModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.Resources.Add("en"); + }); + } + } +} diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/FeatureManagementDomainErrorCodes.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/FeatureManagementDomainErrorCodes.cs new file mode 100644 index 0000000000..010c64efbb --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/FeatureManagementDomainErrorCodes.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.FeatureManagement +{ + public static class FeatureManagementDomainErrorCodes + { + + } +} diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/AbpFeatureManagementResource.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/AbpFeatureManagementResource.cs new file mode 100644 index 0000000000..32291a60a1 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/AbpFeatureManagementResource.cs @@ -0,0 +1,10 @@ +using Volo.Abp.Localization; + +namespace Volo.Abp.FeatureManagement.Localization +{ + [LocalizationResourceName("AbpFeatureManagement")] + public class AbpFeatureManagementResource + { + + } +} 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 new file mode 100644 index 0000000000..9a3ebac4f3 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo.Abp.FeatureManagement.Domain.csproj @@ -0,0 +1,28 @@ + + + + + + netstandard2.0 + + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 0000000000..515db2b03f --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainModule.cs @@ -0,0 +1,45 @@ +using Volo.Abp.Caching; +using Volo.Abp.FeatureManagement.Localization; +using Volo.Abp.Features; +using Volo.Abp.Localization; +using Volo.Abp.Localization.ExceptionHandling; +using Volo.Abp.Modularity; +using Volo.Abp.VirtualFileSystem; + +namespace Volo.Abp.FeatureManagement +{ + [DependsOn( + typeof(AbpFeatureManagementDomainSharedModule), + typeof(AbpFeaturesModule), + typeof(AbpCachingModule) + )] + public class AbpFeatureManagementDomainModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.Providers.Add(); + options.Providers.Add(); + options.Providers.Add(); + }); + + Configure(options => + { + options.FileSets.AddEmbedded(); + }); + + Configure(options => + { + options.Resources + .Get() + .AddVirtualJson("/Volo/Abp/FeatureManagement/Localization/Domain"); + }); + + Configure(options => + { + options.MapCodeNamespace("AbpFeatureManagement", typeof(AbpFeatureManagementResource)); + }); + } + } +} 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/EditionFeatureManagementProvider.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/EditionFeatureManagementProvider.cs new file mode 100644 index 0000000000..d904ef124a --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/EditionFeatureManagementProvider.cs @@ -0,0 +1,32 @@ +using System.Security.Principal; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Features; +using Volo.Abp.Security.Claims; + +namespace Volo.Abp.FeatureManagement +{ + public class EditionFeatureManagementProvider : FeatureManagementProvider, ITransientDependency + { + public override string Name => EditionFeatureValueProvider.ProviderName; + + protected ICurrentPrincipalAccessor PrincipalAccessor { get; } + + public EditionFeatureManagementProvider( + IFeatureManagementStore store, + ICurrentPrincipalAccessor principalAccessor) + : base(store) + { + PrincipalAccessor = principalAccessor; + } + + protected override string NormalizeProviderKey(string providerKey) + { + if (providerKey != null) + { + return providerKey; + } + + return PrincipalAccessor.Principal?.FindEditionId()?.ToString("N"); + } + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/EditionFeatureManagerExtensions.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/EditionFeatureManagerExtensions.cs new file mode 100644 index 0000000000..9cad051d5f --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/EditionFeatureManagerExtensions.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Volo.Abp.Features; + +namespace Volo.Abp.FeatureManagement +{ + public static class EditionFeatureManagerExtensions + { + public static Task GetOrNullForEditionAsync(this IFeatureManager featureManager, [NotNull] string name, Guid editionId, bool fallback = true) + { + return featureManager.GetOrNullAsync(name, EditionFeatureValueProvider.ProviderName, editionId.ToString("N"), fallback); + } + + public static Task> GetAllForEditionAsync(this IFeatureManager featureManager, Guid editionId, bool fallback = true) + { + return featureManager.GetAllAsync(EditionFeatureValueProvider.ProviderName, editionId.ToString("N"), fallback); + } + + public static Task SetForEditionAsync(this IFeatureManager featureManager, Guid editionId, [NotNull] string name, [CanBeNull] string value, bool forceToSet = false) + { + return featureManager.SetAsync(name, value, EditionFeatureValueProvider.ProviderName, editionId.ToString("N"), forceToSet); + } + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementConsts.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementConsts.cs new file mode 100644 index 0000000000..d78f1d75f7 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementConsts.cs @@ -0,0 +1,9 @@ +namespace Volo.Abp.FeatureManagement +{ + public static class FeatureManagementConsts + { + public const string DefaultDbTablePrefix = "Abp"; + + public const string DefaultDbSchema = null; + } +} 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 new file mode 100644 index 0000000000..23d5ea7d50 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureStore.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Features; + +namespace Volo.Abp.FeatureManagement +{ + public class FeatureStore : IFeatureStore, ITransientDependency + { + protected IFeatureManagementStore FeatureManagementStore { get; } + + public FeatureStore(IFeatureManagementStore featureManagementStore) + { + FeatureManagementStore = featureManagementStore; + } + + public Task GetOrNullAsync( + string name, + string providerName, + string providerKey) + { + return FeatureManagementStore.GetOrNullAsync(name, providerName, providerKey); + } + } +} diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValue.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValue.cs new file mode 100644 index 0000000000..78f0f97bab --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValue.cs @@ -0,0 +1,42 @@ +using System; +using JetBrains.Annotations; +using Volo.Abp.Domain.Entities; + +namespace Volo.Abp.FeatureManagement +{ + public class FeatureValue : Entity, IAggregateRoot + { + [NotNull] + public virtual string Name { get; protected set; } + + [NotNull] + public virtual string Value { get; internal set; } + + [NotNull] + public virtual string ProviderName { get; protected set; } + + [CanBeNull] + public virtual string ProviderKey { get; protected set; } + + protected FeatureValue() + { + + } + + public FeatureValue( + Guid id, + [NotNull] string name, + [NotNull] string value, + [NotNull] string providerName, + [CanBeNull] string providerKey) + { + Check.NotNull(name, nameof(name)); + + Id = id; + Name = Check.NotNullOrWhiteSpace(name, nameof(name)); + Value = Check.NotNullOrWhiteSpace(value, nameof(value)); + ProviderName = Check.NotNullOrWhiteSpace(providerName, nameof(providerName)); + ProviderKey = 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/FeatureValueConsts.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValueConsts.cs new file mode 100644 index 0000000000..0fd1a644fa --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValueConsts.cs @@ -0,0 +1,13 @@ +namespace Volo.Abp.FeatureManagement +{ + public static class FeatureValueConsts + { + public const int MaxNameLength = 128; + + public const int MaxProviderNameLength = 64; + + public const int MaxProviderKeyLength = 64; + + public const int MaxValueLength = 128; + } +} 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/IFeatureValueRepository.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureValueRepository.cs new file mode 100644 index 0000000000..c630d045a1 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureValueRepository.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; + +namespace Volo.Abp.FeatureManagement +{ + public interface IFeatureValueRepository : IBasicRepository + { + Task FindAsync(string name, string providerName, string providerKey); + + Task> GetListAsync(string providerName, string providerKey); + } +} diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/Localization/Domain/en.json b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/Localization/Domain/en.json new file mode 100644 index 0000000000..92e4e9582b --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/Localization/Domain/en.json @@ -0,0 +1,6 @@ +{ + "culture": "en", + "texts": { + + } +} \ 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..b03fc43d8a --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/TenantFeatureManagementProvider.cs @@ -0,0 +1,31 @@ +using Volo.Abp.DependencyInjection; +using Volo.Abp.Features; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.FeatureManagement +{ + public class TenantFeatureManagementProvider : FeatureManagementProvider, ITransientDependency + { + public override string Name => TenantFeatureValueProvider.ProviderName; + + protected ICurrentTenant CurrentTenant { get; } + + public TenantFeatureManagementProvider( + IFeatureManagementStore store, + ICurrentTenant currentTenant) + : base(store) + { + CurrentTenant = currentTenant; + } + + protected override string NormalizeProviderKey(string providerKey) + { + if (providerKey != null) + { + return providerKey; + } + + return CurrentTenant.Id?.ToString("N"); + } + } +} \ 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..972c8f7d53 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/TenantFeatureManagerExtensions.cs @@ -0,0 +1,26 @@ +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> GetAllForTenantAsync(this IFeatureManager featureManager, Guid tenantId, bool fallback = true) + { + return featureManager.GetAllAsync(TenantFeatureValueProvider.ProviderName, tenantId.ToString(), 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); + } + } +} diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo.Abp.FeatureManagement.EntityFrameworkCore.csproj b/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo.Abp.FeatureManagement.EntityFrameworkCore.csproj new file mode 100644 index 0000000000..776a415939 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo.Abp.FeatureManagement.EntityFrameworkCore.csproj @@ -0,0 +1,18 @@ + + + + + + netstandard2.0 + + + + + + + + + + + + diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/AbpFeatureManagementEntityFrameworkCoreModule.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/AbpFeatureManagementEntityFrameworkCoreModule.cs new file mode 100644 index 0000000000..4612a52e22 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/AbpFeatureManagementEntityFrameworkCoreModule.cs @@ -0,0 +1,23 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Modularity; + +namespace Volo.Abp.FeatureManagement.EntityFrameworkCore +{ + [DependsOn( + typeof(AbpFeatureManagementDomainModule), + typeof(AbpEntityFrameworkCoreModule) + )] + public class AbpFeatureManagementEntityFrameworkCoreModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAbpDbContext(options => + { + options.AddDefaultRepositories(); + + options.AddRepository(); + }); + } + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/EfCoreFeatureValueRepository.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/EfCoreFeatureValueRepository.cs new file mode 100644 index 0000000000..9545ac36b4 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/EfCoreFeatureValueRepository.cs @@ -0,0 +1,34 @@ +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.FeatureManagement.EntityFrameworkCore +{ + public class EfCoreFeatureValueRepository : EfCoreRepository, IFeatureValueRepository + { + public EfCoreFeatureValueRepository(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(); + } + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureManagementDbContext.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureManagementDbContext.cs new file mode 100644 index 0000000000..61452f178a --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureManagementDbContext.cs @@ -0,0 +1,33 @@ +using Microsoft.EntityFrameworkCore; +using Volo.Abp.Data; +using Volo.Abp.EntityFrameworkCore; + +namespace Volo.Abp.FeatureManagement.EntityFrameworkCore +{ + [ConnectionStringName("AbpFeatureManagement")] + public class FeatureManagementDbContext : AbpDbContext, IFeatureManagementDbContext + { + public static string TablePrefix { get; set; } = FeatureManagementConsts.DefaultDbTablePrefix; + + public static string Schema { get; set; } = FeatureManagementConsts.DefaultDbSchema; + + public DbSet FeatureValues { get; set; } + + public FeatureManagementDbContext(DbContextOptions options) + : base(options) + { + + } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + builder.ConfigureFeatureManagement(options => + { + options.TablePrefix = TablePrefix; + options.Schema = Schema; + }); + } + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureManagementDbContextModelCreatingExtensions.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureManagementDbContextModelCreatingExtensions.cs new file mode 100644 index 0000000000..19abd9d6a5 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureManagementDbContextModelCreatingExtensions.cs @@ -0,0 +1,31 @@ +using System; +using Microsoft.EntityFrameworkCore; + +namespace Volo.Abp.FeatureManagement.EntityFrameworkCore +{ + public static class FeatureManagementDbContextModelCreatingExtensions + { + public static void ConfigureFeatureManagement( + this ModelBuilder builder, + Action optionsAction = null) + { + Check.NotNull(builder, nameof(builder)); + + var options = new FeatureManagementModelBuilderConfigurationOptions(); + + optionsAction?.Invoke(options); + + builder.Entity(b => + { + b.ToTable(options.TablePrefix + "FeatureValues", options.Schema); + + b.Property(x => x.Name).HasMaxLength(FeatureValueConsts.MaxNameLength).IsRequired(); + b.Property(x => x.Value).HasMaxLength(FeatureValueConsts.MaxValueLength).IsRequired(); + b.Property(x => x.ProviderName).HasMaxLength(FeatureValueConsts.MaxProviderNameLength); + b.Property(x => x.ProviderKey).HasMaxLength(FeatureValueConsts.MaxProviderKeyLength); + + b.HasIndex(x => new { x.Name, x.ProviderName, x.ProviderKey }); + }); + } + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureManagementModelBuilderConfigurationOptions.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureManagementModelBuilderConfigurationOptions.cs new file mode 100644 index 0000000000..6567288188 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureManagementModelBuilderConfigurationOptions.cs @@ -0,0 +1,18 @@ +using JetBrains.Annotations; +using Volo.Abp.EntityFrameworkCore.Modeling; + +namespace Volo.Abp.FeatureManagement.EntityFrameworkCore +{ + public class FeatureManagementModelBuilderConfigurationOptions : ModelBuilderConfigurationOptions + { + public FeatureManagementModelBuilderConfigurationOptions( + [NotNull] string tablePrefix = FeatureManagementConsts.DefaultDbTablePrefix, + [CanBeNull] string schema = FeatureManagementConsts.DefaultDbSchema) + : base( + tablePrefix, + schema) + { + + } + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/IFeatureManagementDbContext.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/IFeatureManagementDbContext.cs new file mode 100644 index 0000000000..e687c9f0f6 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/IFeatureManagementDbContext.cs @@ -0,0 +1,12 @@ +using Microsoft.EntityFrameworkCore; +using Volo.Abp.Data; +using Volo.Abp.EntityFrameworkCore; + +namespace Volo.Abp.FeatureManagement.EntityFrameworkCore +{ + [ConnectionStringName("AbpFeatureManagement")] + public interface IFeatureManagementDbContext : IEfCoreDbContext + { + DbSet FeatureValues { get; set; } + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi.Client/Volo.Abp.FeatureManagement.HttpApi.Client.csproj b/modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi.Client/Volo.Abp.FeatureManagement.HttpApi.Client.csproj new file mode 100644 index 0000000000..a4e1ce7e62 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi.Client/Volo.Abp.FeatureManagement.HttpApi.Client.csproj @@ -0,0 +1,18 @@ + + + + + + netstandard2.0 + + + + + + + + + + + + diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi.Client/Volo/Abp/FeatureManagement/AbpFeatureManagementHttpApiClientModule.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi.Client/Volo/Abp/FeatureManagement/AbpFeatureManagementHttpApiClientModule.cs new file mode 100644 index 0000000000..002a7c3a9a --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi.Client/Volo/Abp/FeatureManagement/AbpFeatureManagementHttpApiClientModule.cs @@ -0,0 +1,22 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Http.Client; +using Volo.Abp.Modularity; + +namespace Volo.Abp.FeatureManagement +{ + [DependsOn( + typeof(AbpFeatureManagementApplicationContractsModule), + typeof(AbpHttpClientModule))] + public class AbpFeatureManagementHttpApiClientModule : AbpModule + { + public const string RemoteServiceName = "AbpFeatureManagement"; + + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddHttpClientProxies( + typeof(AbpFeatureManagementApplicationContractsModule).Assembly, + RemoteServiceName + ); + } + } +} diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi/Volo.Abp.FeatureManagement.HttpApi.csproj b/modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi/Volo.Abp.FeatureManagement.HttpApi.csproj new file mode 100644 index 0000000000..5b01128a6d --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi/Volo.Abp.FeatureManagement.HttpApi.csproj @@ -0,0 +1,18 @@ + + + + + + netstandard2.0 + + + + + + + + + + + + diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi/Volo/Abp/FeatureManagement/AbpFeatureManagementHttpApiModule.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi/Volo/Abp/FeatureManagement/AbpFeatureManagementHttpApiModule.cs new file mode 100644 index 0000000000..27cf7fa605 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi/Volo/Abp/FeatureManagement/AbpFeatureManagementHttpApiModule.cs @@ -0,0 +1,13 @@ +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.Modularity; + +namespace Volo.Abp.FeatureManagement +{ + [DependsOn( + typeof(AbpFeatureManagementApplicationContractsModule), + typeof(AbpAspNetCoreMvcModule))] + public class AbpFeatureManagementHttpApiModule : AbpModule + { + + } +} diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo.Abp.FeatureManagement.MongoDB.csproj b/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo.Abp.FeatureManagement.MongoDB.csproj new file mode 100644 index 0000000000..2c03150aa9 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo.Abp.FeatureManagement.MongoDB.csproj @@ -0,0 +1,18 @@ + + + + + + netstandard2.0 + + + + + + + + + + + + diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/AbpFeatureManagementMongoDbModule.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/AbpFeatureManagementMongoDbModule.cs new file mode 100644 index 0000000000..6fad479721 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/AbpFeatureManagementMongoDbModule.cs @@ -0,0 +1,23 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; +using Volo.Abp.MongoDB; + +namespace Volo.Abp.FeatureManagement.MongoDB +{ + [DependsOn( + typeof(AbpFeatureManagementDomainModule), + typeof(AbpMongoDbModule) + )] + public class AbpFeatureManagementMongoDbModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddMongoDbContext(options => + { + options.AddDefaultRepositories(); + + options.AddRepository(); + }); + } + } +} diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/FeatureManagementMongoDbContext.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/FeatureManagementMongoDbContext.cs new file mode 100644 index 0000000000..02b39921f4 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/FeatureManagementMongoDbContext.cs @@ -0,0 +1,24 @@ +using MongoDB.Driver; +using Volo.Abp.Data; +using Volo.Abp.MongoDB; + +namespace Volo.Abp.FeatureManagement.MongoDB +{ + [ConnectionStringName("AbpFeatureManagement")] + public class FeatureManagementMongoDbContext : AbpMongoDbContext, IFeatureManagementMongoDbContext + { + public static string CollectionPrefix { get; set; } = FeatureManagementConsts.DefaultDbTablePrefix; + + public IMongoCollection FeatureValues => Collection(); + + protected override void CreateModel(IMongoModelBuilder modelBuilder) + { + base.CreateModel(modelBuilder); + + modelBuilder.ConfigureFeatureManagement(options => + { + options.CollectionPrefix = CollectionPrefix; + }); + } + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/FeatureManagementMongoDbContextExtensions.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/FeatureManagementMongoDbContextExtensions.cs new file mode 100644 index 0000000000..852efd0576 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/FeatureManagementMongoDbContextExtensions.cs @@ -0,0 +1,24 @@ +using System; +using Volo.Abp.MongoDB; + +namespace Volo.Abp.FeatureManagement.MongoDB +{ + public static class FeatureManagementMongoDbContextExtensions + { + public static void ConfigureFeatureManagement( + this IMongoModelBuilder builder, + Action optionsAction = null) + { + Check.NotNull(builder, nameof(builder)); + + var options = new FeatureManagementMongoModelBuilderConfigurationOptions(); + + optionsAction?.Invoke(options); + + builder.Entity(b => + { + b.CollectionName = options.CollectionPrefix + "FeatureValues"; + }); + } + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/FeatureManagementMongoModelBuilderConfigurationOptions.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/FeatureManagementMongoModelBuilderConfigurationOptions.cs new file mode 100644 index 0000000000..88361e345d --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/FeatureManagementMongoModelBuilderConfigurationOptions.cs @@ -0,0 +1,15 @@ +using JetBrains.Annotations; +using Volo.Abp.MongoDB; + +namespace Volo.Abp.FeatureManagement.MongoDB +{ + public class FeatureManagementMongoModelBuilderConfigurationOptions : MongoModelBuilderConfigurationOptions + { + public FeatureManagementMongoModelBuilderConfigurationOptions( + [NotNull] string tablePrefix = FeatureManagementConsts.DefaultDbTablePrefix) + : base(tablePrefix) + { + + } + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/IFeatureManagementMongoDbContext.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/IFeatureManagementMongoDbContext.cs new file mode 100644 index 0000000000..faf782f66d --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/IFeatureManagementMongoDbContext.cs @@ -0,0 +1,12 @@ +using MongoDB.Driver; +using Volo.Abp.Data; +using Volo.Abp.MongoDB; + +namespace Volo.Abp.FeatureManagement.MongoDB +{ + [ConnectionStringName("AbpFeatureManagement")] + public interface IFeatureManagementMongoDbContext : IAbpMongoDbContext + { + IMongoCollection FeatureValues { get; } + } +} diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/MongoFeatureValueRepository.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/MongoFeatureValueRepository.cs new file mode 100644 index 0000000000..f57d125dce --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/MongoFeatureValueRepository.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using MongoDB.Driver; +using MongoDB.Driver.Linq; +using Volo.Abp.Domain.Repositories.MongoDB; +using Volo.Abp.MongoDB; + +namespace Volo.Abp.FeatureManagement.MongoDB +{ + public class MongoFeatureValueRepository : MongoDbRepository, IFeatureValueRepository + { + public MongoFeatureValueRepository(IMongoDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + + } + + public async Task FindAsync(string name, string providerName, string providerKey) + { + return await GetMongoQueryable() + .FirstOrDefaultAsync(s => s.Name == name && s.ProviderName == providerName && s.ProviderKey == providerKey); + } + + public async Task> GetListAsync(string providerName, string providerKey) + { + return await GetMongoQueryable() + .Where(s => s.ProviderName == providerName && s.ProviderKey == providerKey) + .ToListAsync(); + } + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/AbpFeatureManagementWebModule.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/AbpFeatureManagementWebModule.cs new file mode 100644 index 0000000000..1d5f99c086 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/AbpFeatureManagementWebModule.cs @@ -0,0 +1,58 @@ +using Localization.Resources.AbpUi; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.AspNetCore.Mvc.Localization; +using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; +using Volo.Abp.AutoMapper; +using Volo.Abp.FeatureManagement.Localization; +using Volo.Abp.Localization; +using Volo.Abp.Localization.Resources.AbpValidation; +using Volo.Abp.Modularity; +using Volo.Abp.VirtualFileSystem; + +namespace Volo.Abp.FeatureManagement +{ + [DependsOn( + typeof(AbpFeatureManagementHttpApiModule), + typeof(AbpAspNetCoreMvcUiThemeSharedModule), + typeof(AbpAutoMapperModule) + )] + public class AbpFeatureManagementWebModule : AbpModule + { + public override void PreConfigureServices(ServiceConfigurationContext context) + { + context.Services.PreConfigure(options => + { + options.AddAssemblyResource(typeof(AbpFeatureManagementResource), typeof(AbpFeatureManagementWebModule).Assembly); + }); + } + + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.FileSets.AddEmbedded("Volo.Abp.FeatureManagement"); + }); + + Configure(options => + { + options.Resources + .Get() + .AddBaseTypes( + typeof(AbpValidationResource), + typeof(AbpUiResource) + ).AddVirtualJson("/Localization/Resources/FeatureManagement"); + }); + + Configure(options => + { + options.AddProfile(validate: true); + }); + + Configure(options => + { + //Configure authorization. + }); + } + } +} diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/FeatureManagementWebAutoMapperProfile.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/FeatureManagementWebAutoMapperProfile.cs new file mode 100644 index 0000000000..a08370d890 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/FeatureManagementWebAutoMapperProfile.cs @@ -0,0 +1,12 @@ +using AutoMapper; + +namespace Volo.Abp.FeatureManagement +{ + public class FeatureManagementWebAutoMapperProfile : Profile + { + public FeatureManagementWebAutoMapperProfile() + { + //Create mappings. + } + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Localization/Resources/FeatureManagement/en.json b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Localization/Resources/FeatureManagement/en.json new file mode 100644 index 0000000000..92e4e9582b --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Localization/Resources/FeatureManagement/en.json @@ -0,0 +1,6 @@ +{ + "culture": "en", + "texts": { + + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/_ViewImports.cshtml b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/_ViewImports.cshtml new file mode 100644 index 0000000000..c1da1f5f10 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/_ViewImports.cshtml @@ -0,0 +1,4 @@ +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Properties/launchSettings.json b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Properties/launchSettings.json new file mode 100644 index 0000000000..23a4efbb61 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:56993/", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Abp.FeatureManagement.Web": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:56994/" + } + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Volo.Abp.FeatureManagement.Web.csproj b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Volo.Abp.FeatureManagement.Web.csproj new file mode 100644 index 0000000000..3175e1143a --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Volo.Abp.FeatureManagement.Web.csproj @@ -0,0 +1,36 @@ + + + + + + netstandard2.0 + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + true + Library + Volo.Abp.FeatureManagement + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo.Abp.FeatureManagement.Application.Tests.csproj b/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo.Abp.FeatureManagement.Application.Tests.csproj new file mode 100644 index 0000000000..1c41a820e6 --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo.Abp.FeatureManagement.Application.Tests.csproj @@ -0,0 +1,19 @@ + + + + + + netcoreapp2.2 + + + + + + + + + + + + + diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/AbpFeatureManagementApplicationTestModule.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/AbpFeatureManagementApplicationTestModule.cs new file mode 100644 index 0000000000..fb65739dca --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/AbpFeatureManagementApplicationTestModule.cs @@ -0,0 +1,13 @@ +using Volo.Abp.Modularity; + +namespace Volo.Abp.FeatureManagement +{ + [DependsOn( + typeof(AbpFeatureManagementApplicationModule), + typeof(AbpFeatureManagementDomainTestModule) + )] + public class AbpFeatureManagementApplicationTestModule : AbpModule + { + + } +} diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo.Abp.FeatureManagement.Domain.Tests.csproj b/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo.Abp.FeatureManagement.Domain.Tests.csproj new file mode 100644 index 0000000000..242262f35e --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo.Abp.FeatureManagement.Domain.Tests.csproj @@ -0,0 +1,18 @@ + + + + + + netcoreapp2.2 + + + + + + + + + + + + diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainTestModule.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainTestModule.cs new file mode 100644 index 0000000000..03a7e715ed --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainTestModule.cs @@ -0,0 +1,13 @@ +using Volo.Abp.FeatureManagement.EntityFrameworkCore; +using Volo.Abp.Modularity; + +namespace Volo.Abp.FeatureManagement +{ + [DependsOn( + typeof(AbpFeatureManagementEntityFrameworkCoreTestModule) + )] + public class AbpFeatureManagementDomainTestModule : AbpModule + { + + } +} diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureManagementDomainTestBase.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureManagementDomainTestBase.cs new file mode 100644 index 0000000000..a5307e711c --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureManagementDomainTestBase.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.FeatureManagement +{ + public abstract class FeatureManagementDomainTestBase : FeatureManagementTestBase + { + + } +} \ No newline at end of file diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureManager_Tests.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureManager_Tests.cs new file mode 100644 index 0000000000..bae9041a51 --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureManager_Tests.cs @@ -0,0 +1,83 @@ +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Features; +using Xunit; + +namespace Volo.Abp.FeatureManagement +{ + public class FeatureManager_Tests : FeatureManagementDomainTestBase + { + private readonly IFeatureManager _featureManager; + + public FeatureManager_Tests() + { + _featureManager = GetRequiredService(); + } + + [Fact] + public async Task Should_Get_A_FeatureValue_For_A_Provider() + { + //Default values + + (await _featureManager.GetOrNullDefaultAsync( + TestFeatureDefinitionProvider.SocialLogins + )).ShouldBeNull(); + + (await _featureManager.GetOrNullDefaultAsync( + TestFeatureDefinitionProvider.DailyAnalysis + )).ShouldBe(false.ToString().ToLowerInvariant()); + + (await _featureManager.GetOrNullDefaultAsync( + TestFeatureDefinitionProvider.ProjectCount + )).ShouldBe("1"); + + (await _featureManager.GetOrNullDefaultAsync( + TestFeatureDefinitionProvider.BackupCount + )).ShouldBe("0"); + + //"Enterprise" edition values + + (await _featureManager.GetOrNullForEditionAsync( + TestFeatureDefinitionProvider.SocialLogins, + TestEditionIds.Enterprise + )).ShouldBe(true.ToString().ToLowerInvariant()); + + (await _featureManager.GetOrNullForEditionAsync( + TestFeatureDefinitionProvider.DailyAnalysis, + TestEditionIds.Enterprise + )).ShouldBe(false.ToString().ToLowerInvariant()); + + (await _featureManager.GetOrNullForEditionAsync( + TestFeatureDefinitionProvider.ProjectCount, + TestEditionIds.Enterprise + )).ShouldBe("3"); + + (await _featureManager.GetOrNullForEditionAsync( + TestFeatureDefinitionProvider.BackupCount, + TestEditionIds.Enterprise + )).ShouldBe("5"); + + //"Ultimate" edition values + + (await _featureManager.GetOrNullForEditionAsync( + TestFeatureDefinitionProvider.SocialLogins, + TestEditionIds.Ultimate + )).ShouldBe(true.ToString().ToLowerInvariant()); + + (await _featureManager.GetOrNullForEditionAsync( + TestFeatureDefinitionProvider.DailyAnalysis, + TestEditionIds.Ultimate + )).ShouldBe(true.ToString().ToLowerInvariant()); + + (await _featureManager.GetOrNullForEditionAsync( + TestFeatureDefinitionProvider.ProjectCount, + TestEditionIds.Ultimate + )).ShouldBe("10"); + + (await _featureManager.GetOrNullForEditionAsync( + TestFeatureDefinitionProvider.BackupCount, + TestEditionIds.Ultimate + )).ShouldBe("10"); + } + } +} diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests.csproj b/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests.csproj new file mode 100644 index 0000000000..27d7612baa --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests.csproj @@ -0,0 +1,22 @@ + + + + + + netcoreapp2.2 + + + + + + + + + + + + + + + + diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/AbpFeatureManagementEntityFrameworkCoreTestModule.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/AbpFeatureManagementEntityFrameworkCoreTestModule.cs new file mode 100644 index 0000000000..7a1eef410e --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/AbpFeatureManagementEntityFrameworkCoreTestModule.cs @@ -0,0 +1,41 @@ +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Modularity; + +namespace Volo.Abp.FeatureManagement.EntityFrameworkCore +{ + [DependsOn( + typeof(AbpFeatureManagementTestBaseModule), + typeof(AbpFeatureManagementEntityFrameworkCoreModule) + )] + public class AbpFeatureManagementEntityFrameworkCoreTestModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + var sqliteConnection = CreateDatabaseAndGetConnection(); + + Configure(options => + { + options.Configure(abpDbContextConfigurationContext => + { + abpDbContextConfigurationContext.DbContextOptions.UseSqlite(sqliteConnection); + }); + }); + } + + private static SqliteConnection CreateDatabaseAndGetConnection() + { + var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + + new FeatureManagementDbContext( + new DbContextOptionsBuilder().UseSqlite(connection).Options + ).GetService().CreateTables(); + + return connection; + } + } +} diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureValueRepositoryTests.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureValueRepositoryTests.cs new file mode 100644 index 0000000000..8283d80a5f --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureValueRepositoryTests.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.FeatureManagement.EntityFrameworkCore +{ + public class FeatureValueRepositoryTests : FeatureValueRepository_Tests + { + + } +} diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo.Abp.FeatureManagement.MongoDB.Tests.csproj b/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo.Abp.FeatureManagement.MongoDB.Tests.csproj new file mode 100644 index 0000000000..8bed0f0aa9 --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo.Abp.FeatureManagement.MongoDB.Tests.csproj @@ -0,0 +1,20 @@ + + + + + + netcoreapp2.2 + + + + + + + + + + + + + + diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/AbpFeatureManagementMongoDbTestModule.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/AbpFeatureManagementMongoDbTestModule.cs new file mode 100644 index 0000000000..bc5dfa0b52 --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/AbpFeatureManagementMongoDbTestModule.cs @@ -0,0 +1,30 @@ +using Mongo2Go; +using Volo.Abp.Data; +using Volo.Abp.Modularity; + +namespace Volo.Abp.FeatureManagement.MongoDB +{ + [DependsOn( + typeof(AbpFeatureManagementTestBaseModule), + typeof(AbpFeatureManagementMongoDbModule) + )] + public class AbpFeatureManagementMongoDbTestModule : AbpModule + { + private MongoDbRunner _mongoDbRunner; + + public override void ConfigureServices(ServiceConfigurationContext context) + { + _mongoDbRunner = MongoDbRunner.Start(); + + Configure(options => + { + options.ConnectionStrings.Default = _mongoDbRunner.ConnectionString; + }); + } + + public override void OnApplicationShutdown(ApplicationShutdownContext context) + { + _mongoDbRunner.Dispose(); + } + } +} \ No newline at end of file diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/FeatureValueRepositoryTests.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/FeatureValueRepositoryTests.cs new file mode 100644 index 0000000000..6184e4c90f --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/FeatureValueRepositoryTests.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.FeatureManagement.MongoDB +{ + public class FeatureValueRepositoryTests : FeatureValueRepository_Tests + { + + } +} diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo.Abp.FeatureManagement.TestBase.csproj b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo.Abp.FeatureManagement.TestBase.csproj new file mode 100644 index 0000000000..83288c97ec --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo.Abp.FeatureManagement.TestBase.csproj @@ -0,0 +1,29 @@ + + + + + + netcoreapp2.2 + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/AbpFeatureManagementTestBaseModule.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/AbpFeatureManagementTestBaseModule.cs new file mode 100644 index 0000000000..bf6169b488 --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/AbpFeatureManagementTestBaseModule.cs @@ -0,0 +1,36 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Authorization; +using Volo.Abp.Autofac; +using Volo.Abp.Modularity; + +namespace Volo.Abp.FeatureManagement +{ + [DependsOn( + typeof(AbpAutofacModule), + typeof(AbpTestBaseModule), + typeof(AbpAuthorizationModule), + typeof(AbpFeatureManagementDomainModule) + )] + public class AbpFeatureManagementTestBaseModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAlwaysAllowAuthorization(); + } + + 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/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestBase.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestBase.cs new file mode 100644 index 0000000000..66ab983ebe --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestBase.cs @@ -0,0 +1,13 @@ +using Volo.Abp.Modularity; + +namespace Volo.Abp.FeatureManagement +{ + public abstract class FeatureManagementTestBase : AbpIntegratedTest + where TStartupModule : IAbpModule + { + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + } +} diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestData.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestData.cs new file mode 100644 index 0000000000..e6d2273307 --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestData.cs @@ -0,0 +1,9 @@ +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.FeatureManagement +{ + public class FeatureManagementTestData : ISingletonDependency + { + + } +} diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestDataBuilder.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestDataBuilder.cs new file mode 100644 index 0000000000..f402288fa3 --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestDataBuilder.cs @@ -0,0 +1,192 @@ +using Volo.Abp.DependencyInjection; +using Volo.Abp.Features; +using Volo.Abp.Guids; + +namespace Volo.Abp.FeatureManagement +{ + public class FeatureManagementTestDataBuilder : ITransientDependency + { + private readonly IFeatureValueRepository _featureValueRepository; + private readonly IGuidGenerator _guidGenerator; + private readonly FeatureManagementTestData _testData; + + public FeatureManagementTestDataBuilder( + IGuidGenerator guidGenerator, + FeatureManagementTestData testData, + IFeatureValueRepository featureValueRepository) + { + _guidGenerator = guidGenerator; + _testData = testData; + _featureValueRepository = featureValueRepository; + } + + public void Build() + { + #region "Regular" edition features + + //SocialLogins + _featureValueRepository.Insert( + new FeatureValue( + _guidGenerator.Create(), + TestFeatureDefinitionProvider.SocialLogins, + true.ToString().ToLowerInvariant(), + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString("N") + ) + ); + + //UserCount + _featureValueRepository.Insert( + new FeatureValue( + _guidGenerator.Create(), + TestFeatureDefinitionProvider.UserCount, + "10", + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString("N") + ) + ); + + //ProjectCount + _featureValueRepository.Insert( + new FeatureValue( + _guidGenerator.Create(), + TestFeatureDefinitionProvider.ProjectCount, + "1", + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString("N") + ) + ); + + #endregion + + #region "Enterprise" edition features + + //SocialLogins + _featureValueRepository.Insert( + new FeatureValue( + _guidGenerator.Create(), + TestFeatureDefinitionProvider.SocialLogins, + true.ToString().ToLowerInvariant(), + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Enterprise.ToString("N") + ) + ); + + //EmailSupport + _featureValueRepository.Insert( + new FeatureValue( + _guidGenerator.Create(), + TestFeatureDefinitionProvider.EmailSupport, + true.ToString().ToLowerInvariant(), + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Enterprise.ToString("N") + ) + ); + + //UserCount + _featureValueRepository.Insert( + new FeatureValue( + _guidGenerator.Create(), + TestFeatureDefinitionProvider.UserCount, + "20", + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Enterprise.ToString("N") + ) + ); + + //ProjectCount + _featureValueRepository.Insert( + new FeatureValue( + _guidGenerator.Create(), + TestFeatureDefinitionProvider.ProjectCount, + "3", + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Enterprise.ToString("N") + ) + ); + + //BackupCount + _featureValueRepository.Insert( + new FeatureValue( + _guidGenerator.Create(), + TestFeatureDefinitionProvider.BackupCount, + "5", + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Enterprise.ToString("N") + ) + ); + + #endregion + + #region "Ultimate" edition features + + //SocialLogins + _featureValueRepository.Insert( + new FeatureValue( + _guidGenerator.Create(), + TestFeatureDefinitionProvider.SocialLogins, + true.ToString().ToLowerInvariant(), + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Ultimate.ToString("N") + ) + ); + + //EmailSupport + _featureValueRepository.Insert( + new FeatureValue( + _guidGenerator.Create(), + TestFeatureDefinitionProvider.EmailSupport, + true.ToString().ToLowerInvariant(), + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Ultimate.ToString("N") + ) + ); + + //EmailSupport + _featureValueRepository.Insert( + new FeatureValue( + _guidGenerator.Create(), + TestFeatureDefinitionProvider.DailyAnalysis, + true.ToString().ToLowerInvariant(), + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Ultimate.ToString("N") + ) + ); + + //UserCount + _featureValueRepository.Insert( + new FeatureValue( + _guidGenerator.Create(), + TestFeatureDefinitionProvider.UserCount, + "100", + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Ultimate.ToString("N") + ) + ); + + //ProjectCount + _featureValueRepository.Insert( + new FeatureValue( + _guidGenerator.Create(), + TestFeatureDefinitionProvider.ProjectCount, + "10", + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Ultimate.ToString("N") + ) + ); + + //BackupCount + _featureValueRepository.Insert( + new FeatureValue( + _guidGenerator.Create(), + TestFeatureDefinitionProvider.BackupCount, + "10", + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Ultimate.ToString("N") + ) + ); + + #endregion + } + } +} \ No newline at end of file diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureValueRepository_Tests.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureValueRepository_Tests.cs new file mode 100644 index 0000000000..4ec3d2f437 --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureValueRepository_Tests.cs @@ -0,0 +1,64 @@ +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Features; +using Volo.Abp.Modularity; +using Xunit; + +namespace Volo.Abp.FeatureManagement +{ + public abstract class FeatureValueRepository_Tests : FeatureManagementTestBase + where TStartupModule : IAbpModule + { + protected IFeatureValueRepository Repository { get; set; } + + protected FeatureValueRepository_Tests() + { + Repository = GetRequiredService(); + } + + [Fact] + public async Task FindAsync() + { + //feature value does exists + + var featureValue = await Repository.FindAsync( + TestFeatureDefinitionProvider.ProjectCount, + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Enterprise.ToString("N") + ); + + featureValue.ShouldNotBeNull(); + featureValue.Value.ShouldBe("3"); + + //feature value does not exists + featureValue = await Repository.FindAsync( + TestFeatureDefinitionProvider.ProjectCount, + EditionFeatureValueProvider.ProviderName, + "undefined-edition-id" + ); + + featureValue.ShouldBeNull(); + } + + [Fact] + public async Task GetListAsync() + { + var featureValues = await Repository.GetListAsync( + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Enterprise.ToString("N") + ); + + featureValues.Count.ShouldBeGreaterThan(0); + + featureValues.ShouldContain( + fv => fv.Name == TestFeatureDefinitionProvider.SocialLogins && + fv.Value == "true" + ); + + featureValues.ShouldContain( + fv => fv.Name == TestFeatureDefinitionProvider.ProjectCount && + fv.Value == "3" + ); + } + } +} diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/TestEditionIds.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/TestEditionIds.cs new file mode 100644 index 0000000000..540631cd45 --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/TestEditionIds.cs @@ -0,0 +1,18 @@ +using System; + +namespace Volo.Abp.FeatureManagement +{ + public static class TestEditionIds + { + public static Guid Regular { get; } + public static Guid Enterprise { get; } + public static Guid Ultimate { get; } + + static TestEditionIds() + { + Regular = Guid.Parse("532599ab-c0c0-4345-a04a-e322867b6e15"); + Enterprise = Guid.Parse("27e50758-1feb-436a-be4f-cae8519e0cb2"); + Ultimate = Guid.Parse("6ea78c22-be32-497e-aaba-a2332c564c5e"); + } + } +} \ No newline at end of file diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/TestFeatureDefinitionProvider.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/TestFeatureDefinitionProvider.cs new file mode 100644 index 0000000000..8c4dfdf06b --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/TestFeatureDefinitionProvider.cs @@ -0,0 +1,54 @@ +using Volo.Abp.Features; +using Volo.Abp.Validation.StringValues; + +namespace Volo.Abp.FeatureManagement +{ + public class TestFeatureDefinitionProvider : FeatureDefinitionProvider + { + public const string SocialLogins = "SocialLogins"; + public const string EmailSupport = "EmailSupport"; + public const string DailyAnalysis = "DailyAnalysis"; + public const string UserCount = "UserCount"; + public const string ProjectCount = "ProjectCount"; + public const string BackupCount = "BackupCount"; + + public override void Define(IFeatureDefinitionContext context) + { + var group = context.AddGroup("TestGroup"); + + group.AddFeature( + SocialLogins, + valueType: new ToggleStringValueType() + ); + + group.AddFeature( + EmailSupport, + valueType: new ToggleStringValueType() + ); + + group.AddFeature( + DailyAnalysis, + defaultValue: false.ToString().ToLowerInvariant(), //Optional, it is already false by default + valueType: new ToggleStringValueType() + ); + + group.AddFeature( + UserCount, + defaultValue: "1", + valueType: new FreeTextStringValueType(new NumericValueValidator(1, 1000)) + ); + + group.AddFeature( + ProjectCount, + defaultValue: "1", + valueType: new FreeTextStringValueType(new NumericValueValidator(1, 10)) + ); + + group.AddFeature( + BackupCount, + defaultValue: "0", + valueType: new FreeTextStringValueType(new NumericValueValidator(0, 10)) + ); + } + } +} diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/AbpIdentityApplicationContractsModule.cs b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/AbpIdentityApplicationContractsModule.cs index 9a45258f4f..821457ac93 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/AbpIdentityApplicationContractsModule.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/AbpIdentityApplicationContractsModule.cs @@ -7,6 +7,7 @@ using Volo.Abp.Modularity; using Volo.Abp.PermissionManagement; using Volo.Abp.Users; using Volo.Abp.VirtualFileSystem; +using Volo.Abp.Localization.ExceptionHandling; namespace Volo.Abp.Identity { @@ -21,11 +22,6 @@ namespace Volo.Abp.Identity { public override void ConfigureServices(ServiceConfigurationContext context) { - Configure(options => - { - options.DefinitionProviders.Add(); - }); - Configure(options => { options.FileSets.AddEmbedded(); @@ -37,6 +33,11 @@ namespace Volo.Abp.Identity .Get() .AddVirtualJson("/Volo/Abp/Identity/Localization/ApplicationContracts"); }); + + Configure(options => + { + options.MapCodeNamespace("Volo.Abp.Identity", typeof(IdentityResource)); + }); } } } \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityErrorCodes.cs b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityErrorCodes.cs new file mode 100644 index 0000000000..4bdfc5d8e9 --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityErrorCodes.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Volo.Abp.Identity +{ + public static class IdentityErrorCodes + { + public const string UserSelfDeletion = "Volo.Abp.Identity:010001"; + } +} \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/Localization/ApplicationContracts/en.json b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/Localization/ApplicationContracts/en.json index 4a302c8057..22464dae71 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/Localization/ApplicationContracts/en.json +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/Localization/ApplicationContracts/en.json @@ -1,6 +1,7 @@ { "culture": "en", "texts": { + "Volo.Abp.Identity:010001": "You can not delete your own account!", "Permission:IdentityManagement": "Identity management", "Permission:RoleManagement": "Role management", "Permission:Create": "Create", diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/Localization/ApplicationContracts/tr.json b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/Localization/ApplicationContracts/tr.json index 25760c354b..4e0746de1f 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/Localization/ApplicationContracts/tr.json +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/Localization/ApplicationContracts/tr.json @@ -1,6 +1,7 @@ { "culture": "tr", "texts": { + "Volo.Abp.Identity:010001": "Kendi hesabınızı silemezsiniz!", "Permission:IdentityManagement": "Kimlik yönetimi", "Permission:RoleManagement": "Rol yönetimi", "Permission:Create": "Oluşturma", diff --git a/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityUserAppService.cs b/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityUserAppService.cs index e1fb6ba8e5..814d32fd19 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityUserAppService.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityUserAppService.cs @@ -80,6 +80,11 @@ namespace Volo.Abp.Identity [Authorize(IdentityPermissions.Users.Delete)] public async Task DeleteAsync(Guid id) { + if (CurrentUser.Id.Value == id) + { + throw new BusinessException(code: IdentityErrorCodes.UserSelfDeletion); + } + var user = await _userManager.FindByIdAsync(id.ToString()); if (user == null) { diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo.Abp.Identity.Domain.csproj b/modules/identity/src/Volo.Abp.Identity.Domain/Volo.Abp.Identity.Domain.csproj index 733c9195c6..b0cff474c1 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo.Abp.Identity.Domain.csproj +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo.Abp.Identity.Domain.csproj @@ -17,22 +17,22 @@ + + + + + + - - - - - - diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityDomainModule.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityDomainModule.cs index 0b4c5b3fac..067c0b43c9 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityDomainModule.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityDomainModule.cs @@ -7,8 +7,6 @@ using Volo.Abp.EventBus.Distributed; using Volo.Abp.Identity.Localization; using Volo.Abp.Localization; using Volo.Abp.Modularity; -using Volo.Abp.SettingManagement; -using Volo.Abp.Settings; using Volo.Abp.Users; using Volo.Abp.VirtualFileSystem; @@ -17,18 +15,12 @@ namespace Volo.Abp.Identity [DependsOn( typeof(AbpDddDomainModule), typeof(AbpIdentityDomainSharedModule), - typeof(AbpUsersDomainModule), - typeof(AbpSettingManagementDomainModule) + typeof(AbpUsersDomainModule) )] public class AbpIdentityDomainModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { - Configure(options => - { - options.DefinitionProviders.Add(); - }); - Configure(options => { options.FileSets.AddEmbedded(); diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDataSeeder.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDataSeeder.cs index 0ff080cfad..09718049de 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDataSeeder.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDataSeeder.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; using Volo.Abp.DependencyInjection; @@ -53,7 +52,7 @@ namespace Volo.Abp.Identity adminUser = new IdentityUser(_guidGenerator.Create(), adminUserName, "admin@abp.io", tenantId); adminUser.Name = adminUserName; - CheckIdentityErrors(await _userManager.CreateAsync(adminUser, adminUserPassword)); + (await _userManager.CreateAsync(adminUser, adminUserPassword)).CheckErrors(); result.CreatedAdminUser = true; //"admin" role @@ -65,24 +64,13 @@ namespace Volo.Abp.Identity adminRole.IsStatic = true; adminRole.IsPublic = true; - CheckIdentityErrors(await _roleManager.CreateAsync(adminRole)); + (await _roleManager.CreateAsync(adminRole)).CheckErrors(); result.CreatedAdminRole = true; } - CheckIdentityErrors(await _userManager.AddToRoleAsync(adminUser, adminRoleName)); + (await _userManager.AddToRoleAsync(adminUser, adminRoleName)).CheckErrors(); return result; } - - protected void CheckIdentityErrors(IdentityResult identityResult) //TODO: This is temporary and duplicate code! - { - if (!identityResult.Succeeded) - { - //TODO: A better exception that can be shown on UI as localized? - throw new AbpException("Operation failed: " + identityResult.Errors.Select(e => $"[{e.Code}] {e.Description}").JoinAsString(", ")); - } - - //identityResult.CheckErrors(LocalizationManager); //TODO: Get from old Abp - } } } diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Navigation/AbpIdentityWebMainMenuContributor.cs b/modules/identity/src/Volo.Abp.Identity.Web/Navigation/AbpIdentityWebMainMenuContributor.cs index 22581401be..f607e62bd3 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Navigation/AbpIdentityWebMainMenuContributor.cs +++ b/modules/identity/src/Volo.Abp.Identity.Web/Navigation/AbpIdentityWebMainMenuContributor.cs @@ -16,11 +16,13 @@ namespace Volo.Abp.Identity.Web.Navigation return; } + var administrationMenu = context.Menu.GetAdministration(); + var authorizationService = context.ServiceProvider.GetRequiredService(); var l = context.ServiceProvider.GetRequiredService>(); var identityMenuItem = new ApplicationMenuItem(IdentityMenuNames.GroupName, l["Menu:IdentityManagement"], icon: "fa fa-id-card-o"); - context.Menu.AddItem(identityMenuItem); + administrationMenu.AddItem(identityMenuItem); if (await authorizationService.IsGrantedAsync(IdentityPermissions.Roles.Default)) { diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Shared/PersonalSettingsModal.cshtml b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Shared/PersonalSettingsModal.cshtml index 29a0600148..4c18be54eb 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Shared/PersonalSettingsModal.cshtml +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Shared/PersonalSettingsModal.cshtml @@ -7,11 +7,11 @@ @using Volo.Abp.Settings @model PersonalSettingsModal @inject IHtmlLocalizer L -@inject ISettingManager SettingManager +@inject ISettingProvider SettingProvider @{ Layout = null; - var isUserNameUpdateEnabled = await SettingManager.IsTrueAsync(IdentitySettingNames.User.IsUserNameUpdateEnabled); - var isEmailUpdateEnabled = await SettingManager.IsTrueAsync(IdentitySettingNames.User.IsEmailUpdateEnabled); + var isUserNameUpdateEnabled = await SettingProvider.IsTrueAsync(IdentitySettingNames.User.IsUserNameUpdateEnabled); + var isEmailUpdateEnabled = await SettingProvider.IsTrueAsync(IdentitySettingNames.User.IsEmailUpdateEnabled); }
diff --git a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityDomainTestModule.cs b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityDomainTestModule.cs index 16dd6149de..cbb52f02ba 100644 --- a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityDomainTestModule.cs +++ b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityDomainTestModule.cs @@ -13,14 +13,6 @@ namespace Volo.Abp.Identity )] public class AbpIdentityDomainTestModule : AbpModule { - public override void ConfigureServices(ServiceConfigurationContext context) - { - Configure(options => - { - options.DefinitionProviders.Add(); - }); - } - public override void OnApplicationInitialization(ApplicationInitializationContext context) { SeedTestData(context); diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo.Abp.IdentityServer.Domain.csproj b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo.Abp.IdentityServer.Domain.csproj index fc2969a97b..01b2f8da5a 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo.Abp.IdentityServer.Domain.csproj +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo.Abp.IdentityServer.Domain.csproj @@ -18,6 +18,7 @@ + diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerDomainModule.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerDomainModule.cs index 9b7ea50fc1..dc06ce130d 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerDomainModule.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerDomainModule.cs @@ -2,6 +2,7 @@ using IdentityServer4.Stores; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AutoMapper; +using Volo.Abp.Caching; using Volo.Abp.Domain; using Volo.Abp.Identity; using Volo.Abp.IdentityServer.Clients; @@ -14,7 +15,8 @@ namespace Volo.Abp.IdentityServer typeof(AbpIdentityServerDomainSharedModule), typeof(AbpAutoMapperModule), typeof(AbpIdentityDomainModule), - typeof(AbpSecurityModule) + typeof(AbpSecurityModule), + typeof(AbpCachingModule) )] public class AbpIdentityServerDomainModule : AbpModule { diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionGrant.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionGrant.cs index cd8d7d3395..7ec99fbe3a 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionGrant.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionGrant.cs @@ -5,6 +5,7 @@ using Volo.Abp.MultiTenancy; namespace Volo.Abp.PermissionManagement { + //TODO: To aggregate root? public class PermissionGrant : Entity, IMultiTenant { public virtual Guid? TenantId { get; protected set; } @@ -15,7 +16,7 @@ namespace Volo.Abp.PermissionManagement [NotNull] public virtual string ProviderName { get; protected set; } - [NotNull] + [CanBeNull] public virtual string ProviderKey { get; protected set; } protected PermissionGrant() @@ -27,14 +28,14 @@ namespace Volo.Abp.PermissionManagement Guid id, [NotNull] string name, [NotNull] string providerName , - [NotNull] string providerKey, + [CanBeNull] string providerKey, Guid? tenantId = null) { Check.NotNull(name, nameof(name)); Id = id; - Name = name; - ProviderName = providerName; + Name = Check.NotNullOrWhiteSpace(name, nameof(name)); + ProviderName = Check.NotNullOrWhiteSpace(providerName, nameof(providerName)); ProviderKey = providerKey; TenantId = tenantId; } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManagementProvider.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManagementProvider.cs index 01251029ee..0b83f37fd1 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManagementProvider.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManagementProvider.cs @@ -1,5 +1,4 @@ using System.Threading.Tasks; -using Volo.Abp.Authorization.Permissions; using Volo.Abp.Guids; using Volo.Abp.MultiTenancy; @@ -24,8 +23,7 @@ namespace Volo.Abp.PermissionManagement GuidGenerator = guidGenerator; CurrentTenant = currentTenant; } - - + public virtual async Task CheckAsync(string name, string providerName, string providerKey) { if (providerName != Name) diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/AbpPermissionManagementTestBaseModule.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/AbpPermissionManagementTestBaseModule.cs index b5a616d1c4..4423cf114b 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/AbpPermissionManagementTestBaseModule.cs +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/AbpPermissionManagementTestBaseModule.cs @@ -12,14 +12,6 @@ namespace Volo.Abp.PermissionManagement )] public class AbpPermissionManagementTestBaseModule : AbpModule { - public override void ConfigureServices(ServiceConfigurationContext context) - { - Configure(options => - { - options.DefinitionProviders.Add(); - }); - } - public override void OnApplicationInitialization(ApplicationInitializationContext context) { SeedTestData(context); 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) diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingStore.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingStore.cs index f95887658d..91b389c354 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingStore.cs +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingStore.cs @@ -1,90 +1,21 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Volo.Abp.Caching; +using System.Threading.Tasks; using Volo.Abp.DependencyInjection; -using Volo.Abp.Guids; using Volo.Abp.Settings; namespace Volo.Abp.SettingManagement { public class SettingStore : ISettingStore, ITransientDependency { - protected IDistributedCache Cache { get; } - protected ISettingRepository SettingRepository { get; } - protected IGuidGenerator GuidGenerator { get; } + protected ISettingManagementStore ManagementStore { get; } - public SettingStore( - ISettingRepository settingRepository, - IGuidGenerator guidGenerator, - IDistributedCache cache) + public SettingStore(ISettingManagementStore managementStore) { - SettingRepository = settingRepository; - GuidGenerator = guidGenerator; - Cache = cache; + ManagementStore = managementStore; } - public async Task GetOrNullAsync(string name, string providerName, string providerKey) + public 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 setting = await SettingRepository.FindAsync(name, providerName, providerKey); - if (setting == null) - { - setting = new Setting(GuidGenerator.Create(), name, value, providerName, providerKey); - await SettingRepository.InsertAsync(setting); - } - else - { - setting.Value = value; - await SettingRepository.UpdateAsync(setting); - } - } - - public async Task> GetListAsync(string providerName, string providerKey) - { - var settings = await SettingRepository.GetListAsync(providerName, providerKey); - return settings.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); - } - } - - 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 setting = await SettingRepository.FindAsync(name, providerName, providerKey); - - cacheItem = new SettingCacheItem(setting?.Value); - - await Cache.SetAsync( - cacheKey, - cacheItem - ); - - return cacheItem; - } - - protected virtual string CalculateCacheKey(string name, string providerName, string providerKey) - { - return SettingCacheItem.CalculateCacheKey(name, providerName, providerKey); + return ManagementStore.GetOrNullAsync(name, providerName, providerKey); } } } diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.EntityFrameworkCore/Volo/Abp/SettingManagement/EntityFrameworkCore/AbpSettingManagementEntityFrameworkCoreModule.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.EntityFrameworkCore/Volo/Abp/SettingManagement/EntityFrameworkCore/AbpSettingManagementEntityFrameworkCoreModule.cs index 97195bd852..6b11457739 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.EntityFrameworkCore/Volo/Abp/SettingManagement/EntityFrameworkCore/AbpSettingManagementEntityFrameworkCoreModule.cs +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.EntityFrameworkCore/Volo/Abp/SettingManagement/EntityFrameworkCore/AbpSettingManagementEntityFrameworkCoreModule.cs @@ -4,13 +4,15 @@ using Volo.Abp.Modularity; namespace Volo.Abp.SettingManagement.EntityFrameworkCore { - [DependsOn(typeof(AbpSettingManagementDomainModule))] - [DependsOn(typeof(AbpEntityFrameworkCoreModule))] + [DependsOn( + typeof(AbpSettingManagementDomainModule), + typeof(AbpEntityFrameworkCoreModule) + )] public class AbpSettingManagementEntityFrameworkCoreModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { - context.Services.AddAbpDbContext(options => + context.Services.AddAbpDbContext(options => { options.AddDefaultRepositories(); diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.EntityFrameworkCore/Volo/Abp/SettingManagement/EntityFrameworkCore/AbpSettingManagementDbContext.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.EntityFrameworkCore/Volo/Abp/SettingManagement/EntityFrameworkCore/SettingManagementDbContext.cs similarity index 76% rename from modules/setting-management/src/Volo.Abp.SettingManagement.EntityFrameworkCore/Volo/Abp/SettingManagement/EntityFrameworkCore/AbpSettingManagementDbContext.cs rename to modules/setting-management/src/Volo.Abp.SettingManagement.EntityFrameworkCore/Volo/Abp/SettingManagement/EntityFrameworkCore/SettingManagementDbContext.cs index 7118aae6fe..6567782997 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.EntityFrameworkCore/Volo/Abp/SettingManagement/EntityFrameworkCore/AbpSettingManagementDbContext.cs +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.EntityFrameworkCore/Volo/Abp/SettingManagement/EntityFrameworkCore/SettingManagementDbContext.cs @@ -5,7 +5,7 @@ using Volo.Abp.EntityFrameworkCore; namespace Volo.Abp.SettingManagement.EntityFrameworkCore { [ConnectionStringName("AbpSettingManagement")] - public class AbpSettingManagementDbContext : AbpDbContext, ISettingManagementDbContext + public class SettingManagementDbContext : AbpDbContext, ISettingManagementDbContext { public static string TablePrefix { get; set; } = AbpSettingManagementConsts.DefaultDbTablePrefix; @@ -13,7 +13,7 @@ namespace Volo.Abp.SettingManagement.EntityFrameworkCore public DbSet Settings { get; set; } - public AbpSettingManagementDbContext(DbContextOptions options) + public SettingManagementDbContext(DbContextOptions options) : base(options) { diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.EntityFrameworkCore/Volo/Abp/SettingManagement/EntityFrameworkCore/AbpSettingManagementDbContextModelBuilderExtensions.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.EntityFrameworkCore/Volo/Abp/SettingManagement/EntityFrameworkCore/SettingManagementDbContextModelBuilderExtensions.cs similarity index 85% rename from modules/setting-management/src/Volo.Abp.SettingManagement.EntityFrameworkCore/Volo/Abp/SettingManagement/EntityFrameworkCore/AbpSettingManagementDbContextModelBuilderExtensions.cs rename to modules/setting-management/src/Volo.Abp.SettingManagement.EntityFrameworkCore/Volo/Abp/SettingManagement/EntityFrameworkCore/SettingManagementDbContextModelBuilderExtensions.cs index 75de647690..5c8c68b4f4 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.EntityFrameworkCore/Volo/Abp/SettingManagement/EntityFrameworkCore/AbpSettingManagementDbContextModelBuilderExtensions.cs +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.EntityFrameworkCore/Volo/Abp/SettingManagement/EntityFrameworkCore/SettingManagementDbContextModelBuilderExtensions.cs @@ -3,8 +3,9 @@ using Microsoft.EntityFrameworkCore; namespace Volo.Abp.SettingManagement.EntityFrameworkCore { - public static class AbpSettingManagementDbContextModelBuilderExtensions + public static class SettingManagementDbContextModelBuilderExtensions { + //TODO: Instead of getting parameters, get a action of SettingManagementModelBuilderConfigurationOptions like other modules public static void ConfigureSettingManagement( [NotNull] this ModelBuilder builder, [CanBeNull] string tablePrefix = AbpSettingManagementConsts.DefaultDbTablePrefix, diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.MongoDB/Volo/Abp/SettingManagement/MongoDB/AbpSettingManagementMongoDbContextExtensions.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.MongoDB/Volo/Abp/SettingManagement/MongoDB/SettingManagementMongoDbContextExtensions.cs similarity index 90% rename from modules/setting-management/src/Volo.Abp.SettingManagement.MongoDB/Volo/Abp/SettingManagement/MongoDB/AbpSettingManagementMongoDbContextExtensions.cs rename to modules/setting-management/src/Volo.Abp.SettingManagement.MongoDB/Volo/Abp/SettingManagement/MongoDB/SettingManagementMongoDbContextExtensions.cs index ee7a00d1e2..057eebf7a4 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.MongoDB/Volo/Abp/SettingManagement/MongoDB/AbpSettingManagementMongoDbContextExtensions.cs +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.MongoDB/Volo/Abp/SettingManagement/MongoDB/SettingManagementMongoDbContextExtensions.cs @@ -3,7 +3,7 @@ using Volo.Abp.MongoDB; namespace Volo.Abp.SettingManagement.MongoDB { - public static class AbpSettingManagementMongoDbContextExtensions + public static class SettingManagementMongoDbContextExtensions { public static void ConfigureSettingManagement( this IMongoModelBuilder builder, diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Navigation/SettingManagementMainMenuContributor.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Navigation/SettingManagementMainMenuContributor.cs index c6ff751d57..1fbd519bfd 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Navigation/SettingManagementMainMenuContributor.cs +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Navigation/SettingManagementMainMenuContributor.cs @@ -25,7 +25,16 @@ namespace Volo.Abp.SettingManagement.Web.Navigation //TODO: Localize //var l = context.ServiceProvider.GetRequiredService>(); - context.Menu.AddItem(new ApplicationMenuItem(SettingManagementMenuNames.GroupName, "Settings", "/SettingManagement", icon: "fa fa-cog", order: int.MaxValue - 1000)); + context.Menu + .GetAdministration() + .AddItem( + new ApplicationMenuItem( + SettingManagementMenuNames.GroupName, + "Settings", + "/SettingManagement", + icon: "fa fa-cog" + ) + ); return Task.CompletedTask; } diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Navigation/SettingManagementMenuNames.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Navigation/SettingManagementMenuNames.cs index 67678f5b9e..4c5af73abe 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Navigation/SettingManagementMenuNames.cs +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Navigation/SettingManagementMenuNames.cs @@ -1,14 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Volo.Abp.SettingManagement.Web.Navigation +namespace Volo.Abp.SettingManagement.Web.Navigation { public class SettingManagementMenuNames { public const string GroupName = "SettingManagement"; - - public const string Settings = GroupName + ".Settings"; } } diff --git a/modules/setting-management/test/Volo.Abp.SettingManagement.EntityFrameworkCore.Tests/Volo/Abp/SettingManagement/EntityFrameworkCore/AbpSettingManagementEntityFrameworkCoreTestModule.cs b/modules/setting-management/test/Volo.Abp.SettingManagement.EntityFrameworkCore.Tests/Volo/Abp/SettingManagement/EntityFrameworkCore/AbpSettingManagementEntityFrameworkCoreTestModule.cs index c392f1da64..1a234961bc 100644 --- a/modules/setting-management/test/Volo.Abp.SettingManagement.EntityFrameworkCore.Tests/Volo/Abp/SettingManagement/EntityFrameworkCore/AbpSettingManagementEntityFrameworkCoreTestModule.cs +++ b/modules/setting-management/test/Volo.Abp.SettingManagement.EntityFrameworkCore.Tests/Volo/Abp/SettingManagement/EntityFrameworkCore/AbpSettingManagementEntityFrameworkCoreTestModule.cs @@ -32,8 +32,8 @@ namespace Volo.Abp.SettingManagement.EntityFrameworkCore var connection = new SqliteConnection("Data Source=:memory:"); connection.Open(); - new AbpSettingManagementDbContext( - new DbContextOptionsBuilder().UseSqlite(connection).Options + new SettingManagementDbContext( + new DbContextOptionsBuilder().UseSqlite(connection).Options ).GetService().CreateTables(); return connection; diff --git a/modules/setting-management/test/Volo.Abp.SettingManagement.TestBase/Volo/Abp/SettingManagement/AbpSettingManagementTestBaseModule.cs b/modules/setting-management/test/Volo.Abp.SettingManagement.TestBase/Volo/Abp/SettingManagement/AbpSettingManagementTestBaseModule.cs index ca7be601f1..7d90dcd747 100644 --- a/modules/setting-management/test/Volo.Abp.SettingManagement.TestBase/Volo/Abp/SettingManagement/AbpSettingManagementTestBaseModule.cs +++ b/modules/setting-management/test/Volo.Abp.SettingManagement.TestBase/Volo/Abp/SettingManagement/AbpSettingManagementTestBaseModule.cs @@ -11,14 +11,6 @@ namespace Volo.Abp.SettingManagement typeof(AbpSettingManagementDomainModule))] public class AbpSettingManagementTestBaseModule : AbpModule { - public override void ConfigureServices(ServiceConfigurationContext context) - { - Configure(options => - { - options.DefinitionProviders.Add(); - }); - } - public override void OnApplicationInitialization(ApplicationInitializationContext context) { SeedTestData(context); diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/AbpTenantManagementApplicationContractsModule.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/AbpTenantManagementApplicationContractsModule.cs index 24af59cda2..50c6aae024 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/AbpTenantManagementApplicationContractsModule.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/AbpTenantManagementApplicationContractsModule.cs @@ -14,11 +14,6 @@ namespace Volo.Abp.TenantManagement { public override void ConfigureServices(ServiceConfigurationContext context) { - Configure(options => - { - options.DefinitionProviders.Add(); - }); - Configure(options => { options.FileSets.AddEmbedded(); diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/ITenantRepository.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/ITenantRepository.cs index f7d430c214..7b1c7e15f1 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/ITenantRepository.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/ITenantRepository.cs @@ -20,7 +20,5 @@ namespace Volo.Abp.TenantManagement string filter = null, bool includeDetails = false, CancellationToken cancellationToken = default); - - Task GetCountAsync(CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/EfCoreTenantRepository.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/EfCoreTenantRepository.cs index 0a13f77bf0..d2859f29a1 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/EfCoreTenantRepository.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/EfCoreTenantRepository.cs @@ -48,11 +48,6 @@ namespace Volo.Abp.TenantManagement.EntityFrameworkCore .ToListAsync(cancellationToken); } - public virtual async Task GetCountAsync(CancellationToken cancellationToken = default) - { - return await DbSet.LongCountAsync(cancellationToken); - } - public override IQueryable WithDetails() { return GetQueryable().IncludeDetails(); diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.MongoDB/Volo/Abp/TenantManagement/MongoDb/MongoTenantRepository.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.MongoDB/Volo/Abp/TenantManagement/MongoDb/MongoTenantRepository.cs index 05aa92757a..2b30dd5a26 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.MongoDB/Volo/Abp/TenantManagement/MongoDb/MongoTenantRepository.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.MongoDB/Volo/Abp/TenantManagement/MongoDb/MongoTenantRepository.cs @@ -28,11 +28,6 @@ namespace Volo.Abp.TenantManagement.MongoDb .FirstOrDefaultAsync(t => t.Name == name, cancellationToken); } - public virtual async Task GetCountAsync(CancellationToken cancellationToken = default) - { - return await GetMongoQueryable().LongCountAsync(cancellationToken); - } - public virtual async Task> GetListAsync( string sorting = null, int maxResultCount = int.MaxValue, diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Navigation/AbpTenantManagementWebMainMenuContributor.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Navigation/AbpTenantManagementWebMainMenuContributor.cs index 63dee4f7f7..b7ef9f277e 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Navigation/AbpTenantManagementWebMainMenuContributor.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Navigation/AbpTenantManagementWebMainMenuContributor.cs @@ -16,11 +16,13 @@ namespace Volo.Abp.TenantManagement.Web.Navigation return; } + var administrationMenu = context.Menu.GetAdministration(); + var authorizationService = context.ServiceProvider.GetRequiredService(); var l = context.ServiceProvider.GetRequiredService>(); var tenantManagementMenuItem = new ApplicationMenuItem(TenantManagementMenuNames.GroupName, l["Menu:TenantManagement"], icon: "fa fa-users"); - context.Menu.AddItem(tenantManagementMenuItem); + administrationMenu.AddItem(tenantManagementMenuItem); if (await authorizationService.IsGrantedAsync(TenantManagementPermissions.Tenants.Default)) { diff --git a/modules/tenant-management/test/Volo.Abp.TenantManagement.TestBase/Volo/Abp/TenantManagement/TenantRepository_Tests.cs b/modules/tenant-management/test/Volo.Abp.TenantManagement.TestBase/Volo/Abp/TenantManagement/TenantRepository_Tests.cs index b48299ffd1..06c75317fa 100644 --- a/modules/tenant-management/test/Volo.Abp.TenantManagement.TestBase/Volo/Abp/TenantManagement/TenantRepository_Tests.cs +++ b/modules/tenant-management/test/Volo.Abp.TenantManagement.TestBase/Volo/Abp/TenantManagement/TenantRepository_Tests.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Threading.Tasks; using Shouldly; using Volo.Abp.Modularity; -using Volo.Abp.Uow; using Xunit; namespace Volo.Abp.TenantManagement @@ -48,12 +47,6 @@ namespace Volo.Abp.TenantManagement tenant.ConnectionStrings.Count.ShouldBeGreaterThanOrEqualTo(2); } - [Fact] - public async Task GetCountAsync() - { - (await TenantRepository.GetCountAsync()).ShouldBeGreaterThan(0); - } - [Fact] public async Task GetListAsync() { diff --git a/nupkg/common.ps1 b/nupkg/common.ps1 index b6a589c0ba..8a166107e1 100644 --- a/nupkg/common.ps1 +++ b/nupkg/common.ps1 @@ -40,7 +40,7 @@ $projects = ( "framework/src/Volo.Abp.AspNetCore.TestBase", "framework/src/Volo.Abp.Auditing", "framework/src/Volo.Abp.Authorization", - "framework/src/Volo.Abp.AutoFac", + "framework/src/Volo.Abp.Autofac", "framework/src/Volo.Abp.AutoMapper", "framework/src/Volo.Abp.BackgroundJobs", "framework/src/Volo.Abp.BackgroundJobs.Abstractions", diff --git a/nupkg/common.sh b/nupkg/common.sh new file mode 100644 index 0000000000..1a553f58de --- /dev/null +++ b/nupkg/common.sh @@ -0,0 +1,192 @@ +#!/bin/bash + +# Paths +packFolder="$(pwd)" +rootFolder="$(dirname "$packFolder")" + +# List of solutions +solutions=( + "framework" + "modules/users" + "modules/setting-management" + "modules/permission-management" + "modules/tenant-management" + "modules/identity" + "modules/identityserver" + "modules/account" + "modules/audit-logging" + "modules/background-jobs" + "modules/blogging" + "modules/docs" + "abp_io" +) + +# List of projects +projects=( + "framework/src/Volo.Abp.ApiVersioning.Abstractions" + "framework/src/Volo.Abp.AspNetCore" + "framework/src/Volo.Abp.AspNetCore.Authentication.OAuth" + "framework/src/Volo.Abp.AspNetCore.MultiTenancy" + "framework/src/Volo.Abp.AspNetCore.Mvc" + "framework/src/Volo.Abp.AspNetCore.Mvc.Contracts" + "framework/src/Volo.Abp.AspNetCore.Mvc.Client" + "framework/src/Volo.Abp.AspNetCore.Mvc.UI" + "framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap" + "framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling" + "framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy" + "framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages" + "framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic" + "framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared" + "framework/src/Volo.Abp.AspNetCore.TestBase" + + "framework/src/Volo.Abp.Auditing" + "framework/src/Volo.Abp.Authorization" + "framework/src/Volo.Abp.Autofac" + "framework/src/Volo.Abp.AutoMapper" + "framework/src/Volo.Abp.BackgroundJobs" + "framework/src/Volo.Abp.BackgroundJobs.Abstractions" + "framework/src/Volo.Abp.BackgroundJobs.HangFire" + "framework/src/Volo.Abp.BackgroundJobs.RabbitMQ" + "framework/src/Volo.Abp.BackgroundWorkers" + "framework/src/Volo.Abp.Caching" + "framework/src/Volo.Abp.Castle.Core" + "framework/src/Volo.Abp.Core" + "framework/src/Volo.Abp.Data" + "framework/src/Volo.Abp.Ddd.Application" + "framework/src/Volo.Abp.Ddd.Domain" + "framework/src/Volo.Abp.Emailing" + "framework/src/Volo.Abp.EntityFrameworkCore" + "framework/src/Volo.Abp.EntityFrameworkCore.SqlServer" + "framework/src/Volo.Abp.EntityFrameworkCore.MySQL" + "framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql" + "framework/src/Volo.Abp.EventBus" + "framework/src/Volo.Abp.EventBus.RabbitMQ" + "framework/src/Volo.Abp.Features" + "framework/src/Volo.Abp.Guids" + "framework/src/Volo.Abp.HangFire" + "framework/src/Volo.Abp.Http" + "framework/src/Volo.Abp.Http.Abstractions" + "framework/src/Volo.Abp.Http.Client" + "framework/src/Volo.Abp.Http.Client.IdentityModel" + "framework/src/Volo.Abp.IdentityModel" + "framework/src/Volo.Abp.Json" + "framework/src/Volo.Abp.Localization" + "framework/src/Volo.Abp.Localization.Abstractions" + "framework/src/Volo.Abp.MemoryDb" + "framework/src/Volo.Abp.MongoDB" + "framework/src/Volo.Abp.MultiTenancy.Abstractions" + "framework/src/Volo.Abp.ObjectMapping" + "framework/src/Volo.Abp.RabbitMQ" + "framework/src/Volo.Abp.Security" + "framework/src/Volo.Abp.Serialization" + "framework/src/Volo.Abp.Settings" + "framework/src/Volo.Abp.Sms" + "framework/src/Volo.Abp.TestBase" + "framework/src/Volo.Abp.Threading" + "framework/src/Volo.Abp.Timing" + "framework/src/Volo.Abp.UI" + "framework/src/Volo.Abp.UI.Navigation" + "framework/src/Volo.Abp.Uow" + "framework/src/Volo.Abp.Validation" + "framework/src/Volo.Abp.VirtualFileSystem" + + # modules/users + "modules/users/src/Volo.Abp.Users.Abstractions" + "modules/users/src/Volo.Abp.Users.Domain" + "modules/users/src/Volo.Abp.Users.Domain.Shared" + "modules/users/src/Volo.Abp.Users.EntityFrameworkCore" + "modules/users/src/Volo.Abp.Users.MongoDB" + + # modules/setting-management + "modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared" + "modules/setting-management/src/Volo.Abp.SettingManagement.Domain" + "modules/setting-management/src/Volo.Abp.SettingManagement.EntityFrameworkCore" + "modules/setting-management/src/Volo.Abp.SettingManagement.MongoDB" + "modules/setting-management/src/Volo.Abp.SettingManagement.Web" + + # modules/permission-management + "modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared" + "modules/permission-management/src/Volo.Abp.PermissionManagement.Domain" + "modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts" + "modules/permission-management/src/Volo.Abp.PermissionManagement.Application" + "modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore" + "modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB" + "modules/permission-management/src/Volo.Abp.PermissionManagement.HttpApi" + "modules/permission-management/src/Volo.Abp.PermissionManagement.HttpApi.Client" + "modules/permission-management/src/Volo.Abp.PermissionManagement.Web" + + # modules/tenant-management + "modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared" + "modules/tenant-management/src/Volo.Abp.TenantManagement.Domain" + "modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts" + "modules/tenant-management/src/Volo.Abp.TenantManagement.Application" + "modules/tenant-management/src/Volo.Abp.TenantManagement.EntityFrameworkCore" + "modules/tenant-management/src/Volo.Abp.TenantManagement.MongoDB" + "modules/tenant-management/src/Volo.Abp.TenantManagement.HttpApi" + "modules/tenant-management/src/Volo.Abp.TenantManagement.HttpApi.Client" + "modules/tenant-management/src/Volo.Abp.TenantManagement.Web" + + # modules/identity + "modules/identity/src/Volo.Abp.Identity.AspNetCore" + + "modules/identity/src/Volo.Abp.Identity.Application" + "modules/identity/src/Volo.Abp.Identity.Application.Contracts" + "modules/identity/src/Volo.Abp.Identity.Domain" + "modules/identity/src/Volo.Abp.Identity.Domain.Shared" + "modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore" + "modules/identity/src/Volo.Abp.Identity.MongoDB" + "modules/identity/src/Volo.Abp.Identity.HttpApi" + "modules/identity/src/Volo.Abp.Identity.HttpApi.Client" + "modules/identity/src/Volo.Abp.Identity.Web" + + "modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity" + + # modules/identityserver + "modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared" + "modules/identityserver/src/Volo.Abp.IdentityServer.Domain" + "modules/identityserver/src/Volo.Abp.PermissionManagement.Domain.IdentityServer" + "modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore" + + "modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB" + + # modules/account + "modules/account/src/Volo.Abp.Account.Web" + "modules/account/src/Volo.Abp.Account.Web.IdentityServer" + + # modules/docs + "modules/docs/src/Volo.Docs.Application" + "modules/docs/src/Volo.Docs.Application.Contracts" + "modules/docs/src/Volo.Docs.Domain" + "modules/docs/src/Volo.Docs.Domain.Shared" + "modules/docs/src/Volo.Docs.EntityFrameworkCore" + "modules/docs/src/Volo.Docs.HttpApi" + "modules/docs/src/Volo.Docs.HttpApi.Client" + "modules/docs/src/Volo.Docs.Web" + + + # modules/blogging + "modules/blogging/src/Volo.Blogging.Application" + "modules/blogging/src/Volo.Blogging.Application.Contracts" + "modules/blogging/src/Volo.Blogging.Domain" + "modules/blogging/src/Volo.Blogging.Domain.Shared" + "modules/blogging/src/Volo.Blogging.EntityFrameworkCore" + "modules/blogging/src/Volo.Blogging.MongoDB" + "modules/blogging/src/Volo.Blogging.HttpApi" + "modules/blogging/src/Volo.Blogging.HttpApi.Client" + "modules/blogging/src/Volo.Blogging.Web" + + # modules/audit-logging + "modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared" + "modules/audit-logging/src/Volo.Abp.AuditLogging.Domain" + "modules/audit-logging/src/Volo.Abp.AuditLogging.EntityFrameworkCore" + "modules/audit-logging/src/Volo.Abp.AuditLogging.MongoDB" + + # modules/background-jobs + "modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain.Shared" + "modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain" + "modules/background-jobs/src/Volo.Abp.BackgroundJobs.EntityFrameworkCore" + "modules/background-jobs/src/Volo.Abp.BackgroundJobs.MongoDB" + + # abp_io/SolutionTemplating + "abp_io/src/Volo.Utils.SolutionTemplating" +) diff --git a/nupkg/pack.sh b/nupkg/pack.sh new file mode 100755 index 0000000000..122bedcfba --- /dev/null +++ b/nupkg/pack.sh @@ -0,0 +1,25 @@ +#!/bin/bash +set -eE +. ./common.sh + +# Rebuild all solutions +for solution in "${solutions[@]}" +do + solutionFolder="$rootFolder/$solution" + cd "$solutionFolder" \ + && dotnet restore +done + +# Create all packages +for project in "${projects[@]}" +do + projectFolder="$rootFolder/$project" + + # Create nuget pack + cd "$projectFolder" + rm -rf "$projectFolder/bin/Release" \ + & dotnet pack --no-restore -c Release -p:SourceLinkCreate=true -o "$packFolder" +done + +# Go back to the pack folder +cd "$packFolder" \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Application/BookStoreApplicationModule.cs b/samples/BookStore/src/Acme.BookStore.Application/BookStoreApplicationModule.cs index 6c8ae1df2b..55b344b1ea 100644 --- a/samples/BookStore/src/Acme.BookStore.Application/BookStoreApplicationModule.cs +++ b/samples/BookStore/src/Acme.BookStore.Application/BookStoreApplicationModule.cs @@ -15,11 +15,6 @@ namespace Acme.BookStore { public override void ConfigureServices(ServiceConfigurationContext context) { - Configure(options => - { - options.DefinitionProviders.Add(); - }); - Configure(options => { options.AddProfile(); diff --git a/samples/BookStore/src/Acme.BookStore.Domain/BookStoreDomainModule.cs b/samples/BookStore/src/Acme.BookStore.Domain/BookStoreDomainModule.cs index 044cbbb827..abd062308b 100644 --- a/samples/BookStore/src/Acme.BookStore.Domain/BookStoreDomainModule.cs +++ b/samples/BookStore/src/Acme.BookStore.Domain/BookStoreDomainModule.cs @@ -30,11 +30,6 @@ namespace Acme.BookStore .AddBaseTypes(typeof(AbpValidationResource)) .AddVirtualJson("/Localization/BookStore"); }); - - Configure(options => - { - options.DefinitionProviders.Add(); - }); } } } diff --git a/samples/MicroserviceDemo/applications/AuthServer.Host/BrandingProvider.cs b/samples/MicroserviceDemo/applications/AuthServer.Host/BrandingProvider.cs new file mode 100644 index 0000000000..820fec9adf --- /dev/null +++ b/samples/MicroserviceDemo/applications/AuthServer.Host/BrandingProvider.cs @@ -0,0 +1,11 @@ +using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Components; +using Volo.Abp.DependencyInjection; + +namespace AuthServer.Host +{ + public class BrandingProvider : IBrandingProvider, ISingletonDependency + { + public string AppName => "Authentication Server"; + public string LogoUrl => null; + } +} diff --git a/samples/MicroserviceDemo/applications/AuthServer.Host/Pages/Index.cshtml b/samples/MicroserviceDemo/applications/AuthServer.Host/Pages/Index.cshtml index 53bcfed077..0a1f18c6f1 100644 --- a/samples/MicroserviceDemo/applications/AuthServer.Host/Pages/Index.cshtml +++ b/samples/MicroserviceDemo/applications/AuthServer.Host/Pages/Index.cshtml @@ -2,22 +2,32 @@ @using Volo.Abp.Users @model AuthServer.Host.Pages.IndexModel @inject ICurrentUser CurrentUser -

Running the AuthServer.Host application!

-

Current User

-
    -
  • IsAuthenticated: @CurrentUser.IsAuthenticated
  • -
  • UserName: @CurrentUser.UserName
  • -
  • Email: @CurrentUser.Email
  • -
  • Roles: @CurrentUser.Roles.JoinAsString(", ")
  • - @*
  • Claims: @CurrentUser.GetAllClaims().Select(c => $"{c.Type}={c.Value}").JoinAsString(" | ")
  • *@ -
  • TenantId: @CurrentUser.TenantId
  • -
- @if (CurrentUser.IsAuthenticated) { - Logout +
+ + + + Logout + + +

@CurrentUser.UserName

+
@CurrentUser.Email
+
+ Roles: @CurrentUser.Roles.JoinAsString(", ") +
+ Claims:
+ @Html.Raw(CurrentUser.GetAllClaims().Select(c => $"{c.Type}={c.Value}").JoinAsString("
")) +
+
+
+
} -else + +@if (!CurrentUser.IsAuthenticated) { - Login +
+

+ Login +
} \ No newline at end of file diff --git a/samples/MicroserviceDemo/applications/AuthServer.Host/wwwroot/images/anonymous-user.png b/samples/MicroserviceDemo/applications/AuthServer.Host/wwwroot/images/anonymous-user.png new file mode 100644 index 0000000000..fdae8cbe7f Binary files /dev/null and b/samples/MicroserviceDemo/applications/AuthServer.Host/wwwroot/images/anonymous-user.png differ diff --git a/samples/MicroserviceDemo/applications/BackendAdminApp.Host/BrandingProvider.cs b/samples/MicroserviceDemo/applications/BackendAdminApp.Host/BrandingProvider.cs new file mode 100644 index 0000000000..b0f5b10dd2 --- /dev/null +++ b/samples/MicroserviceDemo/applications/BackendAdminApp.Host/BrandingProvider.cs @@ -0,0 +1,11 @@ +using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Components; +using Volo.Abp.DependencyInjection; + +namespace BackendAdminApp.Host +{ + public class BrandingProvider : IBrandingProvider, ISingletonDependency + { + public string AppName => "Backend Admin App"; + public string LogoUrl => null; + } +} diff --git a/samples/MicroserviceDemo/applications/ConsoleClientDemo/ClientDemoService.cs b/samples/MicroserviceDemo/applications/ConsoleClientDemo/ClientDemoService.cs index 4066e21156..1560c383d1 100644 --- a/samples/MicroserviceDemo/applications/ConsoleClientDemo/ClientDemoService.cs +++ b/samples/MicroserviceDemo/applications/ConsoleClientDemo/ClientDemoService.cs @@ -51,15 +51,18 @@ namespace ConsoleClientDemo { await _authenticator.AuthenticateAsync(client); - var response = await client.GetAsync(_remoteServiceOptions.RemoteServices.Default.BaseUrl.EnsureEndsWith('/') + "Test/Index"); + var url = GetServerUrl() + "Test/Index"; + + var response = await client.GetAsync(url); + if (!response.IsSuccessStatusCode) { Console.WriteLine(response.StatusCode); } else { - var content = await response.Content.ReadAsStringAsync(); - Console.WriteLine(content); + var responseContent = await response.Content.ReadAsStringAsync(); + Console.WriteLine(responseContent); } } } @@ -113,6 +116,7 @@ namespace ConsoleClientDemo try { var output = await _productAppService.GetListAsync(); + Console.WriteLine("Total product count: " + output.Items.Count); foreach (var product in output.Items) @@ -125,5 +129,10 @@ namespace ConsoleClientDemo Console.WriteLine(e); } } + + private string GetServerUrl() + { + return _remoteServiceOptions.RemoteServices.Default.BaseUrl.EnsureEndsWith('/'); + } } } \ No newline at end of file diff --git a/samples/MicroserviceDemo/applications/PublicWebSite.Host/BrandingProvider.cs b/samples/MicroserviceDemo/applications/PublicWebSite.Host/BrandingProvider.cs new file mode 100644 index 0000000000..57f7a94f27 --- /dev/null +++ b/samples/MicroserviceDemo/applications/PublicWebSite.Host/BrandingProvider.cs @@ -0,0 +1,11 @@ +using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Components; +using Volo.Abp.DependencyInjection; + +namespace PublicWebSite.Host +{ + public class BrandingProvider : IBrandingProvider, ISingletonDependency + { + public string AppName => "Public Web Site"; + public string LogoUrl => null; + } +} diff --git a/samples/MicroserviceDemo/applications/PublicWebSite.Host/Pages/Index.cshtml b/samples/MicroserviceDemo/applications/PublicWebSite.Host/Pages/Index.cshtml index 4f80454947..671521a5cb 100644 --- a/samples/MicroserviceDemo/applications/PublicWebSite.Host/Pages/Index.cshtml +++ b/samples/MicroserviceDemo/applications/PublicWebSite.Host/Pages/Index.cshtml @@ -2,16 +2,152 @@ @using Volo.Abp.Users @model PublicWebSite.Host.Pages.IndexModel @inject ICurrentUser CurrentUser -

Public Web Site Application

-

Welcome...

- -@if (CurrentUser.IsAuthenticated) -{ - Logout +@section styles{ + } -else -{ - - - -} \ No newline at end of file + +
+
+

Public Web Site Application

+

Welcome to our web site...

+

+ This is a simple hero unit, a simple jumbotron-style component + for calling extra attention to featured content or information. +

+

+ + @if (CurrentUser.IsAuthenticated) + { + Logout + } + else + { +

+ +
+ } +

+
+ +
+
+
+
+
What is New?
+
+
+
+
+ +
+
+
+
Asus Transformer Book T300CHI-FH011H
+
+ 1249 +
+
+
+ +
+
+ +
+
+
+
Lego Star Wars - 75059 Sandcrawler UCS
+
+ $999 +
+
+
+ +
+
+ +
+
+
+
Nikon AF-S 50mm f/1.8 G Lens
+
+ $1499 +
+
+
+ +
+
+ +
+
+
+
What is Hot?
+
+
+
+
+
+
+ Aliquam erat volutpat. Praesent nec dapibus neque. Proin hendrerit, magna in faucibus +
+
+
+
+
+
+
+
+
Curabitur consectetur libero lacus, nec facilisis nulla facilisis non.
+
+
+
+
+
+
+
+
Duis et felis nec ipsum auctor .
+
+
+
+
+
+
+
+
Morbi eu dolor pellentesque, porttitor lorem vitae, pulvinar eros.
+
+
+
+
+ +
+
+
+
+
+
Contact Us
+
+
+ +
+ 47322 W Alexander Rd
(867) 322-5566
info@volosoft.com +
+

+ We are developing open source and commercial software development tools. +

+

+ Praesent et posuere dui. + felis. +

+
+ +
+
+
+
\ No newline at end of file diff --git a/samples/MicroserviceDemo/applications/PublicWebSite.Host/Pages/Products.cshtml b/samples/MicroserviceDemo/applications/PublicWebSite.Host/Pages/Products.cshtml index 4165781b58..8f7664dad6 100644 --- a/samples/MicroserviceDemo/applications/PublicWebSite.Host/Pages/Products.cshtml +++ b/samples/MicroserviceDemo/applications/PublicWebSite.Host/Pages/Products.cshtml @@ -3,16 +3,50 @@ @using Volo.Abp.Settings @model PublicWebSite.Host.Pages.ProductsModel @inject ISettingProvider SettingProvider -

Our Products

-
    - @foreach (var product in Model.Products.Items) +@{ + const int columnSize = 3; +} +@section styles{ + +} +

    Our Products

    +
    + @for (int i = 0; i < Math.Ceiling(Model.Products.Items.Count / (double)columnSize); i++) { -
  • - @product.Code / @product.Name / $@product.Price
    - Stock count: @product.StockCount -
  • + + @for (int j = 0; j < columnSize; j++) + { + + @if ((i * columnSize) + j < Model.Products.Items.Count) + { + var product = Model.Products.Items[(i * columnSize) + j]; +
    + @if (!product.ImageName.IsNullOrEmpty()) + { + + } + else + { + + } +
    +
    +
    +
    @product.Name
    + @product.Code - Stock count: @product.StockCount +
    +
    +
    + $@product.Price +
    +
    +
    + } +
    + } +
    } -
+ @* An example to show how to read settings from the client side / UI *@ -

Maximum allowed page size: @await SettingProvider.GetOrNullAsync(ProductManagementSettings.MaxPageSize)

\ No newline at end of file +

Maximum allowed page size: @await SettingProvider.GetOrNullAsync(ProductManagementSettings.MaxPageSize)

\ No newline at end of file diff --git a/samples/MicroserviceDemo/applications/PublicWebSite.Host/Pages/Products.css b/samples/MicroserviceDemo/applications/PublicWebSite.Host/Pages/Products.css new file mode 100644 index 0000000000..4ce2df2f4a --- /dev/null +++ b/samples/MicroserviceDemo/applications/PublicWebSite.Host/Pages/Products.css @@ -0,0 +1,30 @@ +body { + background-color: #f9f9f9; +} + +.product-list-item { + background-color: #fff; + padding: 2em; + border: 1px solid #eee; + border-radius: 3px; + box-shadow: 0 0 10px rgba(0, 0, 0, .05); + margin-bottom: 2em; + transition: all .2s; +} + +.product-list-item:hover { + box-shadow: 0 0 20px rgba(0, 0, 0, .1); + cursor: pointer; +} + +.product-list-item img { + width: 100%; +} + +.product-list-item .text-muted { + opacity: .8 +} + +.product-info-box { + min-height: 80px; +} diff --git a/samples/MicroserviceDemo/applications/PublicWebSite.Host/PublicWebSite.Host.csproj b/samples/MicroserviceDemo/applications/PublicWebSite.Host/PublicWebSite.Host.csproj index 158e41f576..b97bc35b68 100644 --- a/samples/MicroserviceDemo/applications/PublicWebSite.Host/PublicWebSite.Host.csproj +++ b/samples/MicroserviceDemo/applications/PublicWebSite.Host/PublicWebSite.Host.csproj @@ -39,6 +39,30 @@
+ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/asus.jpg b/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/asus.jpg new file mode 100644 index 0000000000..44de167813 Binary files /dev/null and b/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/asus.jpg differ diff --git a/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/beats.jpg b/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/beats.jpg new file mode 100644 index 0000000000..08417c235b Binary files /dev/null and b/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/beats.jpg differ diff --git a/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/bluecat.jpg b/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/bluecat.jpg new file mode 100644 index 0000000000..794a734a4a Binary files /dev/null and b/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/bluecat.jpg differ diff --git a/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/lego.jpg b/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/lego.jpg new file mode 100644 index 0000000000..63e31e9577 Binary files /dev/null and b/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/lego.jpg differ diff --git a/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/nikon.jpg b/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/nikon.jpg new file mode 100644 index 0000000000..eb6fb605a1 Binary files /dev/null and b/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/nikon.jpg differ diff --git a/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/noimage.jpg b/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/noimage.jpg new file mode 100644 index 0000000000..4c5133c8c9 Binary files /dev/null and b/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/noimage.jpg differ diff --git a/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/oki.jpg b/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/oki.jpg new file mode 100644 index 0000000000..6fefeec6e3 Binary files /dev/null and b/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/oki.jpg differ diff --git a/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/playstation.jpg b/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/playstation.jpg new file mode 100644 index 0000000000..880832401a Binary files /dev/null and b/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/playstation.jpg differ diff --git a/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/rampage.jpg b/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/rampage.jpg new file mode 100644 index 0000000000..e49660df17 Binary files /dev/null and b/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/rampage.jpg differ diff --git a/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/sunny.jpg b/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/sunny.jpg new file mode 100644 index 0000000000..8efff5a674 Binary files /dev/null and b/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/product-images/sunny.jpg differ diff --git a/samples/MicroserviceDemo/gateways/BackendAdminAppGateway.Host/BackendAdminAppGatewayHostModule.cs b/samples/MicroserviceDemo/gateways/BackendAdminAppGateway.Host/BackendAdminAppGatewayHostModule.cs index 660419662c..6b80cb6693 100644 --- a/samples/MicroserviceDemo/gateways/BackendAdminAppGateway.Host/BackendAdminAppGatewayHostModule.cs +++ b/samples/MicroserviceDemo/gateways/BackendAdminAppGateway.Host/BackendAdminAppGatewayHostModule.cs @@ -98,12 +98,11 @@ namespace BackendAdminAppGateway.Host options.SwaggerEndpoint("/swagger/v1/swagger.json", "BackendAdminApp Gateway API"); }); - app.MapWhen(ctx => ctx.Request.Path.ToString().StartsWith("/api/abp/") || - ctx.Request.Path.ToString().StartsWith("/Abp/"), - app2 => - { - app2.UseMvcWithDefaultRouteAndArea(); - }); + app.MapWhen( + ctx => ctx.Request.Path.ToString().StartsWith("/api/abp/") || + ctx.Request.Path.ToString().StartsWith("/Abp/"), + app2 => { app2.UseMvcWithDefaultRouteAndArea(); } + ); app.UseOcelot().Wait(); } diff --git a/samples/MicroserviceDemo/gateways/InternalGateway.Host/InternalGatewayHostModule.cs b/samples/MicroserviceDemo/gateways/InternalGateway.Host/InternalGatewayHostModule.cs index 943eaf0fc9..b4e8b23b14 100644 --- a/samples/MicroserviceDemo/gateways/InternalGateway.Host/InternalGatewayHostModule.cs +++ b/samples/MicroserviceDemo/gateways/InternalGateway.Host/InternalGatewayHostModule.cs @@ -87,14 +87,13 @@ namespace InternalGateway.Host options.SwaggerEndpoint("/swagger/v1/swagger.json", "Internal Gateway API"); }); - app.MapWhen(ctx => - ctx.Request.Path.ToString().StartsWith("/api/abp/") || + app.MapWhen( + ctx => + ctx.Request.Path.ToString().StartsWith("/api/abp/") || ctx.Request.Path.ToString().StartsWith("/Abp/") || ctx.Request.Path.ToString().StartsWith("/Test/"), - app2 => - { - app2.UseMvcWithDefaultRouteAndArea(); - }); + app2 => { app2.UseMvcWithDefaultRouteAndArea(); } + ); app.UseOcelot().Wait(); } diff --git a/samples/MicroserviceDemo/gateways/PublicWebSiteGateway.Host/PublicWebSiteGatewayHostModule.cs b/samples/MicroserviceDemo/gateways/PublicWebSiteGateway.Host/PublicWebSiteGatewayHostModule.cs index 88e74045d0..c40bac1d55 100644 --- a/samples/MicroserviceDemo/gateways/PublicWebSiteGateway.Host/PublicWebSiteGatewayHostModule.cs +++ b/samples/MicroserviceDemo/gateways/PublicWebSiteGateway.Host/PublicWebSiteGatewayHostModule.cs @@ -85,8 +85,11 @@ namespace PublicWebSiteGateway.Host options.SwaggerEndpoint("/swagger/v1/swagger.json", "PublicWebSite Gateway API"); }); - app.MapWhen(ctx => ctx.Request.Path.ToString().StartsWith("/api/abp/") || ctx.Request.Path.ToString().StartsWith("/Abp/"), - app2 => { app2.UseMvcWithDefaultRouteAndArea(); }); + app.MapWhen( + ctx => ctx.Request.Path.ToString().StartsWith("/api/abp/") || + ctx.Request.Path.ToString().StartsWith("/Abp/"), + app2 => { app2.UseMvcWithDefaultRouteAndArea(); } + ); app.UseOcelot().Wait(); } diff --git a/samples/MicroserviceDemo/k8s/auth-server-deployment.yaml b/samples/MicroserviceDemo/k8s/auth-server-deployment.yaml index b2307834d2..7ddbc30309 100644 --- a/samples/MicroserviceDemo/k8s/auth-server-deployment.yaml +++ b/samples/MicroserviceDemo/k8s/auth-server-deployment.yaml @@ -17,19 +17,17 @@ spec: - name: ASPNETCORE_ENVIRONMENT value: Development - name: ASPNETCORE_URLS - value: http://0.0.0.0:64999 + value: http://0.0.0.0:51511 - name: ConnectionStrings__Default - value: Server=sqlserver;Database=MsDemo_Identity;Trusted_Connection=True;MultipleActiveResultSets=true;User=sa;Password=yourStrong(!)Password;Integrated - Security=false + value: Server=sqlserver;Database=MsDemo_Identity;Trusted_Connection=True;MultipleActiveResultSets=true;User=sa;Password=yourStrong(!)Password;Integrated Security=false - name: RabbitMQ__Connections__Default__HostName value: rabbitmq - name: Redis__Configuration value: redis - image: microservice-demo/auth-server:latest + image: volosoft/microservice-demo-auth-server name: auth-server ports: - - containerPort: 64999 + - containerPort: 51511 resources: {} - imagePullPolicy: Never restartPolicy: Always status: {} diff --git a/samples/MicroserviceDemo/k8s/auth-server-service.yaml b/samples/MicroserviceDemo/k8s/auth-server-service.yaml index c96e9c3e3d..d317ba1fae 100644 --- a/samples/MicroserviceDemo/k8s/auth-server-service.yaml +++ b/samples/MicroserviceDemo/k8s/auth-server-service.yaml @@ -6,9 +6,8 @@ metadata: name: auth-server spec: ports: - - name: "64999" - port: 64999 - targetPort: 64999 + - port: 51511 + targetPort: 51511 selector: io.kompose.service: auth-server status: diff --git a/samples/MicroserviceDemo/k8s/backend-admin-app-deployment.yaml b/samples/MicroserviceDemo/k8s/backend-admin-app-deployment.yaml index ce6c238f7c..f26cb02806 100644 --- a/samples/MicroserviceDemo/k8s/backend-admin-app-deployment.yaml +++ b/samples/MicroserviceDemo/k8s/backend-admin-app-deployment.yaml @@ -19,16 +19,15 @@ spec: - name: ASPNETCORE_URLS value: http://0.0.0.0:80 - name: AuthServer__Authority - value: http://auth-server:64999 + value: http://auth-server:51511 - name: Redis__Configuration value: redis - name: RemoteServices__Default__BaseUrl value: http://backend-admin-app-gateway/ - image: microservice-demo/backend-admin-app:latest + image: volosoft/microservice-demo-backend-admin-app name: backend-admin-app ports: - containerPort: 80 resources: {} - imagePullPolicy: Never restartPolicy: Always status: {} diff --git a/samples/MicroserviceDemo/k8s/backend-admin-app-gateway-deployment.yaml b/samples/MicroserviceDemo/k8s/backend-admin-app-gateway-deployment.yaml index 10490961e5..c2f28623b2 100644 --- a/samples/MicroserviceDemo/k8s/backend-admin-app-gateway-deployment.yaml +++ b/samples/MicroserviceDemo/k8s/backend-admin-app-gateway-deployment.yaml @@ -19,29 +19,27 @@ spec: - name: ASPNETCORE_URLS value: http://0.0.0.0:80 - name: AuthServer__Authority - value: http://auth-server:64999 + value: http://auth-server:51511 - name: ConnectionStrings__Default - value: Server=sqlserver;Database=MsDemo_Identity;Trusted_Connection=True;MultipleActiveResultSets=true;User=sa;Password=yourStrong(!)Password;Integrated - Security=false + value: Server=sqlserver;Database=MsDemo_Identity;Trusted_Connection=True;MultipleActiveResultSets=true;User=sa;Password=yourStrong(!)Password;Integrated Security=false - name: GlobalConfiguration__BaseUrl value: http://backend-admin-app-gateway - - name: ReRoutes__0__DownstreamHostAndPorts__Host + - name: ReRoutes__0__DownstreamHostAndPorts__0__Host value: identity-service - - name: ReRoutes__0__DownstreamHostAndPorts__Port + - name: ReRoutes__0__DownstreamHostAndPorts__0__Port value: "80" - - name: ReRoutes__1__DownstreamHostAndPorts__Host + - name: ReRoutes__1__DownstreamHostAndPorts__0__Host value: product-service - - name: ReRoutes__1__DownstreamHostAndPorts__Port + - name: ReRoutes__1__DownstreamHostAndPorts__0__Port value: "80" - name: Redis__Configuration value: redis - name: RemoteServices__Default__BaseUrl value: http://backend-admin-app-gateway/ - image: microservice-demo/backend-admin-app-gateway:latest + image: volosoft/microservice-demo-backend-admin-app-gateway name: backend-admin-app-gateway ports: - containerPort: 80 resources: {} - imagePullPolicy: Never restartPolicy: Always status: {} diff --git a/samples/MicroserviceDemo/k8s/backend-admin-app-gateway-service.yaml b/samples/MicroserviceDemo/k8s/backend-admin-app-gateway-service.yaml index ca3faee62c..4864ee547a 100644 --- a/samples/MicroserviceDemo/k8s/backend-admin-app-gateway-service.yaml +++ b/samples/MicroserviceDemo/k8s/backend-admin-app-gateway-service.yaml @@ -6,8 +6,7 @@ metadata: name: backend-admin-app-gateway spec: ports: - - name: "65115" - port: 65115 + - port: 80 targetPort: 80 selector: io.kompose.service: backend-admin-app-gateway diff --git a/samples/MicroserviceDemo/k8s/backend-admin-app-service.yaml b/samples/MicroserviceDemo/k8s/backend-admin-app-service.yaml index f735fdae1c..f8df4e57de 100644 --- a/samples/MicroserviceDemo/k8s/backend-admin-app-service.yaml +++ b/samples/MicroserviceDemo/k8s/backend-admin-app-service.yaml @@ -6,8 +6,7 @@ metadata: name: backend-admin-app spec: ports: - - name: "3000" - port: 3000 + - port: 80 targetPort: 80 selector: io.kompose.service: backend-admin-app diff --git a/samples/MicroserviceDemo/k8s/blogging-service-deployment.yaml b/samples/MicroserviceDemo/k8s/blogging-service-deployment.yaml index a5b801106b..6bf8d33a8c 100644 --- a/samples/MicroserviceDemo/k8s/blogging-service-deployment.yaml +++ b/samples/MicroserviceDemo/k8s/blogging-service-deployment.yaml @@ -19,21 +19,21 @@ spec: - name: ASPNETCORE_URLS value: http://0.0.0.0:80 - name: AuthServer__Authority - value: http://auth-server:64999 + value: http://auth-server:51511 - name: ConnectionStrings__Blogging value: mongodb://mongodb|MsDemo_Blogging - name: ConnectionStrings__Default - value: Server=sqlserver;Database=MsDemo_Identity;Trusted_Connection=True;MultipleActiveResultSets=true;User=sa;Password=yourStrong(!)Password;Integrated - Security=false + value: Server=sqlserver;Database=MsDemo_Identity;Trusted_Connection=True;MultipleActiveResultSets=true;User=sa;Password=yourStrong(!)Password;Integrated Security=false - name: RabbitMQ__Connections__Default__HostName value: rabbitmq - name: Redis__Configuration value: redis - image: microservice-demo/blogging-service:latest + - name: RemoteServices__Default__BaseUrl + value: http://internal-gateway/ + image: volosoft/microservice-demo-blogging-service name: blogging-service ports: - containerPort: 80 resources: {} - imagePullPolicy: Never restartPolicy: Always status: {} diff --git a/samples/MicroserviceDemo/k8s/blogging-service-service.yaml b/samples/MicroserviceDemo/k8s/blogging-service-service.yaml index d0ab95528e..c2b359580a 100644 --- a/samples/MicroserviceDemo/k8s/blogging-service-service.yaml +++ b/samples/MicroserviceDemo/k8s/blogging-service-service.yaml @@ -6,8 +6,7 @@ metadata: name: blogging-service spec: ports: - - name: "62157" - port: 62157 + - port: 80 targetPort: 80 selector: io.kompose.service: blogging-service diff --git a/samples/MicroserviceDemo/k8s/console-client-demo-deployment.yaml b/samples/MicroserviceDemo/k8s/console-client-demo-deployment.yaml deleted file mode 100644 index 4ccfd788be..0000000000 --- a/samples/MicroserviceDemo/k8s/console-client-demo-deployment.yaml +++ /dev/null @@ -1,32 +0,0 @@ -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - labels: - io.kompose.service: console-client-demo - name: console-client-demo -spec: - replicas: 1 - strategy: {} - template: - metadata: - labels: - io.kompose.service: console-client-demo - spec: - containers: - - env: - - name: ASPNETCORE_ENVIRONMENT - value: Development - - name: ASPNETCORE_URLS - value: http://0.0.0.0:80 - - name: IdentityClients__Default__Authority - value: http://auth-server:64999 - - name: RemoteServices__Default__BaseUrl - value: http://internal-gateway/ - image: microservice-demo/console-client-demo:latest - name: console-client-demo - ports: - - containerPort: 80 - resources: {} - imagePullPolicy: Never - restartPolicy: Always -status: {} diff --git a/samples/MicroserviceDemo/k8s/console-client-demo-service.yaml b/samples/MicroserviceDemo/k8s/console-client-demo-service.yaml deleted file mode 100644 index 5146110356..0000000000 --- a/samples/MicroserviceDemo/k8s/console-client-demo-service.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - labels: - io.kompose.service: console-client-demo - name: console-client-demo -spec: - ports: - - name: "3001" - port: 3001 - targetPort: 80 - selector: - io.kompose.service: console-client-demo -status: - loadBalancer: {} diff --git a/samples/MicroserviceDemo/k8s/expose-service.yaml b/samples/MicroserviceDemo/k8s/expose-service.yaml new file mode 100644 index 0000000000..f7a7e7dfe5 --- /dev/null +++ b/samples/MicroserviceDemo/k8s/expose-service.yaml @@ -0,0 +1,47 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + io.kompose.service: auth-server + name: auth-server-expose +spec: + type: LoadBalancer + selector: + io.kompose.service: auth-server + ports: + - protocol: TCP + port: 51511 + targetPort: 51511 + nodePort: 30001 +--- +apiVersion: v1 +kind: Service +metadata: + labels: + io.kompose.service: backend-admin-app + name: backend-admin-app-expose +spec: + type: LoadBalancer + selector: + io.kompose.service: backend-admin-app + ports: + - protocol: TCP + port: 80 + targetPort: 80 + nodePort: 30002 +--- +apiVersion: v1 +kind: Service +metadata: + labels: + io.kompose.service: public-website + name: public-website-expose +spec: + type: LoadBalancer + selector: + io.kompose.service: public-website + ports: + - protocol: TCP + port: 80 + targetPort: 80 + nodePort: 30003 \ No newline at end of file diff --git a/samples/MicroserviceDemo/k8s/identity-service-deployment.yaml b/samples/MicroserviceDemo/k8s/identity-service-deployment.yaml index 4df2099e49..6179f57e44 100644 --- a/samples/MicroserviceDemo/k8s/identity-service-deployment.yaml +++ b/samples/MicroserviceDemo/k8s/identity-service-deployment.yaml @@ -19,19 +19,17 @@ spec: - name: ASPNETCORE_URLS value: http://0.0.0.0:80 - name: AuthServer__Authority - value: http://auth-server:64999 + value: http://auth-server:51511 - name: ConnectionStrings__Default - value: Server=sqlserver;Database=MsDemo_Identity;Trusted_Connection=True;MultipleActiveResultSets=true;User=sa;Password=yourStrong(!)Password;Integrated - Security=false + value: Server=sqlserver;Database=MsDemo_Identity;Trusted_Connection=True;MultipleActiveResultSets=true;User=sa;Password=yourStrong(!)Password;Integrated Security=false - name: RabbitMQ__Connections__Default__HostName value: rabbitmq - name: Redis__Configuration value: redis - image: microservice-demo/identity-service:latest + image: volosoft/microservice-demo-identity-service name: identity-service ports: - containerPort: 80 resources: {} - imagePullPolicy: Never restartPolicy: Always status: {} diff --git a/samples/MicroserviceDemo/k8s/identity-service-service.yaml b/samples/MicroserviceDemo/k8s/identity-service-service.yaml index 8c601852a3..994f120b0b 100644 --- a/samples/MicroserviceDemo/k8s/identity-service-service.yaml +++ b/samples/MicroserviceDemo/k8s/identity-service-service.yaml @@ -6,8 +6,7 @@ metadata: name: identity-service spec: ports: - - name: "63568" - port: 63568 + - port: 80 targetPort: 80 selector: io.kompose.service: identity-service diff --git a/samples/MicroserviceDemo/k8s/internal-gateway-deployment.yaml b/samples/MicroserviceDemo/k8s/internal-gateway-deployment.yaml index 4abd77f1df..9c7edf6cdb 100644 --- a/samples/MicroserviceDemo/k8s/internal-gateway-deployment.yaml +++ b/samples/MicroserviceDemo/k8s/internal-gateway-deployment.yaml @@ -19,31 +19,29 @@ spec: - name: ASPNETCORE_URLS value: http://0.0.0.0:80 - name: AuthServer__Authority - value: http://auth-server:64999 + value: http://auth-server:51511 - name: ConnectionStrings__Default - value: Server=sqlserver;Database=MsDemo_Identity;Trusted_Connection=True;MultipleActiveResultSets=true;User=sa;Password=yourStrong(!)Password;Integrated - Security=false + value: Server=sqlserver;Database=MsDemo_Identity;Trusted_Connection=True;MultipleActiveResultSets=true;User=sa;Password=yourStrong(!)Password;Integrated Security=false - name: GlobalConfiguration__BaseUrl value: http://internal-gateway - - name: ReRoutes__0__DownstreamHostAndPorts__Host + - name: ReRoutes__0__DownstreamHostAndPorts__0__Host value: identity-service - - name: ReRoutes__0__DownstreamHostAndPorts__Port + - name: ReRoutes__0__DownstreamHostAndPorts__0__Port value: "80" - - name: ReRoutes__1__DownstreamHostAndPorts__Host + - name: ReRoutes__1__DownstreamHostAndPorts__0__Host value: product-service - - name: ReRoutes__1__DownstreamHostAndPorts__Port + - name: ReRoutes__1__DownstreamHostAndPorts__0__Port value: "80" - - name: ReRoutes__2__DownstreamHostAndPorts__Host + - name: ReRoutes__2__DownstreamHostAndPorts__0__Host value: blogging-service - - name: ReRoutes__2__DownstreamHostAndPorts__Port + - name: ReRoutes__2__DownstreamHostAndPorts__0__Port value: "80" - name: Redis__Configuration value: redis - image: microservice-demo/internal-gateway:latest + image: volosoft/microservice-demo-internal-gateway name: internal-gateway ports: - containerPort: 80 resources: {} - imagePullPolicy: Never restartPolicy: Always status: {} diff --git a/samples/MicroserviceDemo/k8s/internal-gateway-service.yaml b/samples/MicroserviceDemo/k8s/internal-gateway-service.yaml index 5b392f5619..e2766c3c3a 100644 --- a/samples/MicroserviceDemo/k8s/internal-gateway-service.yaml +++ b/samples/MicroserviceDemo/k8s/internal-gateway-service.yaml @@ -6,8 +6,7 @@ metadata: name: internal-gateway spec: ports: - - name: "65129" - port: 65129 + - port: 80 targetPort: 80 selector: io.kompose.service: internal-gateway diff --git a/samples/MicroserviceDemo/k8s/migrations-deployment.yaml b/samples/MicroserviceDemo/k8s/migrations-deployment.yaml deleted file mode 100644 index bae8d1388b..0000000000 --- a/samples/MicroserviceDemo/k8s/migrations-deployment.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - labels: - io.kompose.service: migrations - name: migrations -spec: - replicas: 1 - strategy: {} - template: - metadata: - labels: - io.kompose.service: migrations - spec: - containers: - - image: microservice-demo/migrations:latest - name: migrations - resources: {} - imagePullPolicy: Never - restartPolicy: Always -status: {} diff --git a/samples/MicroserviceDemo/k8s/migrations-job.yaml b/samples/MicroserviceDemo/k8s/migrations-job.yaml deleted file mode 100644 index 257393379e..0000000000 --- a/samples/MicroserviceDemo/k8s/migrations-job.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: migrations -spec: - template: - spec: - containers: - - image: microservice-demo/migrations:latest - name: migrations - imagePullPolicy: Never - restartPolicy: Never diff --git a/samples/MicroserviceDemo/k8s/mongodb-deployment.yaml b/samples/MicroserviceDemo/k8s/mongodb-deployment.yaml index 48617c488c..938af49960 100644 --- a/samples/MicroserviceDemo/k8s/mongodb-deployment.yaml +++ b/samples/MicroserviceDemo/k8s/mongodb-deployment.yaml @@ -18,6 +18,5 @@ spec: ports: - containerPort: 27017 resources: {} - imagePullPolicy: IfNotPresent restartPolicy: Always status: {} diff --git a/samples/MicroserviceDemo/k8s/mongodb-service.yaml b/samples/MicroserviceDemo/k8s/mongodb-service.yaml index 59480974ff..34958fb121 100644 --- a/samples/MicroserviceDemo/k8s/mongodb-service.yaml +++ b/samples/MicroserviceDemo/k8s/mongodb-service.yaml @@ -6,8 +6,7 @@ metadata: name: mongodb spec: ports: - - name: "27017" - port: 27017 + - port: 27017 targetPort: 27017 selector: io.kompose.service: mongodb diff --git a/samples/MicroserviceDemo/k8s/product-service-deployment.yaml b/samples/MicroserviceDemo/k8s/product-service-deployment.yaml index 5418a8abc7..669de15ae1 100644 --- a/samples/MicroserviceDemo/k8s/product-service-deployment.yaml +++ b/samples/MicroserviceDemo/k8s/product-service-deployment.yaml @@ -19,22 +19,20 @@ spec: - name: ASPNETCORE_URLS value: http://0.0.0.0:80 - name: AuthServer__Authority - value: http://auth-server:64999 + value: http://auth-server:51511 - name: ConnectionStrings__Default value: Server=sqlserver;Database=MsDemo_Identity;Trusted_Connection=True;MultipleActiveResultSets=true;User=sa;Password=yourStrong(!)Password;Integrated Security=false - name: ConnectionStrings__ProductManagement - value: Server=sqlserver;Database=MsDemo_Identity;Trusted_Connection=True;MultipleActiveResultSets=true;User=sa;Password=yourStrong(!)Password;Integrated - Security=false + value: Server=sqlserver;Database=MsDemo_ProductManagement;Trusted_Connection=True;MultipleActiveResultSets=true;User=sa;Password=yourStrong(!)Password;Integrated Security=false - name: RabbitMQ__Connections__Default__HostName value: rabbitmq - name: Redis__Configuration value: redis - image: microservice-demo/product-service:latest + image: volosoft/microservice-demo-product-service name: product-service ports: - containerPort: 80 resources: {} - imagePullPolicy: Never restartPolicy: Always status: {} diff --git a/samples/MicroserviceDemo/k8s/product-service-service.yaml b/samples/MicroserviceDemo/k8s/product-service-service.yaml index 25b52f9d7c..3c5d1f333b 100644 --- a/samples/MicroserviceDemo/k8s/product-service-service.yaml +++ b/samples/MicroserviceDemo/k8s/product-service-service.yaml @@ -6,8 +6,7 @@ metadata: name: product-service spec: ports: - - name: "60244" - port: 60244 + - port: 80 targetPort: 80 selector: io.kompose.service: product-service diff --git a/samples/MicroserviceDemo/k8s/public-website-deployment.yaml b/samples/MicroserviceDemo/k8s/public-website-deployment.yaml index 3c8955a99b..c643f68b99 100644 --- a/samples/MicroserviceDemo/k8s/public-website-deployment.yaml +++ b/samples/MicroserviceDemo/k8s/public-website-deployment.yaml @@ -19,16 +19,15 @@ spec: - name: ASPNETCORE_URLS value: http://0.0.0.0:80 - name: AuthServer__Authority - value: http://auth-server:64999 + value: http://auth-server:51511 - name: Redis__Configuration value: redis - name: RemoteServices__Default__BaseUrl value: http://public-website-gateway/ - image: microservice-demo/public-website:latest + image: volosoft/microservice-demo-public-website name: public-website ports: - containerPort: 80 resources: {} - imagePullPolicy: Never restartPolicy: Always status: {} diff --git a/samples/MicroserviceDemo/k8s/public-website-gateway-deployment.yaml b/samples/MicroserviceDemo/k8s/public-website-gateway-deployment.yaml index 9532d9334f..802f12a7e3 100644 --- a/samples/MicroserviceDemo/k8s/public-website-gateway-deployment.yaml +++ b/samples/MicroserviceDemo/k8s/public-website-gateway-deployment.yaml @@ -19,27 +19,25 @@ spec: - name: ASPNETCORE_URLS value: http://0.0.0.0:80 - name: AuthServer__Authority - value: http://auth-server:64999 + value: http://auth-server:51511 - name: ConnectionStrings__Default - value: Server=sqlserver;Database=MsDemo_Identity;Trusted_Connection=True;MultipleActiveResultSets=true;User=sa;Password=yourStrong(!)Password;Integrated - Security=false + value: Server=sqlserver;Database=MsDemo_Identity;Trusted_Connection=True;MultipleActiveResultSets=true;User=sa;Password=yourStrong(!)Password;Integrated Security=false - name: GlobalConfiguration__BaseUrl value: http://public-website-gateway - - name: ReRoutes__0__DownstreamHostAndPorts__Host + - name: ReRoutes__0__DownstreamHostAndPorts__0__Host value: product-service - - name: ReRoutes__0__DownstreamHostAndPorts__Port + - name: ReRoutes__0__DownstreamHostAndPorts__0__Port value: "80" - - name: ReRoutes__1__DownstreamHostAndPorts__Host + - name: ReRoutes__1__DownstreamHostAndPorts__0__Host value: blogging-service - - name: ReRoutes__1__DownstreamHostAndPorts__Port + - name: ReRoutes__1__DownstreamHostAndPorts__0__Port value: "80" - name: Redis__Configuration value: redis - image: microservice-demo/public-website-gateway:latest + image: volosoft/microservice-demo-public-website-gateway name: public-website-gateway ports: - containerPort: 80 resources: {} - imagePullPolicy: Never restartPolicy: Always status: {} diff --git a/samples/MicroserviceDemo/k8s/public-website-gateway-service.yaml b/samples/MicroserviceDemo/k8s/public-website-gateway-service.yaml index 4c007686b7..5ab4e428b9 100644 --- a/samples/MicroserviceDemo/k8s/public-website-gateway-service.yaml +++ b/samples/MicroserviceDemo/k8s/public-website-gateway-service.yaml @@ -6,8 +6,7 @@ metadata: name: public-website-gateway spec: ports: - - name: "64897" - port: 64897 + - port: 80 targetPort: 80 selector: io.kompose.service: public-website-gateway diff --git a/samples/MicroserviceDemo/k8s/public-website-service.yaml b/samples/MicroserviceDemo/k8s/public-website-service.yaml index 77fac13ac3..5f5c4f56d9 100644 --- a/samples/MicroserviceDemo/k8s/public-website-service.yaml +++ b/samples/MicroserviceDemo/k8s/public-website-service.yaml @@ -6,8 +6,7 @@ metadata: name: public-website spec: ports: - - name: "3002" - port: 3002 + - port: 80 targetPort: 80 selector: io.kompose.service: public-website diff --git a/samples/MicroserviceDemo/k8s/rabbitmq-deployment.yaml b/samples/MicroserviceDemo/k8s/rabbitmq-deployment.yaml index 5f32e398c1..e455bebac6 100644 --- a/samples/MicroserviceDemo/k8s/rabbitmq-deployment.yaml +++ b/samples/MicroserviceDemo/k8s/rabbitmq-deployment.yaml @@ -19,6 +19,5 @@ spec: - containerPort: 5672 - containerPort: 15672 resources: {} - imagePullPolicy: IfNotPresent restartPolicy: Always status: {} diff --git a/samples/MicroserviceDemo/k8s/redis-deployment.yaml b/samples/MicroserviceDemo/k8s/redis-deployment.yaml index b4389df699..35d9973110 100644 --- a/samples/MicroserviceDemo/k8s/redis-deployment.yaml +++ b/samples/MicroserviceDemo/k8s/redis-deployment.yaml @@ -18,6 +18,5 @@ spec: ports: - containerPort: 6379 resources: {} - imagePullPolicy: IfNotPresent restartPolicy: Always status: {} diff --git a/samples/MicroserviceDemo/k8s/redis-service.yaml b/samples/MicroserviceDemo/k8s/redis-service.yaml index 702eaab08c..8ef5014d20 100644 --- a/samples/MicroserviceDemo/k8s/redis-service.yaml +++ b/samples/MicroserviceDemo/k8s/redis-service.yaml @@ -6,8 +6,7 @@ metadata: name: redis spec: ports: - - name: "6379" - port: 6379 + - port: 6379 targetPort: 6379 selector: io.kompose.service: redis diff --git a/samples/MicroserviceDemo/k8s/restore-database-job.yaml b/samples/MicroserviceDemo/k8s/restore-database-job.yaml new file mode 100644 index 0000000000..b35b67844f --- /dev/null +++ b/samples/MicroserviceDemo/k8s/restore-database-job.yaml @@ -0,0 +1,23 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: restore-database +spec: + template: + metadata: + name: restore-database + spec: + containers: + - env: + - name: SA_PASSWORD + value: yourStrong(!)Password + name: restore-database + image: volosoft/microservice-demo-restore-database + volumeMounts: + - mountPath: /var/opt/mssql + name: dbdata + volumes: + - name: dbdata + persistentVolumeClaim: + claimName: dbdata + restartPolicy: Never \ No newline at end of file diff --git a/samples/MicroserviceDemo/k8s/sqlserver-deployment.yaml b/samples/MicroserviceDemo/k8s/sqlserver-deployment.yaml index 568f29ab4d..1de273530e 100644 --- a/samples/MicroserviceDemo/k8s/sqlserver-deployment.yaml +++ b/samples/MicroserviceDemo/k8s/sqlserver-deployment.yaml @@ -24,7 +24,6 @@ spec: ports: - containerPort: 1433 resources: {} - imagePullPolicy: IfNotPresent volumeMounts: - mountPath: /var/opt/mssql name: dbdata diff --git a/samples/MicroserviceDemo/k8s/sqlserver-service.yaml b/samples/MicroserviceDemo/k8s/sqlserver-service.yaml index 57e77a1b42..da4a98c85a 100644 --- a/samples/MicroserviceDemo/k8s/sqlserver-service.yaml +++ b/samples/MicroserviceDemo/k8s/sqlserver-service.yaml @@ -6,8 +6,7 @@ metadata: name: sqlserver spec: ports: - - name: "1433" - port: 1433 + - port: 1433 targetPort: 1433 selector: io.kompose.service: sqlserver diff --git a/samples/MicroserviceDemo/microservices/BloggingService.Host/BloggingServiceHostModule.cs b/samples/MicroserviceDemo/microservices/BloggingService.Host/BloggingServiceHostModule.cs index da076cf6e0..ac7af17fd4 100644 --- a/samples/MicroserviceDemo/microservices/BloggingService.Host/BloggingServiceHostModule.cs +++ b/samples/MicroserviceDemo/microservices/BloggingService.Host/BloggingServiceHostModule.cs @@ -85,7 +85,6 @@ namespace BloggingService.Host Configure(options => { options.FileUploadLocalFolder = Path.Combine(hostingEnvironment.WebRootPath, "files"); - options.FileUploadUrlRoot = "/files/"; }); context.Services.AddDistributedRedisCache(options => diff --git a/samples/MicroserviceDemo/microservices/BloggingService.Host/wwwroot/files/100d4c0445cbf55f687339ec8e656abb.jpg b/samples/MicroserviceDemo/microservices/BloggingService.Host/wwwroot/files/100d4c0445cbf55f687339ec8e656abb.jpg new file mode 100644 index 0000000000..5ff55ea887 Binary files /dev/null and b/samples/MicroserviceDemo/microservices/BloggingService.Host/wwwroot/files/100d4c0445cbf55f687339ec8e656abb.jpg differ diff --git a/samples/MicroserviceDemo/microservices/BloggingService.Host/wwwroot/files/90974f3f1edb4ab0a10cf9759fd5f9ae.png b/samples/MicroserviceDemo/microservices/BloggingService.Host/wwwroot/files/90974f3f1edb4ab0a10cf9759fd5f9ae.png new file mode 100644 index 0000000000..17aea07098 Binary files /dev/null and b/samples/MicroserviceDemo/microservices/BloggingService.Host/wwwroot/files/90974f3f1edb4ab0a10cf9759fd5f9ae.png differ diff --git a/samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/files/ec882345a403be325bc539ebda211e79.jpg b/samples/MicroserviceDemo/microservices/BloggingService.Host/wwwroot/files/ec882345a403be325bc539ebda211e79.jpg similarity index 100% rename from samples/MicroserviceDemo/applications/PublicWebSite.Host/wwwroot/files/ec882345a403be325bc539ebda211e79.jpg rename to samples/MicroserviceDemo/microservices/BloggingService.Host/wwwroot/files/ec882345a403be325bc539ebda211e79.jpg diff --git a/samples/MicroserviceDemo/microservices/ProductService.Host/Migrations/20190307065413_Added_ImageName_To_Product.Designer.cs b/samples/MicroserviceDemo/microservices/ProductService.Host/Migrations/20190307065413_Added_ImageName_To_Product.Designer.cs new file mode 100644 index 0000000000..7106d08d50 --- /dev/null +++ b/samples/MicroserviceDemo/microservices/ProductService.Host/Migrations/20190307065413_Added_ImageName_To_Product.Designer.cs @@ -0,0 +1,74 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using ProductService.Host.EntityFrameworkCore; + +namespace ProductService.Host.Migrations +{ + [DbContext(typeof(ProductServiceMigrationDbContext))] + [Migration("20190307065413_Added_ImageName_To_Product")] + partial class Added_ImageName_To_Product + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.0-rtm-35687") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("ProductManagement.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Code") + .IsRequired() + .HasMaxLength(32); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("ImageName") + .HasMaxLength(128); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256); + + b.Property("Price"); + + b.Property("StockCount"); + + b.HasKey("Id"); + + b.HasIndex("Code"); + + b.HasIndex("Name"); + + b.ToTable("PmProducts"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/samples/MicroserviceDemo/microservices/ProductService.Host/Migrations/20190307065413_Added_ImageName_To_Product.cs b/samples/MicroserviceDemo/microservices/ProductService.Host/Migrations/20190307065413_Added_ImageName_To_Product.cs new file mode 100644 index 0000000000..082bf9bf52 --- /dev/null +++ b/samples/MicroserviceDemo/microservices/ProductService.Host/Migrations/20190307065413_Added_ImageName_To_Product.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ProductService.Host.Migrations +{ + public partial class Added_ImageName_To_Product : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ImageName", + table: "PmProducts", + maxLength: 128, + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ImageName", + table: "PmProducts"); + } + } +} diff --git a/samples/MicroserviceDemo/microservices/ProductService.Host/Migrations/ProductServiceMigrationDbContextModelSnapshot.cs b/samples/MicroserviceDemo/microservices/ProductService.Host/Migrations/ProductServiceMigrationDbContextModelSnapshot.cs index 5bc9669fb0..dcbec3288c 100644 --- a/samples/MicroserviceDemo/microservices/ProductService.Host/Migrations/ProductServiceMigrationDbContextModelSnapshot.cs +++ b/samples/MicroserviceDemo/microservices/ProductService.Host/Migrations/ProductServiceMigrationDbContextModelSnapshot.cs @@ -41,6 +41,9 @@ namespace ProductService.Host.Migrations b.Property("ExtraProperties") .HasColumnName("ExtraProperties"); + b.Property("ImageName") + .HasMaxLength(128); + b.Property("LastModificationTime") .HasColumnName("LastModificationTime"); diff --git a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/CreateProductDto.cs b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/CreateProductDto.cs index 693dd0d501..42091eb6ec 100644 --- a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/CreateProductDto.cs +++ b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/CreateProductDto.cs @@ -1,11 +1,20 @@ -namespace ProductManagement +using System.ComponentModel.DataAnnotations; + +namespace ProductManagement { public class CreateProductDto { + [Required] + [StringLength(ProductConsts.MaxCodeLength)] public string Code { get; set; } + [Required] + [StringLength(ProductConsts.MaxNameLength)] public string Name { get; set; } + [StringLength(ProductConsts.MaxImageNameLength)] + public string ImageName { get; set; } + public float Price { get; set; } public int StockCount { get; set; } diff --git a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/ProductDto.cs b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/ProductDto.cs index 86a4536785..959473001c 100644 --- a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/ProductDto.cs +++ b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/ProductDto.cs @@ -9,6 +9,8 @@ namespace ProductManagement public string Name { get; set; } + public string ImageName { get; set; } + public float Price { get; set; } public int StockCount { get; set; } diff --git a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/ProductManagementApplicationContractsModule.cs b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/ProductManagementApplicationContractsModule.cs index 53fe58cbe0..fedac990be 100644 --- a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/ProductManagementApplicationContractsModule.cs +++ b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/ProductManagementApplicationContractsModule.cs @@ -16,11 +16,6 @@ namespace ProductManagement { public override void ConfigureServices(ServiceConfigurationContext context) { - Configure(options => - { - options.DefinitionProviders.Add(); - }); - Configure(options => { options.FileSets.AddEmbedded(); @@ -32,11 +27,6 @@ namespace ProductManagement .Get() .AddVirtualJson("/ProductManagement/Localization/ApplicationContracts"); }); - - Configure(options => - { - options.DefinitionProviders.Add(); - }); } } } diff --git a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/UpdateProductDto.cs b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/UpdateProductDto.cs index aa96bf9c51..744d58dbbe 100644 --- a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/UpdateProductDto.cs +++ b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/UpdateProductDto.cs @@ -1,9 +1,16 @@ -namespace ProductManagement +using System.ComponentModel.DataAnnotations; + +namespace ProductManagement { public class UpdateProductDto { + [Required] + [StringLength(ProductConsts.MaxNameLength)] public string Name { get; set; } + [StringLength(ProductConsts.MaxImageNameLength)] + public string ImageName { get; set; } + public float Price { get; set; } public int StockCount { get; set; } diff --git a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application/ProductManagement/ProductAppService.cs b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application/ProductManagement/ProductAppService.cs index c993f27f02..6cdf82f22a 100644 --- a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application/ProductManagement/ProductAppService.cs +++ b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application/ProductManagement/ProductAppService.cs @@ -28,7 +28,7 @@ namespace ProductManagement await NormalizeMaxResultCountAsync(input); var products = await _productRepository - .OrderBy(input.Sorting) + .OrderBy(input.Sorting ?? "Name") .Skip(input.SkipCount) .Take(input.MaxResultCount) .ToListAsync(); @@ -59,7 +59,13 @@ namespace ProductManagement [Authorize(ProductManagementPermissions.Products.Create)] public async Task CreateAsync(CreateProductDto input) { - var product = await _productManager.CreateAsync(input.Code, input.Name, input.Price, input.StockCount); + var product = await _productManager.CreateAsync( + input.Code, + input.Name, + input.Price, + input.StockCount, + input.ImageName + ); return ObjectMapper.Map(product); } @@ -72,6 +78,7 @@ namespace ProductManagement product.SetName(input.Name); product.SetPrice(input.Price); product.SetStockCount(input.StockCount); + product.SetImageName(input.ImageName); return ObjectMapper.Map(product); } diff --git a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain.Shared/ProductManagement/ProductConsts.cs b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain.Shared/ProductManagement/ProductConsts.cs index c6cc028936..26ca7ccea5 100644 --- a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain.Shared/ProductManagement/ProductConsts.cs +++ b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain.Shared/ProductManagement/ProductConsts.cs @@ -5,5 +5,7 @@ public const int MaxCodeLength = 32; public const int MaxNameLength = 256; + + public const int MaxImageNameLength = 128; } } diff --git a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement/Product.cs b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement/Product.cs index bca0a7b09d..6686637185 100644 --- a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement/Product.cs +++ b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement/Product.cs @@ -22,6 +22,8 @@ namespace ProductManagement public int StockCount { get; private set; } + public string ImageName { get; private set; } + private Product() { //Default constructor is needed for ORMs. @@ -32,7 +34,8 @@ namespace ProductManagement [NotNull] string code, [NotNull] string name, float price = 0.0f, - int stockCount = 0) + int stockCount = 0, + string imageName = null) { Check.NotNullOrWhiteSpace(code, nameof(code)); @@ -45,6 +48,7 @@ namespace ProductManagement Code = code; SetName(Check.NotNullOrWhiteSpace(name, nameof(name))); SetPrice(price); + SetImageName(imageName); SetStockCountInternal(stockCount, triggerEvent: false); } @@ -61,6 +65,22 @@ namespace ProductManagement return this; } + public Product SetImageName([CanBeNull] string imageName) + { + if (imageName == null) + { + return this; + } + + if (imageName.Length >= ProductConsts.MaxImageNameLength) + { + throw new ArgumentException($"Product image name can not be longer than {ProductConsts.MaxImageNameLength}"); + } + + ImageName = imageName; + return this; + } + public Product SetPrice(float price) { if (price < 0.0f) diff --git a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement/ProductManager.cs b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement/ProductManager.cs index 4c39feb7bf..9b924ca1d6 100644 --- a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement/ProductManager.cs +++ b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement/ProductManager.cs @@ -20,7 +20,8 @@ namespace ProductManagement [NotNull] string code, [NotNull] string name, float price = 0.0f, - int stockCount = 0) + int stockCount = 0, + string imageName = null) { var existingProduct = await _productRepository.FirstOrDefaultAsync(p => p.Code == code); if (existingProduct != null) @@ -34,7 +35,8 @@ namespace ProductManagement code, name, price, - stockCount + stockCount, + imageName ) ); } diff --git a/samples/MicroserviceDemo/modules/product/src/ProductManagement.EntityFrameworkCore/ProductManagement/EntityFrameworkCore/ProductManagementDbContextModelCreatingExtensions.cs b/samples/MicroserviceDemo/modules/product/src/ProductManagement.EntityFrameworkCore/ProductManagement/EntityFrameworkCore/ProductManagementDbContextModelCreatingExtensions.cs index 8a12ad11a8..94d74e836f 100644 --- a/samples/MicroserviceDemo/modules/product/src/ProductManagement.EntityFrameworkCore/ProductManagement/EntityFrameworkCore/ProductManagementDbContextModelCreatingExtensions.cs +++ b/samples/MicroserviceDemo/modules/product/src/ProductManagement.EntityFrameworkCore/ProductManagement/EntityFrameworkCore/ProductManagementDbContextModelCreatingExtensions.cs @@ -27,6 +27,7 @@ namespace ProductManagement.EntityFrameworkCore b.Property(x => x.Code).IsRequired().HasMaxLength(ProductConsts.MaxCodeLength); b.Property(x => x.Name).IsRequired().HasMaxLength(ProductConsts.MaxNameLength); + b.Property(x => x.ImageName).HasMaxLength(ProductConsts.MaxImageNameLength); b.HasIndex(q => q.Code); b.HasIndex(q => q.Name); diff --git a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/Localization/Resources/ProductManagement/en.json b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/Localization/Resources/ProductManagement/en.json index d9e9ea167a..39d48b30e2 100644 --- a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/Localization/Resources/ProductManagement/en.json +++ b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/Localization/Resources/ProductManagement/en.json @@ -10,6 +10,7 @@ "Code": "Code", "Name": "Name", "Price": "Price", + "ImageName": "Image Name", "ProductDeletionWarningMessage": "Are you sure you want to delete this product?" } } \ No newline at end of file diff --git a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/Pages/ProductManagement/Products/Create.cshtml.cs b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/Pages/ProductManagement/Products/Create.cshtml.cs index 89188ad2d2..e660ff8f8b 100644 --- a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/Pages/ProductManagement/Products/Create.cshtml.cs +++ b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/Pages/ProductManagement/Products/Create.cshtml.cs @@ -10,7 +10,7 @@ namespace ProductManagement.Pages.ProductManagement.Products private readonly IProductAppService _productAppService; [BindProperty] - public ProductCreateModalView Product { get; set; } = new ProductCreateModalView(); + public ProductCreateViewModel Product { get; set; } = new ProductCreateViewModel(); public CreateModel(IProductAppService productAppService) { @@ -19,14 +19,14 @@ namespace ProductManagement.Pages.ProductManagement.Products public async Task OnPostAsync() { - var createProductDto = ObjectMapper.Map(Product); + var createProductDto = ObjectMapper.Map(Product); await _productAppService.CreateAsync(createProductDto); return NoContent(); } - public class ProductCreateModalView + public class ProductCreateViewModel { [Required] [StringLength(ProductConsts.MaxCodeLength)] @@ -36,6 +36,9 @@ namespace ProductManagement.Pages.ProductManagement.Products [StringLength(ProductConsts.MaxNameLength)] public string Name { get; set; } + [StringLength(ProductConsts.MaxImageNameLength)] + public string ImageName { get; set; } + public float Price { get; set; } public int StockCount { get; set; } diff --git a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/Pages/ProductManagement/Products/Edit.cshtml.cs b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/Pages/ProductManagement/Products/Edit.cshtml.cs index 4776f135bd..d0b2450e97 100644 --- a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/Pages/ProductManagement/Products/Edit.cshtml.cs +++ b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/Pages/ProductManagement/Products/Edit.cshtml.cs @@ -1,10 +1,7 @@ using System; -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; namespace ProductManagement.Pages.ProductManagement.Products @@ -14,7 +11,7 @@ namespace ProductManagement.Pages.ProductManagement.Products private readonly IProductAppService _productAppService; [BindProperty] - public ProductEditModalView Product { get; set; } = new ProductEditModalView(); + public ProductEditViewModel Product { get; set; } = new ProductEditViewModel(); public EditModel(IProductAppService productAppService) { @@ -25,7 +22,7 @@ namespace ProductManagement.Pages.ProductManagement.Products { var productDto = await _productAppService.GetAsync(productId); - Product = ObjectMapper.Map(productDto); + Product = ObjectMapper.Map(productDto); return Page(); } @@ -40,7 +37,7 @@ namespace ProductManagement.Pages.ProductManagement.Products }); } - public class ProductEditModalView + public class ProductEditViewModel { [HiddenInput] [Required] @@ -50,6 +47,9 @@ namespace ProductManagement.Pages.ProductManagement.Products [StringLength(ProductConsts.MaxNameLength)] public string Name { get; set; } + [StringLength(ProductConsts.MaxImageNameLength)] + public string ImageName { get; set; } + public float Price { get; set; } public int StockCount { get; set; } diff --git a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/ProductManagementWebAutoMapperProfile.cs b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/ProductManagementWebAutoMapperProfile.cs index 43b2be406a..577aa0f352 100644 --- a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/ProductManagementWebAutoMapperProfile.cs +++ b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/ProductManagementWebAutoMapperProfile.cs @@ -7,8 +7,8 @@ namespace ProductManagement { public ProductManagementWebAutoMapperProfile() { - CreateMap(); - CreateMap(); + CreateMap(); + CreateMap(); } } } \ No newline at end of file diff --git a/samples/RabbitMqEventBus/App1/Program.cs b/samples/RabbitMqEventBus/App1/Program.cs index aab454523a..26decc1248 100644 --- a/samples/RabbitMqEventBus/App1/Program.cs +++ b/samples/RabbitMqEventBus/App1/Program.cs @@ -14,8 +14,6 @@ namespace App1 { application.Initialize(); - var x = application.ServiceProvider.GetRequiredService(); - var messagingService = application .ServiceProvider .GetRequiredService(); diff --git a/templates/module/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName/MyProjectName/MyProjectNameApplicationContractsModule.cs b/templates/module/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName/MyProjectName/MyProjectNameApplicationContractsModule.cs index 6d4c99a263..84be3eb86f 100644 --- a/templates/module/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName/MyProjectName/MyProjectNameApplicationContractsModule.cs +++ b/templates/module/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName/MyProjectName/MyProjectNameApplicationContractsModule.cs @@ -16,11 +16,6 @@ namespace MyCompanyName.MyProjectName { public override void ConfigureServices(ServiceConfigurationContext context) { - Configure(options => - { - options.DefinitionProviders.Add(); - }); - Configure(options => { options.FileSets.AddEmbedded(); diff --git a/templates/module/src/MyCompanyName.MyProjectName.Application/MyCompanyName/MyProjectName/MyProjectNameApplicationModule.cs b/templates/module/src/MyCompanyName.MyProjectName.Application/MyCompanyName/MyProjectName/MyProjectNameApplicationModule.cs index f62329d534..891ff26af3 100644 --- a/templates/module/src/MyCompanyName.MyProjectName.Application/MyCompanyName/MyProjectName/MyProjectNameApplicationModule.cs +++ b/templates/module/src/MyCompanyName.MyProjectName.Application/MyCompanyName/MyProjectName/MyProjectNameApplicationModule.cs @@ -18,11 +18,6 @@ namespace MyCompanyName.MyProjectName { options.AddProfile(validate: true); }); - - Configure(options => - { - options.DefinitionProviders.Add(); - }); } } } diff --git a/templates/mvc/MyCompanyName.MyProjectName.sln b/templates/mvc/MyCompanyName.MyProjectName.sln index ed0f5881c3..0c616dbc3b 100644 --- a/templates/mvc/MyCompanyName.MyProjectName.sln +++ b/templates/mvc/MyCompanyName.MyProjectName.sln @@ -19,7 +19,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.Web.Tests", "test\MyCompanyName.MyProjectName.Web.Tests\MyCompanyName.MyProjectName.Web.Tests.csproj", "{5F1B28C6-8D0C-4155-92D0-252F7EA5F674}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyCompanyName.MyProjectName.MongoDB", "src\MyCompanyName.MyProjectName.MongoDB\MyCompanyName.MyProjectName.MongoDB.csproj", "{E3444355-D47E-431E-BDD0-DD3A7113B2AE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.MongoDB", "src\MyCompanyName.MyProjectName.MongoDB\MyCompanyName.MyProjectName.MongoDB.csproj", "{E3444355-D47E-431E-BDD0-DD3A7113B2AE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations", "src\MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations\MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations.csproj", "{0372FA84-C517-4EB3-9A9F-B9ACAC0CA5E0}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -55,6 +57,10 @@ Global {E3444355-D47E-431E-BDD0-DD3A7113B2AE}.Debug|Any CPU.Build.0 = Debug|Any CPU {E3444355-D47E-431E-BDD0-DD3A7113B2AE}.Release|Any CPU.ActiveCfg = Release|Any CPU {E3444355-D47E-431E-BDD0-DD3A7113B2AE}.Release|Any CPU.Build.0 = Release|Any CPU + {0372FA84-C517-4EB3-9A9F-B9ACAC0CA5E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0372FA84-C517-4EB3-9A9F-B9ACAC0CA5E0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0372FA84-C517-4EB3-9A9F-B9ACAC0CA5E0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0372FA84-C517-4EB3-9A9F-B9ACAC0CA5E0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -67,6 +73,7 @@ Global {50B2631D-129C-47B3-A587-029CCD6099BC} = {04DBDB01-70F4-4E06-B468-8F87850B22BE} {5F1B28C6-8D0C-4155-92D0-252F7EA5F674} = {04DBDB01-70F4-4E06-B468-8F87850B22BE} {E3444355-D47E-431E-BDD0-DD3A7113B2AE} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0} + {0372FA84-C517-4EB3-9A9F-B9ACAC0CA5E0} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {28315BFD-90E7-4E14-A2EA-F3D23AF4126F} diff --git a/templates/mvc/common.props b/templates/mvc/common.props new file mode 100644 index 0000000000..7585f303ba --- /dev/null +++ b/templates/mvc/common.props @@ -0,0 +1,7 @@ + + + latest + 1.0.0 + $(NoWarn);CS1591 + + \ No newline at end of file diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Application/MyCompanyName.MyProjectName.Application.csproj b/templates/mvc/src/MyCompanyName.MyProjectName.Application/MyCompanyName.MyProjectName.Application.csproj index 70b63090cc..f774c93818 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.Application/MyCompanyName.MyProjectName.Application.csproj +++ b/templates/mvc/src/MyCompanyName.MyProjectName.Application/MyCompanyName.MyProjectName.Application.csproj @@ -1,5 +1,7 @@  + + netcoreapp2.2 MyCompanyName.MyProjectName diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Application/MyProjectNameApplicationModule.cs b/templates/mvc/src/MyCompanyName.MyProjectName.Application/MyProjectNameApplicationModule.cs index 83a95f6946..c23175b956 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.Application/MyProjectNameApplicationModule.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.Application/MyProjectNameApplicationModule.cs @@ -16,11 +16,6 @@ namespace MyCompanyName.MyProjectName { public override void ConfigureServices(ServiceConfigurationContext context) { - Configure(options => - { - options.DefinitionProviders.Add(); - }); - Configure(options => { options.AddProfile(); diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyCompanyName.MyProjectName.Domain.csproj b/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyCompanyName.MyProjectName.Domain.csproj index 258847de37..32d5d55698 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyCompanyName.MyProjectName.Domain.csproj +++ b/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyCompanyName.MyProjectName.Domain.csproj @@ -1,5 +1,7 @@  + + netcoreapp2.2 MyCompanyName.MyProjectName diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameConsts.cs b/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameConsts.cs new file mode 100644 index 0000000000..86a322bb13 --- /dev/null +++ b/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameConsts.cs @@ -0,0 +1,9 @@ +namespace MyCompanyName.MyProjectName +{ + public static class MyProjectNameConsts + { + public const string DefaultDbTablePrefix = "App"; + + public const string DefaultDbSchema = null; + } +} diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDomainModule.cs b/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDomainModule.cs index f32225025f..565e4f8d5f 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDomainModule.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDomainModule.cs @@ -36,11 +36,6 @@ namespace MyCompanyName.MyProjectName .AddBaseTypes(typeof(AbpValidationResource)) .AddVirtualJson("/Localization/MyProjectName"); }); - - Configure(options => - { - options.DefinitionProviders.Add(); - }); } } } diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/MyProjectNameMigrationsDbContext.cs b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/MyProjectNameMigrationsDbContext.cs new file mode 100644 index 0000000000..4d8e7bd10f --- /dev/null +++ b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/MyProjectNameMigrationsDbContext.cs @@ -0,0 +1,32 @@ +using Microsoft.EntityFrameworkCore; +using Volo.Abp.AuditLogging.EntityFrameworkCore; +using Volo.Abp.BackgroundJobs.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Identity.EntityFrameworkCore; +using Volo.Abp.PermissionManagement.EntityFrameworkCore; +using Volo.Abp.SettingManagement.EntityFrameworkCore; + +namespace MyCompanyName.MyProjectName.EntityFrameworkCore +{ + public class MyProjectNameMigrationsDbContext : AbpDbContext + { + public MyProjectNameMigrationsDbContext(DbContextOptions options) + : base(options) + { + + } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + builder.ConfigurePermissionManagement(); + builder.ConfigureSettingManagement(); + builder.ConfigureBackgroundJobs(); + builder.ConfigureAuditLogging(); + builder.ConfigureIdentity(); + + builder.ConfigureMyProjectName(); + } + } +} \ No newline at end of file diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/MyProjectNameMigrationsDbContextFactory.cs b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/MyProjectNameMigrationsDbContextFactory.cs new file mode 100644 index 0000000000..d760790fc8 --- /dev/null +++ b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/MyProjectNameMigrationsDbContextFactory.cs @@ -0,0 +1,29 @@ +using System.IO; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.Configuration; + +namespace MyCompanyName.MyProjectName.EntityFrameworkCore +{ + public class MyProjectNameMigrationsDbContextFactory : IDesignTimeDbContextFactory + { + public MyProjectNameMigrationsDbContext CreateDbContext(string[] args) + { + var configuration = BuildConfiguration(); + + var builder = new DbContextOptionsBuilder() + .UseSqlServer(configuration.GetConnectionString("Default")); + + return new MyProjectNameMigrationsDbContext(builder.Options); + } + + private static IConfigurationRoot BuildConfiguration() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false); + + return builder.Build(); + } + } +} diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20190211104804_Initial.Designer.cs b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/20190320072839_Initial.Designer.cs similarity index 99% rename from templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20190211104804_Initial.Designer.cs rename to templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/20190320072839_Initial.Designer.cs index 2002970aad..b25ddbee04 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20190211104804_Initial.Designer.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/20190320072839_Initial.Designer.cs @@ -9,15 +9,15 @@ using MyCompanyName.MyProjectName.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.Migrations { - [DbContext(typeof(MyProjectNameDbContext))] - [Migration("20190211104804_Initial")] + [DbContext(typeof(MyProjectNameMigrationsDbContext))] + [Migration("20190320072839_Initial")] partial class Initial { protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.2.1-servicing-10028") + .HasAnnotation("ProductVersion", "2.2.0-rtm-35687") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20190211104804_Initial.cs b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/20190320072839_Initial.cs similarity index 100% rename from templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20190211104804_Initial.cs rename to templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/20190320072839_Initial.cs diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/MyProjectNameDbContextModelSnapshot.cs b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/MyProjectNameMigrationsDbContextModelSnapshot.cs similarity index 99% rename from templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/MyProjectNameDbContextModelSnapshot.cs rename to templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/MyProjectNameMigrationsDbContextModelSnapshot.cs index 1d2a225f38..43def4fbad 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/MyProjectNameDbContextModelSnapshot.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/MyProjectNameMigrationsDbContextModelSnapshot.cs @@ -8,14 +8,14 @@ using MyCompanyName.MyProjectName.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.Migrations { - [DbContext(typeof(MyProjectNameDbContext))] - partial class MyProjectNameDbContextModelSnapshot : ModelSnapshot + [DbContext(typeof(MyProjectNameMigrationsDbContext))] + partial class MyProjectNameMigrationsDbContextModelSnapshot : ModelSnapshot { protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.2.1-servicing-10028") + .HasAnnotation("ProductVersion", "2.2.0-rtm-35687") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations.csproj b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations.csproj new file mode 100644 index 0000000000..b38b78dbd2 --- /dev/null +++ b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations.csproj @@ -0,0 +1,14 @@ + + + + + + netcoreapp2.2 + MyCompanyName.MyProjectName + + + + + + + diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/EntityFrameworkCore/MyProjectNameDbContext.cs b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/EntityFrameworkCore/MyProjectNameDbContext.cs index f26ede167b..fb4008aff4 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/EntityFrameworkCore/MyProjectNameDbContext.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/EntityFrameworkCore/MyProjectNameDbContext.cs @@ -1,11 +1,6 @@ using Microsoft.EntityFrameworkCore; -using Volo.Abp.AuditLogging.EntityFrameworkCore; -using Volo.Abp.BackgroundJobs.EntityFrameworkCore; using Volo.Abp.Data; using Volo.Abp.EntityFrameworkCore; -using Volo.Abp.Identity.EntityFrameworkCore; -using Volo.Abp.PermissionManagement.EntityFrameworkCore; -using Volo.Abp.SettingManagement.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.EntityFrameworkCore { @@ -18,15 +13,11 @@ namespace MyCompanyName.MyProjectName.EntityFrameworkCore } - protected override void OnModelCreating(ModelBuilder modelBuilder) + protected override void OnModelCreating(ModelBuilder builder) { - base.OnModelCreating(modelBuilder); + base.OnModelCreating(builder); - modelBuilder.ConfigureIdentity(); - modelBuilder.ConfigurePermissionManagement(); - modelBuilder.ConfigureSettingManagement(); - modelBuilder.ConfigureBackgroundJobs(); - modelBuilder.ConfigureAuditLogging(); + builder.ConfigureMyProjectName(); } } } diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/EntityFrameworkCore/MyProjectNameDbContextModelCreatingExtensions.cs b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/EntityFrameworkCore/MyProjectNameDbContextModelCreatingExtensions.cs new file mode 100644 index 0000000000..719d8f2c1e --- /dev/null +++ b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/EntityFrameworkCore/MyProjectNameDbContextModelCreatingExtensions.cs @@ -0,0 +1,34 @@ +using Microsoft.EntityFrameworkCore; +using Volo.Abp; + +namespace MyCompanyName.MyProjectName.EntityFrameworkCore +{ + public static class MyProjectNameDbContextModelCreatingExtensions + { + public static void ConfigureMyProjectName(this ModelBuilder builder) + { + Check.NotNull(builder, nameof(builder)); + + var tablePrefix = MyProjectNameConsts.DefaultDbTablePrefix; + var schema = MyProjectNameConsts.DefaultDbSchema; + + /* Configure all entities here. Example: + + builder.Entity(b => + { + //Configure table & schema name + //b.ToTable(tablePrefix + "Questions", schema); + + //Properties + //b.Property(q => q.Title).IsRequired().HasMaxLength(QuestionConsts.MaxTitleLength); + + //Configure relations + //b.HasMany(question => question.Tags).WithOne().HasForeignKey(qt => qt.QuestionId); + + //Configure indexes + //b.HasIndex(q => q.CreationTime); + }); + */ + } + } +} \ No newline at end of file diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj index debec3acbe..30b78df34e 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj +++ b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj @@ -1,13 +1,11 @@  + + netcoreapp2.2 MyCompanyName.MyProjectName - - - - diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.MongoDB/MyCompanyName.MyProjectName.MongoDB.csproj b/templates/mvc/src/MyCompanyName.MyProjectName.MongoDB/MyCompanyName.MyProjectName.MongoDB.csproj index b313a4c24d..ece0fd1b4d 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.MongoDB/MyCompanyName.MyProjectName.MongoDB.csproj +++ b/templates/mvc/src/MyCompanyName.MyProjectName.MongoDB/MyCompanyName.MyProjectName.MongoDB.csproj @@ -1,5 +1,7 @@  + + netcoreapp2.2 MyCompanyName.MyProjectName diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj b/templates/mvc/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj index 846b262f32..f3e67ec343 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj +++ b/templates/mvc/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj @@ -1,5 +1,7 @@  + + netcoreapp2.2 MyCompanyName.MyProjectName @@ -9,7 +11,6 @@ true true false - @@ -28,7 +29,7 @@ - + diff --git a/templates/mvc/test/MyCompanyName.MyProjectName.Application.Tests/MyCompanyName.MyProjectName.Application.Tests.csproj b/templates/mvc/test/MyCompanyName.MyProjectName.Application.Tests/MyCompanyName.MyProjectName.Application.Tests.csproj index 752b62442f..e9b614f5e8 100644 --- a/templates/mvc/test/MyCompanyName.MyProjectName.Application.Tests/MyCompanyName.MyProjectName.Application.Tests.csproj +++ b/templates/mvc/test/MyCompanyName.MyProjectName.Application.Tests/MyCompanyName.MyProjectName.Application.Tests.csproj @@ -1,5 +1,7 @@  + + netcoreapp2.2 MyCompanyName.MyProjectName diff --git a/templates/mvc/test/MyCompanyName.MyProjectName.Web.Tests/MyCompanyName.MyProjectName.Web.Tests.csproj b/templates/mvc/test/MyCompanyName.MyProjectName.Web.Tests/MyCompanyName.MyProjectName.Web.Tests.csproj index 2a5c9f7a13..177f2da1ec 100644 --- a/templates/mvc/test/MyCompanyName.MyProjectName.Web.Tests/MyCompanyName.MyProjectName.Web.Tests.csproj +++ b/templates/mvc/test/MyCompanyName.MyProjectName.Web.Tests/MyCompanyName.MyProjectName.Web.Tests.csproj @@ -1,5 +1,7 @@  + + netcoreapp2.2 Exe diff --git a/templates/service/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName/MyProjectName/MyProjectNameApplicationContractsModule.cs b/templates/service/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName/MyProjectName/MyProjectNameApplicationContractsModule.cs index 6d4c99a263..84be3eb86f 100644 --- a/templates/service/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName/MyProjectName/MyProjectNameApplicationContractsModule.cs +++ b/templates/service/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName/MyProjectName/MyProjectNameApplicationContractsModule.cs @@ -16,11 +16,6 @@ namespace MyCompanyName.MyProjectName { public override void ConfigureServices(ServiceConfigurationContext context) { - Configure(options => - { - options.DefinitionProviders.Add(); - }); - Configure(options => { options.FileSets.AddEmbedded(); diff --git a/templates/service/src/MyCompanyName.MyProjectName.Application/MyCompanyName/MyProjectName/MyProjectNameApplicationModule.cs b/templates/service/src/MyCompanyName.MyProjectName.Application/MyCompanyName/MyProjectName/MyProjectNameApplicationModule.cs index f62329d534..891ff26af3 100644 --- a/templates/service/src/MyCompanyName.MyProjectName.Application/MyCompanyName/MyProjectName/MyProjectNameApplicationModule.cs +++ b/templates/service/src/MyCompanyName.MyProjectName.Application/MyCompanyName/MyProjectName/MyProjectNameApplicationModule.cs @@ -18,11 +18,6 @@ namespace MyCompanyName.MyProjectName { options.AddProfile(validate: true); }); - - Configure(options => - { - options.DefinitionProviders.Add(); - }); } } }