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/Pages/Templates.cshtml b/abp_io/src/Volo.AbpWebSite.Web/Pages/Templates.cshtml index d7bc5aa603..4643510c34 100644 --- a/abp_io/src/Volo.AbpWebSite.Web/Pages/Templates.cshtml +++ b/abp_io/src/Volo.AbpWebSite.Web/Pages/Templates.cshtml @@ -14,7 +14,7 @@ @using (Html.BeginForm(FormMethod.Post)) {
-
+

Create New Project @@ -64,7 +64,7 @@

-
+

The Startup Project

See the documentation to understand how to run the downloaded project.

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/docs/en/Getting-Started-AspNetCore-MVC-Template.md b/docs/en/Getting-Started-AspNetCore-MVC-Template.md index de9a5b09cb..cb24eb543f 100644 --- a/docs/en/Getting-Started-AspNetCore-MVC-Template.md +++ b/docs/en/Getting-Started-AspNetCore-MVC-Template.md @@ -19,9 +19,9 @@ The downloaded project requires; ### The Solution Structure -Extract the zip file downloaded and open in **Visual Studio 2017 (15.7.0+)**: +Extract the zip file downloaded and open in **Visual Studio 2017 (15.9.0+)**: -![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution.png) +![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-v2.png) The solution has a layered structure (based on Domain Driven Design) where; @@ -30,6 +30,8 @@ The solution has a layered structure (based on Domain Driven Design) where; * ``.Web`` is the presentation layer. * ``.EntityFrameworkCore`` is the EF Core integration package. +EF Core database migrations are separated to a project named `.EntityFrameworkCore.DbMigrations`. + The solution also contains unit & integration test projects properly configured to work with **EF Core** & **SQLite in-memory** database. ### Creating the Database @@ -50,9 +52,9 @@ Right click to the `.Web` project and select **Set as StartUp Project**: ![set-as-startup-project](images/set-as-startup-project.png) -Open the **Package Manager Console**, select `.EntityFrameworkCore` project as the **Default Project** and run the `Update-Database` command: +Open the **Package Manager Console**, select `.EntityFrameworkCore.DbMigrations` project as the **Default Project** and run the `Update-Database` command: -![pcm-update-database](images/pcm-update-database.png) +![pcm-update-database](images/pcm-update-database-v2.png) This will create a new database based on the configured connection string. diff --git a/docs/en/Tutorials/AspNetCore-Mvc/Part-I.md b/docs/en/Tutorials/AspNetCore-Mvc/Part-I.md index 6db6766c0b..337f021587 100644 --- a/docs/en/Tutorials/AspNetCore-Mvc/Part-I.md +++ b/docs/en/Tutorials/AspNetCore-Mvc/Part-I.md @@ -20,7 +20,7 @@ Go to the [startup template page](https://abp.io/Templates) and download a new p This is the how the layered solution structure looks after it's created from the startup template: -![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution.png) +![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-v2.png) ### Create the Book Entity @@ -91,9 +91,9 @@ EF Core requires you to relate entities with your DbContext. The easiest way to #### Add New Migration & Update the Database -The Startup template uses [EF Core Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/) to create and maintain the database schema. Open the **Package Manager Console (PMC)** (under the *Tools/Nuget Package Manager* menu), select the `Acme.BookStore.EntityFrameworkCore` as the **default project** and execute the following command: +The Startup template uses [EF Core Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/) to create and maintain the database schema. Open the **Package Manager Console (PMC)** (under the *Tools/Nuget Package Manager* menu), select the `Acme.BookStore.EntityFrameworkCore.DbMigrations` as the **default project** and execute the following command: -![bookstore-pmc-add-book-migration](images/bookstore-pmc-add-book-migration.png) +![bookstore-pmc-add-book-migration](images/bookstore-pmc-add-book-migration-v2.png) This will create a new migration class inside the `Migrations` folder. Then execute the `Update-Database` command to update the database schema: diff --git a/docs/en/Tutorials/AspNetCore-Mvc/Part-III.md b/docs/en/Tutorials/AspNetCore-Mvc/Part-III.md index a45d406ce6..b70521dad0 100644 --- a/docs/en/Tutorials/AspNetCore-Mvc/Part-III.md +++ b/docs/en/Tutorials/AspNetCore-Mvc/Part-III.md @@ -14,8 +14,6 @@ You can download the **source code** of the application [from here](https://gith There are two test projects in the solution: -![bookstore-test-projects](images/bookstore-test-projects.png) - * `Acme.BookStore.Application.Tests` is for unit & integration tests. You can write tests for application service methods. It uses **EF Core SQLite in-memory** database. * `Acme.BookStore.Web.Tests` is for full stack integration tests including the web layer. So, you can write tests for UI pages too. diff --git a/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-pmc-add-book-migration-v2.png b/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-pmc-add-book-migration-v2.png new file mode 100644 index 0000000000..edf2826361 Binary files /dev/null and b/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-pmc-add-book-migration-v2.png differ diff --git a/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v2.png b/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v2.png new file mode 100644 index 0000000000..48b5881e96 Binary files /dev/null and b/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v2.png differ diff --git a/docs/en/images/bookstore-visual-studio-solution-v2.png b/docs/en/images/bookstore-visual-studio-solution-v2.png new file mode 100644 index 0000000000..48b5881e96 Binary files /dev/null and b/docs/en/images/bookstore-visual-studio-solution-v2.png differ diff --git a/docs/en/images/pcm-update-database-v2.png b/docs/en/images/pcm-update-database-v2.png new file mode 100644 index 0000000000..d5bb9c2975 Binary files /dev/null and b/docs/en/images/pcm-update-database-v2.png differ diff --git a/docs/zh-Hans/Getting-Started-AspNetCore-MVC-Template.md b/docs/zh-Hans/Getting-Started-AspNetCore-MVC-Template.md index 6f274b5177..e8ce4fa71a 100644 --- a/docs/zh-Hans/Getting-Started-AspNetCore-MVC-Template.md +++ b/docs/zh-Hans/Getting-Started-AspNetCore-MVC-Template.md @@ -12,14 +12,14 @@ 下载的项目需要: -* [Visual Studio 2017 (v15.7.0+)](https://visualstudio.microsoft.com/tr/downloads/) +* [Visual Studio 2017 (v15.9.0+)](https://visualstudio.microsoft.com/tr/downloads/) * [.NET Core 2.1.1+](https://www.microsoft.com/net/download/dotnet-core/) ### 解决方案结构 下载后解压文件并在 **Visual Studio 2017(15.7.0 +)** 中打开: -![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution.png) +![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-v2.png) 该解决方案具有分层结构(基于域驱动设计), 其中: @@ -28,6 +28,8 @@ * ``.Web`` 为是表示层. * ``.EntityFrameworkCore`` 是EF Core集成. +EF Core 数据库迁移被分离到名为`.EntityFrameworkCore.DbMigrations`项目中. + 解决方案还包含配置好的的单元&集成测试项目, 以便与 **EF Core** 和 **SQLite内存中** 数据库配合使用. ### 创建数据库 @@ -48,9 +50,9 @@ ![set-as-startup-project](images/set-as-startup-project.png) -打开**包管理器控制台(Package Manager Console)**, 选择`.EntityFrameworkCore`项目作为**默认项目**并运行`Update-Database`命令: +打开**包管理器控制台(Package Manager Console)**, 选择`.EntityFrameworkCore.DbMigrations`项目作为**默认项目**并运行`Update-Database`命令: -![pcm-update-database](images/pcm-update-database.png) +![pcm-update-database](images/pcm-update-database-v2.png) 这将基于配置的连接字符串创建新数据库. diff --git a/docs/zh-Hans/Samples/Microservice-Demo.md b/docs/zh-Hans/Samples/Microservice-Demo.md index e7dd2cfd8a..3a9305f771 100644 --- a/docs/zh-Hans/Samples/Microservice-Demo.md +++ b/docs/zh-Hans/Samples/Microservice-Demo.md @@ -35,85 +35,97 @@ ABP框架的主要目标之一就是提供[便捷的基础设施来创建微服 此示例仍处于开发阶段,尚未完成. -## Running the Solution +## 运行解决方案 -You can either run from the **source code** or from the pre-configured **docker-compose** file. +您可以从 **源代码** 或者预先配置好的 **docker-compose** 文件运行. -### Using the Docker Containers +### 使用Docker容器 -#### Pre Requirements +#### 预先要求 -Running as docker containers is easier since all dependencies are pre-configured. You only need to install the [latest docker](https://docs.docker.com/compose/install/). +由于所有依赖项都已预先配置, 因此作为Docker容器运行更容易. 你只需要安装[最新的docker](https://docs.docker.com/compose/install/). -#### Running Containers +#### 运行容器 -- Clone or download the [ABP repository](https://github.com/abpframework/abp). +- 克隆或下载 [ABP仓库](https://github.com/abpframework/abp). -- Open a command line in the `samples/MicroserviceDemo` folder of the repository. +- 在存储库的`samples/MicroserviceDemo`文件夹中打开命令行. -- Restore SQL Server databases: +- 从Docker Hub中拉取image: + + ``` + docker-compose -f docker-compose.yml -f docker-compose.migrations.yml pull + ``` + +- 如果要在本地构建映像, 可以跳过上述步骤, 使用build命令: + + ``` + docker-compose -f docker-compose.yml -f docker-compose.migrations.yml build + ``` + + 根据你的电脑配置, 构建image可能需要**很长时间**. + +- 还原 SQL Server 数据库: ``` docker-compose -f docker-compose.yml -f docker-compose.migrations.yml run restore-database ``` -- Start the containers: +- 启动容器: ``` docker-compose up -d ``` - At the first run, it will take a **long time** because it will build all docker images. - -- Add this line to the end of your `hosts` file: +- 将此行添加到`hosts`文件的末尾: ``` 127.0.0.1 auth-server ``` - hosts file is located inside the `C:\Windows\System32\Drivers\etc\hosts` folder on Windows and `/etc/hosts` for Linux/MacOS. + hosts文件位于Windows上的`C:\Windows\System32\Drivers\etc\hosts`文件夹, Linux/MacOS的`/etc/hosts`中. -#### Run the Applications +#### 运行应用程序 -There are a few applications running in the containers you may want to explore: +你可能想要了解容器中运行一些应用程序: -* Backend Admin Application (BackendAdminApp.Host): `http://localhost:51512` - *(Used to manage users & products in the system)* -* Public Web Site (PublicWebsite.Host): `http://localhost:51513` - *(Used to list products and run/manage the blog module)* -* Authentication Server (AuthServer.Host): `http://auth-server:51511/` - *(Used as a single sign on and authentication server built with IdentityServer4)* +* 后端管理应用程序 (BackendAdminApp.Host): `http://localhost:51512` + *(用于管理系统中的用户和产品)* +* 公共网站 (PublicWebsite.Host): `http://localhost:51513` + *(用于列出产品并运行/管理博客模块)* +* 认证服务器 (AuthServer.Host): `http://auth-server:51511/` + *(用作使用IdentityServer4构建的单点登录和身份验证服务器)* * Kibana UI: `http://localhost:51510` - *(Use to show/trace logs written by all services/applications/gateways)* + *(用于显示/跟踪所有服务/应用程序/网关写入的日志)* -### Running From the Source Code +### 从源代码运行 -#### Pre Requirements +#### 预先要求 -To be able to run the solution from source code, following tools should be installed and running on your computer: +为了能够从源代码运行解决方案, 应在你的计算机上安装并运行以下工具: -* [SQL Server](https://www.microsoft.com/en-us/sql-server/sql-server-downloads) 2015+ (can be [express edition](https://www.microsoft.com/en-us/sql-server/sql-server-editions-express)) +* [SQL Server](https://www.microsoft.com/en-us/sql-server/sql-server-downloads) 2015+ (可以是 [express edition](https://www.microsoft.com/en-us/sql-server/sql-server-editions-express)) * [Redis](https://redis.io/download) 5.0+ * [RabbitMQ](https://www.rabbitmq.com/install-windows.html) 3.7.11+ * [MongoDB](https://www.mongodb.com/download-center) 4.0+ * [ElasticSearch](https://www.elastic.co/downloads/elasticsearch) 6.6+ -* [Kibana](https://www.elastic.co/downloads/kibana) 6.6+ (optional, recommended to show logs) +* [Kibana](https://www.elastic.co/downloads/kibana) 6.6+ (可选,建议显示日志) -#### Open & Build the Visual Studio Solution +#### 打开并构建Visual Studio解决方案 -* Open the `samples\MicroserviceDemo\MicroserviceDemo.sln` in Visual Studio 2017 (15.9.0+). -* Run `dotnet restore` from the command line inside the `samples\MicroserviceDemo` folder. -* Build the solution in Visual Studio. +* 在Visual Studio 2017 (15.9.0+)中打开`samples\MicroserviceDemo\MicroserviceDemo.sln`. +* 在`samples\MicroserviceDemo`文件夹中的命令行运行`dotnet restore`命令. +* 在Visual Studio中构建解决方案. -#### Restore Databases +#### 还原数据库 -Open `MsDemo_Identity.zip` and `MsDemo_ProductManagement.zip` inside the `samples\MicroserviceDemo\databases` folder and restore to the SQL Server. +在`samples\MicroserviceDemo\databases`文件夹中打开`MsDemo_Identity.zip`和`MsDemo_ProductManagement.zip`并恢复到SQL Server. -> Notice that: These databases have EF Core migrations in the solution, however they don't have seed data, especially required for IdentityServer4 configuration. So, restoring the databases is much more easier. +> 请注意:这些数据库在解决方案中具有EF Core迁移,但它们没有种子数据,尤其是IdentityServer4所需的配置. 因此,恢复数据库要容易得多. -#### Run Projects +#### 运行项目 -Run the projects with the following order (right click to each project, set as startup project an press Ctrl+F5 to run without debug): +按以下顺序运行项目(右键单击每个项目设置为启动项目,按Ctrl+F5运行,无需调试): * AuthServer.Host * IdentityService.Host @@ -125,61 +137,61 @@ Run the projects with the following order (right click to each project, set as s * BackendAdminApp.Host * PublicWebSite.Host -## A Brief Overview of the Solution +## 解决方案简介 -The Visual Studio solution consists of multiple projects each have different roles in the system: +Visual Studio解决方案由多个项目组成,每个项目在系统中具有不同的角色: ![microservice-sample-solution](../images/microservice-sample-solution.png) -### Applications +### 应用程序(Applications) -These are the actual applications those have user interfaces to interact to the users and use the system. +这些是具有用户界面以与用户交互并使用系统的实际应用程序. -- **AuthServer.Host**: Host the IdentityServer4 to provide an authentication service to other services and applications. It is a single-sign server and contains the login page. -- **BackendAdminApp.Host**: This is a backend admin application that host UI for Identity and Product management modules. -- **PubicWebSite.Host**: As public web site that contains a simple product list page and blog module UI. -- **ConsoleClientDemo**: A simple console application to demonstrate the usage of services from a C# application. +- **AuthServer.Host**: 托管IdentityServer4以向其他服务和应用程序提供身份验证服务. 它是一个单点登录服务器,包含登录页面. +- **BackendAdminApp.Host**: 这是一个后端管理应用程序,用于托管身份和产品管理模块的UI. +- **PubicWebSite.Host**: 作为包含简单产品列表页面和博客模块UI的公共网站. +- **ConsoleClientDemo**: 一个简单的控制台应用程序,用于演示C#应用程序中使用服务. -### Gateways / BFFs (Backend for Frontend) +### 网关/BFF(前端后端) -Gateways are used to provide a single entry point to the applications. It can also used for rate limiting, load balancing... etc. Used the [Ocelot](https://github.com/ThreeMammals/Ocelot) library. +网关用于为应用程序提供单一入口点.它还可以用于速率限制,负载平衡等. 使用[Ocelot](https://github.com/ThreeMammals/Ocelot)类库. -* **BackendAdminAppGateway.Host**: Used by the BackendAdminApp.Host application as backend. -* **PublicWebSiteGateway.Host**: Used by the PublicWebSite.Host application as backend. -* **InternalGateway.Host**: Used for inter-service communication (the communication between microservices). +* **BackendAdminAppGateway.Host**: 由BackendAdminApp.Host应用程序用作后端. +* **PublicWebSiteGateway.Host**: 由PublicWebSite.Host应用程序用作后端. +* **InternalGateway.Host**: 用于服务间通信(微服务之间的通信). -### Microservices +### 微服务 -Microservices have no UI, but exposes some REST APIs. +微服务没有UI,但暴露了一些REST API. -- **IdentityService.Host**: Host the ABP Identity module which is used to manage users & roles. It has no additional service, but only hosts the Identity module's API. -- **BloggingService.Host**: Host the ABP Blogging module which is used to manage blog & posts (a typical blog application). It has no additional service, but only hosts the Blogging module's API. -- **ProductService.Host**: Hosts the Product module (that is inside the solution) which is used to manage products. It also contains the EF Core migrations to create/update the Product Management database schema. +- **IdentityService.Host**: 托管用于管理用户和角色的ABP Identity模块. 它没有其他服务,仅托管Identity模块的API. +- **BloggingService.Host**: 托管ABP博客模块,该模块用于管理博客和帖子(典型的博客应用程序). 它没有其他服务,仅托管Blogging模块的API. +- **ProductService.Host**: 托管用于管理产品的产品模块(位于解决方案内). 它还包含用于创建/更新产品管理数据库架构的EF Core迁移. -### Modules +### 模块 -* **Product**: A layered module that is developed with the [module development best practices](../Best-Practices/Index.md). It can be embedded into a monolithic application or can be hosted as a microservice by separately deploying API and UI (as done in this demo solution). +* **产品**: 使用[模块开发最佳实践](../Best-Practices/Index.md)开发的分层模块. 它可以嵌入到单个应用程序中,也可以通过单独部署API和UI作为微服务托管(如本演示解决方案中所述). -### Databases +### 数据库 -This solution is using multiple databases: +此解决方案使用多个数据库: -* **MsDemo_Identity**: An SQL database. Used **SQL Server** by default, but can be any DBMS supported by the EF Core. Shared by AuthServer and IdentityService. Also audit logs, permissions and settings are stored in this database (while they could easily have their own databases, shared the same database to keep it simple). -* **MsDemo_ProductManagement**: An SQL database. Again, used **SQL Server** by default, but can be any DBMS supported by the EF Core. Used by the ProductService as a dedicated database. -* **MsDemo_Blogging**: A **MongoDB** database. Used by the BloggingService. -* **Elasticsearch**: Used to write logs over Serilog. +* **MsDemo_Identity**: 一个SQL数据库. 默认使用** SQL Server **,但可以是EF Core支持的任何DBMS. 由AuthServer和IdentityService共享. 审计日志,权限和设置也存储在此数据库中(虽然它们可以轻松拥有自己的数据库,共享相同的数据库以保持简单). +* **MsDemo_ProductManagement**: 一个SQL数据库. 同样默认使用 **SQL Server**,但可以是EF Core支持的任何DBMS. 由ProductService用作专用数据库. +* **MsDemo_Blogging**: **MongoDB**数据库. 由BloggingService使用. +* **Elasticsearch**: 用于在Serilog上写日志. -## Applications +## 应用 -### Authentication Server (AuthServer.Host) +### 认证服务器 (AuthServer.Host) -This project is used by all other services and applications for authentication & single sign on. Mainly, uses **IdentityServer4** to provide these services. It uses some of the [pre-build ABP modules](../Modules/Index) like *Identity*, *Audit Logging* and *Permission Management*. +所有其他服务和应用程序都使用此项目进行身份验证和单点登录. 主要使用**IdentityServer4**来提供这些服务. 它使用了一些[预构建ABP模块](../Modules/Index) 如 *Identity*, *Audit Logging* 和 *Permission Management*. -#### Database & EF Core Configuration +#### 数据库和EF Core配置 -This application uses a SQL database (named it as **MsDemo_Identity**) and maintains its schema via **Entity Framework Core migrations.** +此应用程序使用SQL数据库(将其命名为**MsDemo_Identity**)并通过**Entity Framework Core迁移**维护其架构. -It has a DbContext named **AuthServerDbContext** and defined as shown below: +它有一个名为**AuthServerDbContext**的DbContext,定义如下: ````csharp public class AuthServerDbContext : AbpDbContext @@ -203,35 +215,35 @@ public class AuthServerDbContext : AbpDbContext } ```` -In the **OnModelCreating**, you see **ConfigureX()** method calls. A module with a database schema generally declares such an extension method to configure EF Core mappings for its own entities. This is a flexible approach where you can arrange your databases and modules inside them; You can use a different database for each module, or combine some of them in a shared database. In the AuthServer project, we decided to combine multiple module schemas in a single EF Core DbContext, in a single physical database. These modules are Identity, IdentityServer, AuditLogging, PermissionManagement and SettingManagement modules. +在**OnModelCreating**方法中, 你会看到 **ConfigureX()** 方法调用. 具有数据库模式的模块通常声明这样的扩展方法,以便为其自己的实体配置EF Core映射. 这是一种灵活的方法, 可以在其中安排数据库和模块; 可以为每个模块使用不同的数据库,或者将它们中的一些组合在一个共享数据库中. 在AuthServer项目中,我们决定在单个物理数据库中将单个EF Core DbContext中的多个模块模式组合在一起. 这些模块是Identity,IdentityServer,AuditLogging,PermissionManagement和SettingManagement模块. -Notice that this DbContext is only for database migrations. All modules have their own `DbContext` classes those are used in the runtime by the modules. +请注意,此DbContext仅用于数据库迁移. 所有模块都有自己的`DbContext`类,模块在运行时使用这些类. -#### User Interface +#### 用户界面 -AuthServer has a simple home page that shows the current user info if the current user has logged in: +AuthServer有一个简单的主页,如果当前用户已登录,则显示当前用户信息: ![microservice-sample-authserver-home](../images/microservice-sample-authserver-home.png) -It also provides Login & Register pages: +它还提供登录和注册页面: ![microservice-sample-authserver-login](../images/microservice-sample-authserver-login.png) -These pages are not included in the project itself. Instead, AuthServer project uses the prebuilt ABP [account module](https://github.com/abpframework/abp/tree/master/modules/account) with IdentityServer extension. That means it can also act as an OpenId Connect server with necessary UI and logic. +这些页面不包含在项目本身中. 相反,AuthServer项目使用带有IdentityServer扩展的预构建ABP[帐户模块](https://github.com/abpframework/abp/tree/master/modules/account). 这意味着它还可以充当具有必要UI和逻辑的OpenId Connect服务器. -#### Dependencies +#### 依赖 -* **RabbitMQ** for messaging to other services. -* **Redis** for distributed/shared caching. -* **Elasticsearch** for storing logs. +* **RabbitMQ** 用于向其他服务发送消息. +* **Redis** 用于分布式/共享缓存. +* **Elasticsearch** 用于存储日志. -### Backend Admin Application (BackendAdminApp.Host) +### 后端管理应用程序 (BackendAdminApp.Host) -This is a web application that is used to manage users, roles, permissions and products in the system. +这是一个Web应用程序,用于管理系统中的用户,角色,权限和产品. -#### Authentication +#### 认证 -BackendAdminApp redirects to the AuthServer for authentication. Once the user enters a correct username & password, the page is redirected to the backend application again. Authentication configuration is setup in the `BackendAdminAppHostModule` class: +BackendAdminApp重定向到AuthServer进行身份验证. 用户输入正确的用户名和密码后,页面将再次重定向到后端应用程序. 身份验证配置在`BackendAdminAppHostModule`类中设置: ````charp context.Services.AddAuthentication(options => @@ -263,12 +275,13 @@ context.Services.AddAuthentication(options => }); ```` -* It adds "Cookies" authentication as the primary authentication type. -* "oidc" authentication is configured to use the AuthServer application as the authentication server. -* It requires the additional identity scopes *role*, *email* and *phone*. +* 它将"Cookies"身份验证添加为主要身份验证类型. +* "oidc"身份验证配置为使用AuthServer应用程序作为身份验证服务器. +* 它需要额外的身份范围(scopes) *role*, *email* and *phone*. * It requires the API resource scopes *BackendAdminAppGateway*, *IdentityService* and *ProductService* because it will use these services as APIs. +* 它需要API资源范围 *BackendAdminAppGateway*, *IdentityService* 和 *ProductService*,因为它将这些服务用作API. -IdentityServer client settings are stored inside the `appsettings.json` file: +IdentityServer客户端设置存储在`appsettings.json`文件中: ````json "AuthServer": { @@ -278,25 +291,27 @@ IdentityServer client settings are stored inside the `appsettings.json` file: } ```` -#### User Interface +#### 用户界面 -The BackendAdminApp.Host project itself has not a single UI element/page. It is only used to serve UI pages of the Identity and Product Management modules. `BackendAdminAppHostModule` adds dependencies to `AbpIdentityWebModule` (*[Volo.Abp.Identity.Web](https://www.nuget.org/packages/Volo.Abp.Identity.Web)* package) and `ProductManagementWebModule` (*ProductManagement.Web* project) for that purpose. +BackendAdminApp.Host项目本身没有单个UI元素/页面. 它仅用于提供身份和产品管理模块的UI页面. -A screenshot from the user management page: +`BackendAdminAppHostModule`将依赖关系添加到`AbpIdentityWebModule`(*[Volo.Abp.Identity.Web](https://www.nuget.org/packages/Volo.Abp.Identity.Web)* 包)和`ProductManagementWebModule`(*ProductManagement.Web*项目)为此目的. + +用户管理页面的屏幕截图: ![microservice-sample-backend-ui](../images/microservice-sample-backend-ui.png) -A screenshot from the permission management modal for a role: +来自权限的权限管理模式的屏幕截图: ![microservice-sample-backend-ui-permissions](../images/microservice-sample-backend-ui-permissions.png) -#### Using Microservices +#### 使用微服务 -Backend admin application uses the Identity and Product microservices for all operations, over the Backend Admin Gateway (BackendAdminAppGateway.Host). +后端管理应用程序通过后端管理网关对所有操作使用Identity和Product微服务(BackendAdminAppGateway.Host). -##### Remote End Point +##### 远程端点 -`appsettings.json` file contains the `RemoteServices` section to declare the remote service endpoint(s). Each microservice will normally have different endpoints. However, this solution uses the API Gateway pattern to provide a single endpoint for the applications: +`appsettings.json`文件包含`RemoteServices`部分,用于声明远程服务端点. 每个微服务通常都有不同的端点. 但是,此解决方案使用API网关模式为应用程序提供单个端点: ````json "RemoteServices": { @@ -306,32 +321,32 @@ Backend admin application uses the Identity and Product microservices for all op } ```` -`http://localhost:65115/` is the URL of the *BackendAdminAppGateway.Host* project. It knows where are Identity and Product services are located. +`http://localhost:65115/` 是 *BackendAdminAppGateway.Host* 项目的URL. 它知道身份和产品服务的位置. ##### HTTP Clients -ABP application modules generally provides C# client libraries to consume services (APIs) easily (they generally uses the [Dynamic C# API Clients](../AspNetCore/Dynamic-CSharp-API-Clients.md) feature of the ABP framework). That means if you need to consume Identity service API, you can reference to its client package and easily use the APIs by provided interfaces. +ABP应用程序模块通常提供C#客户端库以轻松地使用服务(API)(它们通常使用ABP框架的[Dynamic C# API客户端](../AspNetCore/Dynamic-CSharp-API-Clients.md)). 这意味着如果你需要使用Identity Service API, 你可以引用其客户端软件包,并通过提供的接口轻松使用API. -For that purpose, `BackendAdminAppHostModule` class declares dependencies for `AbpIdentityHttpApiClientModule` and `ProductManagementHttpApiClientModule`. +为此`BackendAdminAppHostModule`类声明了`AbpIdentityHttpApiClientModule`和`ProductManagementHttpApiClientModule`的依赖关系. -Once you refer these client packages, you can directly inject an application service interface (e.g. `IIdentityUserAppService`) and use its methods like a local method call. It actually invokes remote service calls over HTTP to the related service endpoint. +一旦引用这些客户端软件包,就可以直接注入应用程序服务接口(例如`IIdentityUserAppService`)并使用其方法,如本地方法调用. 它实际上通过HTTP调用到相关服务端点的远程服务调用. -##### Passing the Access Token +##### 传递访问令牌(Access Token) -Since microservices requires authentication & authorization, each remote service call should contain an Authentication header. This header is obtained from the `access_token` inside the current `HttpContext` for the current user. This is automatically done when you use the `Volo.Abp.Http.Client.IdentityModel` package. `BackendAdminAppHostModule` declares dependencies to this package and to the related `AbpHttpClientIdentityModelModule` class. It is integrated to the HTTP Clients explained above. +由于微服务需要身份验证和授权,因此每个远程服务调用都应包含Authentication头. 该头是从当前用户的当前`HttpContext`中的`access_token`获得的. 当你使用`Volo.Abp.Http.Client.IdentityModel`包时,会自动执行此操作. `BackendAdminAppHostModule`声明对此包和相关的`AbpHttpClientIdentityModelModule`类的依赖. 它集成到上面解释的HTTP客户端. -#### Dependencies +#### 依赖 -- **Redis** for distributed/shared caching. -- **Elasticsearch** for storing logs. +- **Redis** 用于分布式/共享缓存. +- **Elasticsearch** 用于存储日志. -### Public Web Site (PublicWebSite.Host) +### 公共网站 (PublicWebSite.Host) -This is a public web site project that has a web blog and product list page. +这是一个公共网站项目,具有Web博客和产品列表页面. -#### Authentication +#### 认证 -PublicWebSite can show blog posts and product list without login. If you login, you can also manage blogs. It redirects to the AuthServer for authentication. Once the user enters a correct username & password, the page is redirected to the public web site application again. Authentication configuration is setup in the `PublicWebSiteHostModule` class: +公共网站可以在不登录的情况下显示博客文章和产品列表. 如果你登录,你还可以管理博客. 它重定向到AuthServer进行身份验证. 用户输入正确的用户名和密码后,页面将再次重定向到公共网站应用程序. 身份验证配置在`PublicWebSiteHostModule`类中设置: ```charp context.Services.AddAuthentication(options => @@ -363,12 +378,12 @@ context.Services.AddAuthentication(options => }); ``` -- It adds "Cookies" authentication as the primary authentication type. -- "oidc" authentication is configured to use the AuthServer application as the authentication server. -- It requires the additional identity scopes *role*, *email* and *phone*. -- It requires the API resource scopes *PublicWebSiteGateway*, *BloggingService* and *ProductService* because it will use these services as APIs. +- 它将"Cookies"身份验证添加为主要身份验证类型. +- "oidc"身份验证配置为使用AuthServer应用程序作为身份验证服务器. +- 它需要额外的身份范围 *role*, *email* and *phone*. +- 它需要API资源范围 *PublicWebSiteGateway*,*BloggingService*和*ProductService*,因为它将这些服务用作API. -IdentityServer client settings are stored inside the `appsettings.json` file: +IdentityServer客户端设置存储在`appsettings.json`文件中: ```json "AuthServer": { @@ -378,21 +393,22 @@ IdentityServer client settings are stored inside the `appsettings.json` file: } ``` -#### User Interface +#### 用户界面 -The PublicWebSite.Host project has a page to list products (`Pages/Products.cshtml`). It also uses the UI from the blogging module. `PublicWebSiteHostModule` adds dependencies to `BloggingWebModule` (*[Volo.Blogging.Web](https://www.nuget.org/packages/Volo.Blogging.Web)* package) for that purpose. +PublicWebSite.Host项目有一个列出产品的页面 (`Pages/Products.cshtml`). 它还使用博客模块中的UI. 为此`PublicWebSiteHostModule`加入了`BloggingWebModule`(*[Volo.Blogging.Web](https://www.nuget.org/packages/Volo.Blogging.Web)* 包)的依赖项. -A screenshot from the Products page: +产品页面的屏幕截图: ![microservice-sample-public-product-list](../images/microservice-sample-public-product-list.png) -#### Using Microservices +#### 使用微服务 + +公共网站应用程序使用Blogging和Product微服务通过公共网站网关进行所有操作(PublicWebSiteGateway.Host). -Publc web site application uses the Blogging and Product microservices for all operations, over the Public Web Site Gateway (PublicWebSiteGateway.Host). +##### 远程端点 -##### Remote End Point -`appsettings.json` file contains the `RemoteServices` section to declare the remote service endpoint(s). Each microservice will normally have different endpoints. However, this solution uses the API Gateway pattern to provide a single endpoint for the applications: +`appsettings.json`文件包含`RemoteServices`部分,用于声明远程服务端点. 每个微服务通常都有不同的端点. 但是,此解决方案使用API网关模式为应用程序提供单个端点: ```json "RemoteServices": { @@ -402,28 +418,28 @@ Publc web site application uses the Blogging and Product microservices for all o } ``` -`http://localhost:64897/` is the URL of the *PublicWebSiteGateway.Host* project. It knows where are Blogging and Product services are located. +`http://localhost:64897/` 是*PublicWebSiteGateway.Host*项目的URL. 它知道Blogging和产品服务的位置. ##### HTTP Clients -`PublicWebSiteHostModule` class declares dependencies for `BloggingHttpApiClientModule` and `ProductManagementHttpApiClientModule` to be able to use remote HTTP APIs for these services. +`PublicWebSiteHostModule`类声明`BloggingHttpApiClientModule`和`ProductManagementHttpApiClientModule`的依赖关系,以便能够为这些服务使用远程HTTP API. -##### Passing the Access Token +##### 传递访问令牌(Access Token) -Just like explained in the Backend Admin Application section, Public Web Site project also uses the `AbpHttpClientIdentityModelModule` to pass `access_token` to the calling services for authentication. +正如后端管理应用程序部分中所述, Public Web Site项目还使用`AbpHttpClientIdentityModelModule`将`access_token`传递给调用服务进行身份验证. #### Dependencies -- **Redis** for distributed/shared caching. -- **Elasticsearch** for storing logs. +- **Redis** 用于分布式/共享缓存. +- **Elasticsearch** 用于存储日志. -### Console Client Demo +### 控制台客户端演示 -Finally, the solution includes a very simple console application, named ConsoleClientDemo, that uses Identity and Product services by authenticating through the AuthServer. It uses the Internal Gateway (InternalGateway.Host) to perform HTTP API calls. +最后,该解决方案包括一个非常简单的控制台应用程序,名为ConsoleClientDemo,它通过AuthServer进行身份验证来使用Identity和Product服务. 它使用内部网关(InternalGateway.Host)来执行HTTP API调用. -#### Remote Service Configuration +#### 远程服务配置 -`RemoteService` configuration in the `appsettings.json` file is simple: +`appsettings.json`文件中的`RemoteService`配置很简单: ````json "RemoteServices": { @@ -433,11 +449,11 @@ Finally, the solution includes a very simple console application, named ConsoleC } ```` -`http://localhost:65129/` is the URL of the Internal Gateway. All API calls to the services are performed over this URL. +`http://localhost:65129/` 是内部网关的URL. 对服务的所有API调用都是通过此URL执行的. -#### Authentication (IdentityServer Client) Configuration +#### 身份验证(IdentityServer客户端)配置 -`appsettings.json` also has a configuration for the IdentityServer authentication: +`appsettings.json`还有一个IdentityServer身份验证配置: ````json "IdentityClients": { @@ -451,7 +467,7 @@ Finally, the solution includes a very simple console application, named ConsoleC } ```` -This sample uses the `client_credentials` grant type which requires a `ClientId` and `ClientSecret` for the authentication process. There are also [other grant types](http://docs.identityserver.io/en/latest/topics/grant_types.html). For example, you can use the following configuration to swith to the `password` (Resource Owner Password) grant type: +此示例使用`client_credentials` 授予类型,该类型需要`ClientId`和`ClientSecret`进行身份验证过程. 还有[其他授予类型](http://docs.identityserver.io/en/latest/topics/grant_types.html). 例如, 你可以使用以下配置切换到`password`(Resource Owner Password)授予类型: ````json "IdentityClients": { @@ -467,33 +483,33 @@ This sample uses the `client_credentials` grant type which requires a `ClientId` } ```` -Resource Owner Password requires a `UserName` & `UserPassword` in addition to client credentials. This grant type is useful to call remote services on behalf of a user. +除客户端凭据外,Resource Owner Password还需要`UserName` 和 `UserPassword`. 此授权类型对于代表用户调用远程服务很有用. -`Scope` declares the APIs (and the gateway) to grant access. This application uses the Internal Gateway. +`Scope` 声明API(和网关)以授予访问权限. 此应用程序使用内部网关. -#### HTTP Client Dependencies +#### HTTP Client依赖 -`ConsoleClientDemoModule` has dependencies to `AbpIdentityHttpApiClientModule` and `ProductManagementHttpApiClientModule` in order to use Identity and Product APIs. It also has `AbpHttpClientIdentityModelModule` dependency to authenticate via IdentityServer. +`ConsoleClientDemoModule`与`AbpIdentityHttpApiClientModule`和`ProductManagementHttpApiClientModule`有依赖关系,以便使用Identity和Product API. 它还具有“AbpHttpClientIdentityModelModule”依赖性,可通过IdentityServer进行身份验证. -#### Using the Services +#### 使用服务 -Using the services is straightforward. See the `ClientDemoService` class which simply injects `IIdentityUserAppService` and `IProductAppService` and uses them. This class also shows a manual HTTP call using an `HttpClient` object. See source code of the `ClientDemoService` for details. +使用这些服务非常简单. 请参阅`ClientDemoService`类,它只是注入`IIdentityUserAppService`和`IProductAppService`并使用它们. 该类还显示了使用`HttpClient`对象的手动HTTP调用. 有关详细信息,请参阅`ClientDemoService`的源代码. -## API Gateways / BFFs (Backend for Frontend) +## API网关/ BFF(前端后端) -Gateways are used to provide a **single entry point** to the applications. In this way, an application only deal with a single service address (API endpoint) instead of a different addresses for each services. Gateways are also used for rate limiting, security, authentication, load balancing and many more requirements. +网关用于为应用程序提供 **单一入口点**. 通过这种方式,应用程序仅处理单个服务地址(API端点),而不是每个服务的不同地址. 网关还用于速率限制,安全性,身份验证,负载平衡和更多要求. -"**Backend for Frontend**" (BFF) is a common architectural pattern which offers to build a **dedicated and specialized** gateway for each different application / client type. This solution uses this pattern and has multiple gateways. +"**后端前端**"(BFF)是一种常见的架构模式,可为每种不同的应用程序/客户端类型构建**专注而专业的**网关. 此解决方案使用此模式并具有多个网关. -This solution uses the [Ocelot](https://github.com/ThreeMammals/Ocelot) library to build API Gateways. It's a widely accepted API Gateway library for ASP.NET Core. +此解决方案使用[Ocelot](https://github.com/ThreeMammals/Ocelot)库来构建API网关. 它是ASP.NET核心广泛接受的API网关库. -### Backend Admin Application Gateway (BackendAdminAppGateway.Host) +### 后端管理应用程序网关 (BackendAdminAppGateway.Host) -This is backend (server side API) for the "Backend Admin Application" (don't confuse about the naming; Backend Admin Application is a frontend web application actually, but used by system admins rather than regular users). +这是"后端管理应用程序"的后端(服务器端API)(不要混淆命名;后端管理应用程序实际上是前端Web应用程序,但系统管理员而不是普通用户使用). -#### Authentication +#### 认证 -This gateway uses IdentityServer `Bearer` authentication and configured like that: +此网关使用IdentityServer `Bearer`身份验证并配置如下: ````csharp context.Services.AddAuthentication("Bearer") @@ -513,9 +529,9 @@ context.Services.AddAuthentication("Bearer") }); ```` -`AddIdentityServerAuthentication` extension method comes from the [IdentityServer4.AccessTokenValidation](https://www.nuget.org/packages/IdentityServer4.AccessTokenValidation) package, part of the IdentityServer4 project (see [its documentation](http://docs.identityserver.io/en/latest/topics/apis.html)). +`AddIdentityServerAuthentication` 扩展方法来自 [IdentityServer4.AccessTokenValidation](https://www.nuget.org/packages/IdentityServer4.AccessTokenValidation) 包, IdentityServer4项目的一部分 (参见 [identityserver文档](http://docs.identityserver.io/en/latest/topics/apis.html)). -`ApiName` is the API which is being protected, `BackendAdminAppGateway` in this case. So, this solution defines gateways as API resources. Rest of the configuration is related to claims mapping (which is planned to be automated in next ABP versions). The configuration related to authentication in the `appsettings.json` is simple: +`ApiName`是受保护的API,在这种情况下是`BackendAdminAppGateway`. 因此,此解决方案将网关定义为API资源. 其余配置与声明映射(计划在下一个ABP版本中自动化)相关. `appsettings.json`中的身份验证相关的配置很简单: ````json "AuthServer": { @@ -524,9 +540,9 @@ context.Services.AddAuthentication("Bearer") } ```` -#### Ocelot Configuration +#### Ocelot配置 -Ocelot needs to know the real URLs of the microservices to be able to redirect HTTP requests. The configuration for this gateway is like below: +Ocelot需要知道微服务的真实URL才能重定向HTTP请求. 此网关的配置如下所示: ````json "ReRoutes": [ @@ -560,26 +576,28 @@ Ocelot needs to know the real URLs of the microservices to be able to redirect H } ```` -`ReRoutes` is an array of URL mappings. `BaseUrl` in the `GlobalConfiguration` section is the URL of this gateway (Ocelot needs to know its own URL). See [its own documentation](https://ocelot.readthedocs.io/en/latest/features/configuration.html) to better understand the configuration. +`ReRoutes`是一个URL映射数组. `GlobalConfiguration`部分中的`BaseUrl`是该网关的URL(Ocelot需要知道自己的URL). 参见 [ocelot文档](https://ocelot.readthedocs.io/en/latest/features/configuration.html) 更好地了解配置. Ocelot is a finalizer ASP.NET Core middleware and should be written as the last item in the pipeline: +Ocelot是一个终结ASP.NET核心中间件,应该写成管道中的最后一项: + ````csharp app.UseOcelot().Wait(); ```` -It handles and redirects requests based on the configuration above. +它根据上面的配置处理和重定向请求. -#### ABP Configuration Endpoints +#### ABP 配置端点 -ABP provides some built-in APIs to get some configuration and information from the server. Examples: +ABP提供了一些内置API,以从服务器获取一些配置和信息. 例子: -* `/api/abp/application-configuration` returns localization texts, permission and setting values (try http://localhost:65115/api/abp/application-configuration for this gateway). -* `/Abp/ServiceProxyScript` returns dynamic javascript proxies to call services from a javascript client (try http://localhost:65115/Abp/ServiceProxyScript for this gateway). +* `/api/abp/application-configuration` 返回本地化文本,权限和设置值 (http://localhost:65115/api/abp/application-configuration). +* `/Abp/ServiceProxyScript` 返回动态javascript代理以从javascript客户端调用服务 (http://localhost:65115/Abp/ServiceProxyScript ). -These endpoints should be served by the gateway service, not by microservices. A microservice can only know permissions related to that microservice. But, once properly configured, gateway can aggregate permission values for multiple services as a single list which is more suitable for clients. +这些端点应由网关服务提供,而不是由微服务提供. 微服务只能知道与该微服务相关的权限. 但是,一旦正确配置,网关就可以将多个服务的权限值聚合为一个更适合客户端的列表. -For this purpose, the ASP.NET Core pipeline was configured to handle some specific routes via MVC, instead of Ocelot. To make this possible, MapWhen extension method is used like that: +为此, ASP.NET Core管道配置为通过MVC而不是Ocelot处理某些特定路由. 为了实现这一点,MapWhen扩展方法使用如下: ````csharp app.MapWhen(ctx => ctx.Request.Path.ToString().StartsWith("/api/abp/") || @@ -592,33 +610,33 @@ app.MapWhen(ctx => ctx.Request.Path.ToString().StartsWith("/api/abp/") || app.UseOcelot().Wait(); ```` -This configuration uses standard MVC middleware when request path starts with `/api/abp/` or `/Abp/`. +当请求路径以 `/api/abp/` 或 `/Abp/`.开头时,此配置使用标准MVC中间件. #### Swagger -This gateway is configured to use the [swagger UI](https://swagger.io/tools/swagger-ui/), a popular tool to discover & test HTTP APIs. Normally, Ocelot does not support to show APIs on the swagger, because it can not know details of each microservice API. But it is possible when you follow ABP layered module architecture [best practices](../Best-Practices/Index.md). +此网关配置为使用[swagger UI](https://swagger.io/tools/swagger-ui/), 这是一种用于发现和测试HTTP API的流行工具. 通常,Ocelot不支持在swagger上显示API,因为它无法了解每个微服务API的详细信息. 但是当你遵循ABP分层模块架构[最佳实践](../Best-Practices/Index.md)时,它是可能的. -`BackendAdminAppGatewayHostModule` adds dependency to `AbpIdentityHttpApiModule` (*[Volo.Abp.Identity.HttpApi](https://www.nuget.org/packages/Volo.Abp.Identity.HttpApi)* package) and `ProductManagementHttpApiModule` (*ProductManagement.HttpApi* project) to include their HTTP API Controllers. In this way, swagger can discover them. While it references to the API layer, it does not reference to the implementation of application services, because they will be running in the related microservice endpoints and redirected by the Ocelot based on the request URL. +`BackendAdminAppGatewayHostModule`将依赖性添加到`AbpIdentityHttpApiModule`(*[Volo.Abp.Identity.HttpApi](https://www.nuget.org/packages/Volo.Abp.Identity.HttpApi)* 包)和`ProductManagementHttpApiModule`(*ProductManagement.HttpApi* 项目)以包含它们的HTTP API控制器. 通过这种方式,swagger可以发现它们. 虽然它引用了API层,但它没有引用应用程序服务的实现,因为它们将在相关的微服务端点中运行,并由Ocelot根据请求URL重定向. -Anyway, when you open the URL `http://localhost:65115/swagger/index.html`, you will see APIs of all configured microservices. +无论如何,当你打开URL`http://localhost:65115/swagger/index.html`时, 你将看到所有已配置的微服务的API. -#### Permission Management +#### 权限管理 -Backend Admin Application provides a permission management UI (seen before) and uses this gateway to get/set permissions. Permission management API is hosted inside the gateway, instead of a separate service. This is a design decision, but it could be hosted as another microservice if you would like. +后端管理应用程序提供权限管理UI(之前见过),并使用此网关获取/设置权限. 权限管理API托管在网关内,而不是单独的服务. 这是一个设计决策,但如果您愿意,它可以作为另一个微服务托管. #### Dependencies -- **RabbitMQ** for messaging to other services. -- **Redis** for distributed/shared caching. -- **Elasticsearch** for storing logs. +- **RabbitMQ** 用于向其他服务发送消息. +- **Redis** 用于分布式/共享缓存. +- **Elasticsearch** 用于存储日志. -### Public Web Site Gateway (PublicWebSiteGateway.Host) +### 公共网站网关 (PublicWebSiteGateway.Host) -This is backend (server side API gateway) for the "Public Web Site" application. +这是"公共网站"应用程序的后端(服务器端API网关). -#### Authentication +#### 认证 -This gateway uses IdentityServer `Bearer` authentication and configured like that: +此网关使用IdentityServer `Bearer`身份验证并配置如下: ```csharp context.Services.AddAuthentication("Bearer") @@ -638,9 +656,9 @@ context.Services.AddAuthentication("Bearer") }); ``` -`AddIdentityServerAuthentication` extension method comes from the [IdentityServer4.AccessTokenValidation](https://www.nuget.org/packages/IdentityServer4.AccessTokenValidation) package, part of the IdentityServer4 project (see [its documentation](http://docs.identityserver.io/en/latest/topics/apis.html)). +`AddIdentityServerAuthentication` 扩展方法来自 [IdentityServer4.AccessTokenValidation](https://www.nuget.org/packages/IdentityServer4.AccessTokenValidation)包, IdentityServer4项目的一部分 (参见 [identityserver文档](http://docs.identityserver.io/en/latest/topics/apis.html)). -`ApiName` is the API which is being protected, `PublicWebSiteGateway` in this case. Rest of the configuration is related to claims mapping (which is planned to be automated in next ABP versions). The configuration related to authentication in the `appsettings.json` is simple: +`ApiName`是受保护的API,在这种情况下是`PublicWebSiteGateway`. 其余配置与声明映射(计划在下一个ABP版本中自动化)相关. 与`appsettings.json`中的身份验证相关的配置很简单: ```json "AuthServer": { @@ -649,9 +667,9 @@ context.Services.AddAuthentication("Bearer") } ``` -#### Ocelot Configuration +#### Ocelot配置 -Ocelot needs to know the real URLs of the microservices to be able to redirect HTTP requests. The configuration for this gateway is like below: +Ocelot需要知道微服务的真实URL才能重定向HTTP请求. 此网关的配置如下所示: ```json "ReRoutes": [ @@ -685,25 +703,25 @@ Ocelot needs to know the real URLs of the microservices to be able to redirect H } ``` -See [its own documentation](https://ocelot.readthedocs.io/en/latest/features/configuration.html) to better understand the Ocelot configuration. +参见 [ocelot文档](https://ocelot.readthedocs.io/en/latest/features/configuration.html) 更好地了解配置. -#### Other +#### 其它 -See the "ABP Configuration Endpoints" and "Swagger" topics inside the "Backend Admin Application Gateway" section which are very similar for this gateway. +请参阅"后端管理应用程序网关"部分中的"ABP配置端点"和"Swagger"主题,这些主题与此网关非常相似. -#### Dependencies +#### 依赖 -- **RabbitMQ** for messaging to other services. -- **Redis** for distributed/shared caching. -- **Elasticsearch** for storing logs. +- **RabbitMQ** 用于向其他服务发送消息. +- **Redis** 用于分布式/共享缓存. +- **Elasticsearch** 用于存储日志. -### Internal Gateway (InternalGateway.Host) +### 内部网关 (InternalGateway.Host) -This gateway is not a BFF. It is designed for inter-microservice communication and is not exposed publicly. +该网关不是BFF. 它专为微服务间通信而设计,不会公开. -#### Authentication +#### 认证 -This gateway uses IdentityServer `Bearer` authentication and configured like that: +此网关使用IdentityServer `Bearer`身份验证并配置如下: ```csharp context.Services.AddAuthentication("Bearer") @@ -722,9 +740,9 @@ context.Services.AddAuthentication("Bearer") }); ``` -`AddIdentityServerAuthentication` extension method comes from the [IdentityServer4.AccessTokenValidation](https://www.nuget.org/packages/IdentityServer4.AccessTokenValidation) package, part of the IdentityServer4 project (see [its documentation](http://docs.identityserver.io/en/latest/topics/apis.html)). +`AddIdentityServerAuthentication` 扩展方法来自 [IdentityServer4.AccessTokenValidation](https://www.nuget.org/packages/IdentityServer4.AccessTokenValidation)包, IdentityServer4项目的一部分 (参见 [identityserver文档](http://docs.identityserver.io/en/latest/topics/apis.html)). -`ApiName` is the API which is being protected, `InternalGateway` in this case. Rest of the configuration is related to claims mapping (which is planned to be automated in next ABP versions). The configuration related to authentication in the `appsettings.json` is simple: +`ApiName`是受保护的API,在这种情况下是`InternalGateway`. 其余配置与声明映射(计划在下一个ABP版本中自动化)相关. 与`appsettings.json`中的身份验证相关的配置很简单: ```json "AuthServer": { @@ -733,9 +751,9 @@ context.Services.AddAuthentication("Bearer") } ``` -#### Ocelot Configuration +#### Ocelot 配置 -Ocelot needs to know the real URLs of the microservices to be able to redirect HTTP requests. The configuration for this gateway is like below: +Ocelot需要知道微服务的真实URL才能重定向HTTP请求. 此网关的配置如下所示: ```json "ReRoutes": [ @@ -781,47 +799,47 @@ Ocelot needs to know the real URLs of the microservices to be able to redirect H } ``` -`ReRoutes` configuration covers all microservices in the system. See [its own documentation](https://ocelot.readthedocs.io/en/latest/features/configuration.html) to better understand the Ocelot configuration. +`ReRoutes`配置涵盖了系统中的所有微服务. 参见 [ocelot文档](https://ocelot.readthedocs.io/en/latest/features/configuration.html) 更好地了解配置. -#### Other +#### 其它 -See the "ABP Configuration Endpoints" and "Swagger" topics inside the "Backend Admin Application Gateway" section which are very similar for this gateway. +请参阅"后端管理应用程序网关"部分中的"ABP配置端点"和"Swagger"主题,这些主题与此网关非常相似. -#### Dependencies +#### 依赖 -- **RabbitMQ** for messaging to other services. -- **Redis** for distributed/shared caching. -- **Elasticsearch** for storing logs. +- **RabbitMQ** 用于向其他服务发送消息. +- **Redis** 用于分布式/共享缓存. +- **Elasticsearch** 用于存储日志. -## Microservices +## 微服务 -Microservices are standalone HTTP APIs those implement the business of the system in a distributed manner. +微服务是独立的HTTP API,它们以分布式方式实现系统业务. -* They are used by applications and other microservices through the gateways and HTTP APIs. -* They can raise or register to events in the system. -* They can communicate to each other via asynchronous messaging. +* 它们由应用程序和其他微服务通过网关和HTTP API使用. +* 他们可以提升或注册系统中的事件. +* 它们可以通过异步消息传递相互通信. ### Identity Service (IdentityService.Host) -This service provides user and role management APIs. +此服务提供用户和角色管理API. -#### Database +#### 数据库 -Shares the same database (MsDemo_Identity) with the AuthServer application. +与AuthServer应用程序共享相同的数据库(MsDemo_Identity). -#### Identity Module +#### Identity模块 -This service actually just hosts the ABP Identity package/module. Does not include any API itself. In order to host it, adds the following dependencies: +该服务实际上只托管ABP身份包/模块. 不包含任何API本身. 为了托管它,添加以下依赖项: -* `AbpIdentityHttpApiModule` (*[Volo.Abp.Identity.HttpApi](https://www.nuget.org/packages/Volo.Abp.Identity.HttpApi)* package) to provide Identity APIs. -* `AbpIdentityApplicationModule` (*[Volo.Abp.Identity.Application](https://www.nuget.org/packages/Volo.Abp.Identity.Application)* package) to host the implementation of the application and domain layers of the module. -* `AbpIdentityEntityFrameworkCoreModule` (*[Volo.Abp.Identity.EntityFrameworkCore](https://www.nuget.org/packages/Volo.Abp.Identity.EntityFrameworkCore)* package) to use EF Core as database API. +* `AbpIdentityHttpApiModule` (*[Volo.Abp.Identity.HttpApi](https://www.nuget.org/packages/Volo.Abp.Identity.HttpApi)* 包) 提供身份API. +* `AbpIdentityApplicationModule` (*[Volo.Abp.Identity.Application](https://www.nuget.org/packages/Volo.Abp.Identity.Application)* 包)承载模块的应用程序和域层的实现. +* `AbpIdentityEntityFrameworkCoreModule` (*[Volo.Abp.Identity.EntityFrameworkCore](https://www.nuget.org/packages/Volo.Abp.Identity.EntityFrameworkCore)* 包) 使用EF Core作为数据库API. -See the [module architecture best practice guide](../Best-Practices/Module-Architecture) to understand the layering better. +请参阅[module architecture best practice guide](../Best-Practices/Module-Architecture)以更好地理解分层. -#### Authentication +#### 认证 -This microservice uses IdentityServer `Bearer` authentication and configured like that: +这个微服务使用IdentityServer`Bearer`身份验证,并配置如下: ```csharp context.Services.AddAuthentication("Bearer") @@ -841,7 +859,7 @@ context.Services.AddAuthentication("Bearer") }); ``` -`ApiName` is the API which is being protected, `IdentityService` in this case. Rest of the configuration is related to claims mapping (which is planned to be automated in next ABP versions). The configuration related to authentication in the `appsettings.json` is simple: +`ApiName`是受保护的API,在这种情况下是`IdentityService`. 其余配置与声明映射(计划在下一个ABP版本中自动化)相关. 与`appsettings.json`中的身份验证相关的配置很简单: ```json "AuthServer": { @@ -852,21 +870,21 @@ context.Services.AddAuthentication("Bearer") #### Swagger -Swagger UI is configured and is the default page for this service. If you navigate to the URL `http://localhost:63568/`, you are redirected to the swagger page to see and test the API. +Swagger UI已配置,是此服务的默认页面. 如果你导航到URL`http://localhost:63568/`, 你将被重定向到swagger页面以查看和测试API. -#### Dependencies +#### 依赖 -- **RabbitMQ** for messaging to other services. -- **Redis** for distributed/shared caching. -- **Elasticsearch** for storing logs. +- **RabbitMQ** 用于向其他服务发送消息. +- **Redis** 用于分布式/共享缓存. +- **Elasticsearch** 用于存储日志. -### Blogging Service (BloggingService.Host) +### 博客服务 (BloggingService.Host) -This service hosts the blogging API. +此服务托管博客API. -#### Database +#### 数据库 -It has a dedicated MongoDB database (MsDemo_Blogging) to store blog and posts. It also uses the MsDemo_Identity SQL database for audit logs, permissions and settings. So, there are two connection strings in the `appsettings.json` file: +它有一个专门的MongoDB数据库(MsDemo_Blogging)来存储博客和帖子. 它还使用MsDemo_Identity SQL数据库来审核日志,权限和设置. 因此,`appsettings.json`文件中有两个连接字符串: ````json "ConnectionStrings": { @@ -875,19 +893,19 @@ It has a dedicated MongoDB database (MsDemo_Blogging) to store blog and posts. I } ```` -#### Blogging Module +#### 博客模块 -This service actually just hosts the ABP Blogging package/module. Does not include any API itself. In order to host it, adds the following dependencies: +该服务实际上只托管ABP Blogging包/模块. 不包含任何API本身. 为了托管它,添加以下依赖项: -- `BloggingHttpApiModule` (*[Volo.Blogging.HttpApi](https://www.nuget.org/packages/Volo.Blogging.HttpApi)* package) to provide Blogging APIs. -- `BloggingApplicationModule` (*[Volo.Blogging.Application](https://www.nuget.org/packages/Volo.Blogging.Application)* package) to host the implementation of the application and domain layers of the module. -- `BloggingMongoDbModule` (*[Volo.Blogging.MongoDB](https://www.nuget.org/packages/Volo.Abp.Identity.EntityFrameworkCore)* package) to use MongoDB as the database. +- `BloggingHttpApiModule` (*[Volo.Blogging.HttpApi](https://www.nuget.org/packages/Volo.Blogging.HttpApi)* 包) 提供Blogging API. +- `BloggingApplicationModule` (*[Volo.Blogging.Application](https://www.nuget.org/packages/Volo.Blogging.Application)* 包) 承载模块的应用程序和域层的实现. +- `BloggingMongoDbModule` (*[Volo.Blogging.MongoDB](https://www.nuget.org/packages/Volo.Abp.Identity.EntityFrameworkCore)* 包) 使用MongoDB作为数据库. -See the [module architecture best practice guide](../Best-Practices/Module-Architecture) to understand the layering better. +请参阅[module architecture best practice guide](../Best-Practices/Module-Architecture)以更好地理解分层. -#### Authentication +#### 认证 -This microservice uses IdentityServer `Bearer` authentication and configured like that: +这个微服务使用IdentityServer `Bearer`身份验证,并配置如下: ```csharp context.Services.AddAuthentication("Bearer") @@ -907,7 +925,7 @@ context.Services.AddAuthentication("Bearer") }); ``` -`ApiName` is the API which is being protected, `BloggingService` in this case. Rest of the configuration is related to claims mapping (which is planned to be automated in next ABP versions). The configuration related to authentication in the `appsettings.json` is simple: +`ApiName`是受保护的API,在这种情况下是`BloggingService`. 其余配置与声明映射(计划在下一个ABP版本中自动化)相关.与`appsettings.json`中的身份验证相关的配置很简单: ```json "AuthServer": { @@ -918,7 +936,7 @@ context.Services.AddAuthentication("Bearer") #### IdentityServer Client -This microservice also uses the Identity microservice API through the Internal Gateway, because it needs to query user details (username, email, phone, name and surname) in some cases. So, it is also a client for the IdentityServer and defines a section in the `appsettings.json` file for that: +此微服务还通过内部网关使用Identity微服务API, 因为在某些情况下它需要查询用户详细信息(username, email, phone, name and surname). 因此,它也是IdentityServer的客户端,并在`appsettings.json`文件中定义了一个部分: ````json "IdentityClients": { @@ -932,7 +950,7 @@ This microservice also uses the Identity microservice API through the Internal G } ```` -Since it uses the Internal Gateway, it should also configure the remote endpoint of the gateway: +由于它使用内部网关, 因此它还应配置网关的远程端点: ````json "RemoteServices": { @@ -943,31 +961,31 @@ Since it uses the Internal Gateway, it should also configure the remote endpoint } ```` -When you set `UseCurrentAccessToken` to `false`, ABP ignores the current `access_token` in the current `HttpContext` and authenticates to the AuthServer with the credentials defined above. +当你将`UseCurrentAccessToken`设置为`false`时,ABP会忽略当前`HttpContext`中的当前`access_token`,并使用上面定义的凭据对AuthServer进行身份验证. -Why not using the token of the current user in the current request? Because, the user may not have required permissions on the Identity module, so it can not just pass the current authentication token directly to the Identity service. In addition, some of the blog service APIs are anonymous (not requires authenticated user), so in some cases there is no "current user" in the HTTP request. For these reasons, Blogging service should be defined as a client for the Identity service with its own credentials and permissions. +为什么不在当前请求中使用当前用户的令牌? 因为,用户可能没有Identity模块所需的权限,因此它不能直接将当前身份验证令牌传递给Identity服务. 此外,某些博客服务API是匿名的(不需要经过身份验证的用户),因此在某些情况下,HTTP请求中没有"当前用户". 出于这些原因,应将Blogging服务定义为具有自己的凭据和权限的Identity服务的客户端. -If you check the `AbpPermissionGrants` table in the `MsDemo_Identity` database, you can see the related permission for the `blogging-service-client`. +如果检查`MsDemo_Identity`数据库中的`AbpPermissionGrants`表,则可以看到`blogging-service-client`的相关权限. ![microservice-sample-blogservice-permission-in-database](../images/microservice-sample-blogservice-permission-in-database.png) #### Swagger -Swagger UI is configured and is the default page for this service. If you navigate to the URL `http://localhost:62157/`, you are redirected to the swagger page to see and test the API. +Swagger UI已配置,是此服务的默认页面. 如果你导航到URL`http://localhost:62157/`, 你将被重定向到swagger页面以查看和测试API. -#### Dependencies +#### 依赖 -- **RabbitMQ** for messaging to other services. -- **Redis** for distributed/shared caching. -- **Elasticsearch** for storing logs. +- **RabbitMQ** 用于向其他服务发送消息. +- **Redis** 用于分布式/共享缓存. +- **Elasticsearch** 用于存储日志. -### Product Service (ProductService.Host) +### 产品服务 (ProductService.Host) -This service hosts the Product Management API. +此服务托管产品管理API. -#### Database & EF Core Migrations +#### 数据库和EF核心迁移 -It has a separated SQL database, named **MsDemo_ProductManagement**, for the product management module. It uses EF Core as the database provider and has a DbContext named `ProductServiceMigrationDbContext`: +它有一个单独的SQL数据库,名为**MsDemo_ProductManagement**,用于产品管理模块. 它使用EF Core作为数据库提供程序,并具有名为`ProductServiceMigrationDbContext`的DbContext: ````csharp public class ProductServiceMigrationDbContext : AbpDbContext @@ -988,11 +1006,11 @@ public class ProductServiceMigrationDbContext : AbpDbContext @@ -1107,9 +1126,9 @@ public class Product : AuditedAggregateRoot } ```` -All of its properties have private setters which prevents any direct change of the properties from out of the class. Product class ensures its own integrity and validity by its own constructors and methods. +它的所有属性都有私有的set方法,可以防止属性从类中直接更改. 产品类通过自己的构造函数和方法确保其自身的完整性和有效性. -It has two constructors: +它有两个构造函数: ````csharp private Product() @@ -1142,11 +1161,11 @@ internal Product( ```` -Default (**parameterless**) constructor is private and is not used in the application code. It is needed because most ORMs requires a parameterless constructor on deserializing entities while getting from the database. +默认(**无参数**)构造函数是私有的,不在应用程序代码中使用. 这是必需的,因为大多数ORM在从数据库获取时需要在反序列化实体时使用无参数构造函数. -Second constructor is **internal** that means it can only be used inside the domain layer. This enforces to use the `ProductManager` while creating a new `Product`. Because, `ProductManager` should implement a business rule on a new product creation. This constructor only requires the minimal required arguments to create a new product with some optional arguments. It checks some simple business rules to ensure that the entity is created as a valid product. +第二个构造函数是**internal**,这意味着它只能在域层内使用. 这强制在创建新的`Product`时使用`ProductManager`. 因为``ProductManager`应该在新产品创建上实现业务规则. 此构造函数仅需要最少的必需参数来创建具有一些可选参数的新产品. 它会检查一些简单的业务规则,以确保将实体创建为有效产品. -Rest of the class has methods to manipulate properties of the entity. Example: +该类的其余部分具有操纵实体属性的方法. 例: ````csharp public Product SetPrice(float price) @@ -1162,9 +1181,9 @@ public Product SetPrice(float price) ```` -`SetPrice` method is used to change the price of the product in a safe manner (by checking a validation rule). +`SetPrice`方法用于以安全的方式更改产品的价格(通过检查验证规则). -`SetStockCount` is another method that is used to change stock count of a product: +`SetStockCount`是另一种用于更改产品库存数量的方法: ````csharp public Product SetStockCount(int stockCount) @@ -1199,11 +1218,11 @@ private Product SetStockCountInternal(int stockCount, bool triggerEvent = true) ```` -This method also triggers a **distributed event** with the `ProductStockCountChangedEto` parameter (Eto is a conventional postfix stands for **E**vent **T**ransfer **O**bject, but not required) to notify listeners that stock count of a product has changed. Any subscriber can receive this event and perform an action based on that knowledge. +此方法还触发**分布式事件**,其中带有`ProductStockCountChangedEto`参数(Eto是传统的后缀代表**E**vent **T**ransfer **O**bject,但不是必需的)通知产品库存数量的听众已发生变化. 任何订户都可以接收此事件并根据该知识执行操作. -Events are distributed by RabbitMQ for this solution. But ABP is message broker independent by providing necessary abstractions (see the [Event Bus](../Event-Bus.md) document). +RabbitMQ为此解决方案分发事件. 但是ABP通过提供必要的抽象来独立于消息代理[Event Bus](../Event-Bus.md)文档). -As said before, this module forces to always use the `ProductManager` to create a new `Product`. `ProductManager` is a simple domain service defined as shown: +如前所述,该模块强制始终使用`ProductManager`来创建新的`Product`. `ProductManager`是一个简单的域服务,定义如下: ````csharp public class ProductManager : DomainService @@ -1242,13 +1261,13 @@ public class ProductManager : DomainService } ```` -* It checks if given code is used before. Throws `ProductCodeAlreadyExistsException` so. -* If uses the `GuidGenerator` (`IGuidGenerator`) service to create a new `Guid`. -* It inserts the entity to the repository. +* 它检查之前是否使用过给定的代码. 否则抛出`ProductCodeAlreadyExistsException`. +* 使用`GuidGenerator`(`IGuidGenerator`)服务来创建一个新的`Guid`. +* 它将实体插入存储库. -So, with this design, uniqueness of the product code is guaranteed. +因此,通过这种设计,产品代码的唯一性得到保证. -`ProductCodeAlreadyExistsException` is a domain/business exception defined as like below: +`ProductCodeAlreadyExistsException`是一个域/业务异常,定义如下: ````csharp public class ProductCodeAlreadyExistsException : BusinessException @@ -1261,18 +1280,18 @@ public class ProductCodeAlreadyExistsException : BusinessException } ```` -`PM:000001` is a code for the exception type that is sent to the clients, so they can understand the error type. Not implemented for this case, but it is also possible to localize business exceptions. See the [exception handling documentation](../Exception-Handling.md). +`PM:000001`是发送给客户端的异常类型的代码,因此他们可以理解错误类型. 在这种情况下没有实现,但也可以本地化业务异常. 请参阅[异常处理文档](../Exception-Handling.md). -#### Application Layer +#### 应用层 -Application layer of this module has two services: +该模块的应用层有两个服务: -* `ProductAppService` is mainly used by the Backend Admin Application to manage (create, update, delete...) products. It requires permission to perform any operation. -* `PublicProductAppService` is used by the Public Web Site to show list of products to the visitors. It does not require any permission since most of the visitors are not logged in to the application. +* `ProductAppService`主要由后端管理应用程序用于管理(创建,更新,删除.)产品. 它需要许可才能执行任何操作. +* 公共网站使用`PublicProductAppService`来向访问者显示产品列表. 它不需要任何权限,因为大多数访问者都没有登录到应用程序. -Notice that; instead of putting two application service into the same project, it might be a better principle to have separated application layers per application. But we unified them for simplicity in this solution. +请注意; 每个应用程序分离应用程序层可能是更好的原则, 而不是将两个应用程序服务放入同一个项目中. 但是我们在这个解决方案中简化了它们. -As an example, `ProductAppService` has the following method to update a product: +例如,`ProductAppService`具有以下更新产品的方法: ````csharp [Authorize(ProductManagementPermissions.Products.Update)] @@ -1288,29 +1307,29 @@ public async Task UpdateAsync(Guid id, UpdateProductDto input) } ```` -* It defines the required permission (*ProductManagementPermissions.Products.Update* is a constant with value `ProductManagement.Update`) to perform this operation. -* Gets the id of the product and a DTO contains the values to update. -* Gets the related product entity from the repository. -* Uses the related methods (like `SetName`) of the `Product` class to change properties, because they are with private setters and the only way to change a value is to use an entity method. -* Returns an updated `ProductDto` to the client (client may need it for some reason) by using the [ObjectMapper](../Object-To-Object-Mapping.md). +* 它定义了所需的权限(*ProductManagementPermissions.Products.Update*是一个值为`ProductManagement.Update`的常量)来执行此操作. +* 获取产品的ID,DTO包含要更新的值. +* 从存储库中获取相关的产品实体. +* 使用`Product`类的相关方法(如`SetName`)来更改属性,因为它们是私有set方法,更改值的唯一方法是使用实体方法. +* 通过使用[ObjectMapper](../Object-To-Object-Mapping.md)向客户端返回更新的`ProductDto`(客户端可能由于某种原因需要它). -The implementation may vary based on the requirements. This implementation follows the [best practices offered here](../Best-Practices/Application-Services.md). +实施可能会根据要求而有所不同. 此实现遵循此处提供的[最佳实践](../Best-Practices/Application-Services.md). -#### Other Layers +#### 其他层 -See other layers from the source code. +请参阅源代码中的其他层. -## Infrastructure +## 基础设施 -### Messaging and RabbitMQ +### 消息和RabbitMQ -Asynchronous Messaging is a key concept in distributed systems. It makes possible to communicate as a loosely coupled manner with fault tolerance. It does not require both sides to be online at the moment of messaging. So, it is a widely used communication pattern in microservice architecture. +异步消息传递是分布式系统中的关键概念. 它可以以松散耦合的方式与容错进行通信. 在发送消息时,它不要求双方都在线. 因此,它是微服务架构中广泛使用的通信模式. -#### Distributed Event Bus +#### 分布式事件总线 -Distributed Events (Event Bus) is a way of messaging where a service raise/trigger events while other services registers/listens to these events to be notified when an important event occurs. ABP makes distributed events easier to use by providing conventions, services and integrations. +分布式事件(事件总线)是一种消息传递方式,其中服务引发/触发事件,而其他服务注册/侦听这些事件,以便在发生重要事件时得到通知. ABP通过提供约定,服务和集成使分布式事件更易于使用. -You have seen that the `Product` class publishing an event using the following code line: +您已经看到`Product`类使用以下代码行发布事件: ````csharp AddDistributedEvent(new ProductStockCountChangedEto(Id, StockCount, stockCount)); @@ -1342,7 +1361,7 @@ public class ProductStockCountChangedEto : EtoBase } ```` -This object stores necessary information about the event. Another service can easily register to this event by implementing the `IDistributedEventHandler` interface with the generic `ProductStockCountChangedEto` parameter: +该对象存储有关该事件的必要信息. 通过使用通用的`ProductStockCountChangedEto`参数实现`IDistributedEventHandler`接口,另一个服务可以轻松注册到此事件: ````csharp public class MyHandler : IDistributedEventHandler @@ -1355,17 +1374,17 @@ public class MyHandler : IDistributedEventHandler } ```` -All the integration and communication are done by the ABP framework when you use the [Volo.Abp.EventBus.RabbitMQ](https://www.nuget.org/packages/Volo.Abp.EventBus.RabbitMQ) package. If you need to publish events out of an entity, just inject the `IDistributedEventBus` and use the `PublishAsync` method. +当你使用[Volo.Abp.EventBus.RabbitMQ](https://www.nuget.org/packages/Volo.Abp.EventBus.RabbitMQ)包时,所有集成和通信都由ABP框架完成.如果需要从实体发布事件,只需注入`IDistributedEventBus`并使用`PublishAsync`方法. -See the [Event Bus](../Event-Bus.md) documentation for more information about the distributed event system. +有关分布式事件系统的更多信息, 请参见[Event Bus](../Event-Bus.md)文档. -#### RabbitMQ Configuration +#### RabbitMQ配置 -In this solution, [RabbitMQ](https://www.rabbitmq.com/) is used for messaging & distributed events. +在此解决方案中, [RabbitMQ](https://www.rabbitmq.com/)用于消息传递和分布式事件. -[Volo.Abp.EventBus.RabbitMQ](https://www.nuget.org/packages/Volo.Abp.EventBus.RabbitMQ) package is required to integrate to the RabbitMQ for distributed event system. Then you need to add dependency to the `AbpEventBusRabbitMqModule` for your module. For example, `ProductServiceHostModule` declares this dependency. +[Volo.Abp.EventBus.RabbitMQ](https://www.nuget.org/packages/Volo.Abp.EventBus.RabbitMQ)包需要集成到RabbitMQ以用于分布式事件系统. 然后你需要为模块的`AbpEventBusRabbitMqModule`添加依赖项. 例如`ProductServiceHostModule`声明了这种依赖. -`AbpEventBusRabbitMqModule` gets configuration from the `appsettings.json` by default. For example, the Product Service has such a configuration: +默认情况下,`abpEventBusRabbitMqModule`从`appsettings.json`获取配置. 例如产品服务具有以下配置: ````json "RabbitMQ": { @@ -1381,19 +1400,19 @@ In this solution, [RabbitMQ](https://www.rabbitmq.com/) is used for messaging & } ```` -### Caching and Redis +### 缓存和Redis -A distributed system obviously needs to a distributed and shared cache, instead of isolated in-memory caches for each service. +分布式系统显然需要分布式和共享缓存,而不是每个服务的隔离内存缓存. -[Redis](https://redis.io/) is used as a distributed cache in this solution. The solution uses Microsoft's standard [Microsoft.Extensions.Caching.Redis](https://www.nuget.org/packages/Microsoft.Extensions.Caching.Redis) package for integration. All applications and services uses Redis cache when you use and configure this package. See [Microsoft's documentation](https://docs.microsoft.com/en-us/aspnet/core/performance/caching/distributed) for more. +[Redis](https://redis.io/)在此解决方案中用作分布式缓存. 该解决方案使用Microsoft的标准[Microsoft.Extensions.Caching.Redis](https://www.nuget.org/packages/Microsoft.Extensions.Caching.Redis) 包进行集成. 使用和配置此程序包时,所有应用程序和服务都使用Redis缓存. 有关详细信息请参阅[Microsoft的文档](https://docs.microsoft.com/en-us/aspnet/core/performance/caching/distributed). -The solution also uses the [Microsoft.AspNetCore.DataProtection.StackExchangeRedis](https://www.nuget.org/packages/Microsoft.AspNetCore.DataProtection.StackExchangeRedis) package to share data protection keys between applications and services over Redis cache. +该解决方案还使用[Microsoft.AspNetCore.DataProtection.StackExchangeRedis](https://www.nuget.org/packages/Microsoft.AspNetCore.DataProtection.StackExchangeRedis)包在Redis缓存上共享应用程序和服务之间的数据保护密钥. -### Logging, Serilog, Elasticsearch and Kibana +### Logging,Serilog,Elasticsearch和Kibana -This solution uses [Serilog](https://serilog.net/) as a logging library. It is a widely used library which has many data source integrations including [Elasticsearch](https://www.elastic.co/products/elasticsearch). +该解决方案使用[Serilog](https://serilog.net/)作为日志库. 它是一个广泛使用的库,有许多数据源集成,包括[Elasticsearch](https://www.elastic.co/products/elasticsearch). -Logging configurations are done in `Program.cs` files using a code block similar to the given below: +使用类似于下面给出的代码块在`Program.cs`文件中完成日志配置: ````csharp Log.Logger = new LoggerConfiguration() @@ -1412,9 +1431,9 @@ Log.Logger = new LoggerConfiguration() .CreateLogger(); ```` -This configures multiple log target: File and Elasticsearch. `Application` property is set to `ProductService` for this example. This is a way of distinguishing the logs of multiple services in a single database. You can then query logs by the `Application` name. +这会配置多个日志输出目标:File和Elasticsearch.对于此示例,`Application`属性设置为`ProductService`. 这是一种区分单个数据库中多个服务的日志的方法. 然后, 你可以通过`Application`名称查询日志. -Elasticsearch URL is read from the `appsettings.json` configuration file: +从`appsettings.json`配置文件中读取Elasticsearch URL: ````json "ElasticSearch": { @@ -1422,22 +1441,22 @@ Elasticsearch URL is read from the `appsettings.json` configuration file: } ```` -If you use Kibana, which is a Visualization tool that is well integrated to Elasticsearch, you can see some fancy UI about your logs: +如果你使用Kibana, 它是一个与Elasticsearch完美集成的可视化工具,可以看到有关你的日志的一些UI: ![microservice-sample-kibana-2](../images/microservice-sample-kibana-2.png) -*Figure - A dashboard that shows log and error counts by service/application.* +*Figure - 一个仪表板,显示服务/应用程序的日志和错误计数.* ![microservice-sample-kibana-1](../images/microservice-sample-kibana-1.png) -*Figure - A list of log entries* +*Figure - 日志条目列表* -Kibana URL is `http://localhost:5601/` by default. +Kibana URL默认为`http://localhost:5601/`. -### Audit Logging +### 审计日志 -ABP provides automatic audit logging which saves every request in detail (who is the current user, what is the browser/client, what actions performed, which entities changed, even which properties of entities has been updated). See the [audit logging document](../Audit-Logging.md) for details. +ABP提供自动审计日志记录,详细保存每个请求(当前用户,浏览器/客户端,执行了哪些操作,哪些实体更改,甚至实体的哪些属性已更新). 有关详细信息,请参阅[审计日志文档](../Audit-Logging.md). -All of the services and applications are configured to write audit logs. Audit logs are saved to the MsDemo_Identity SQL database. So, you can query all audit logs of all applications from a single point. +所有服务和应用程序都配置为编写审核日志. 审核日志将保存到MsDemo_Identity SQL数据库中. 因此,您可以从单个点查询所有应用程序的所有审核日志. -An Audit Log record has a `CorrelationId` property that can be used to track a request. When a service calls another service in a single web request, they both save audit logs with the same `CorrelationId`. See the `AbpAuditLogs` table in the database. \ No newline at end of file +审核日志记录具有`CorrelationId`属性,可用于跟踪请求. 当服务在单个Web请求中调用另一个服务时,它们都会使用相同的`CorrelationId`保存审核日志. 请参阅数据库中的`AbpAuditLogs`表. \ No newline at end of file diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-I.md b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-I.md index 34acc3f01f..dfc17e431d 100644 --- a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-I.md +++ b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-I.md @@ -20,7 +20,7 @@ 下面的图片展示了从启动模板创建的项目是如何分层的. -![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution.png) +![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-v2.png) ### 创建Book实体 @@ -93,7 +93,7 @@ public class BookStoreDbContext : AbpDbContext 这个启动模板使用了[EF Core Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/)来创建并维护数据库结构.打开 **Package Manager Console (PMC)** (工具/Nuget包管理器菜单),选择 `Acme.BookStore.EntityFrameworkCore`作为默认的项目然后执行下面的命令: -![bookstore-pmc-add-book-migration](images/bookstore-pmc-add-book-migration.png) +![bookstore-pmc-add-book-migration](images/bookstore-pmc-add-book-migration-v2.png) 这样就会在`Migrations`文件夹中创建一个新的migration类.然后执行`Update-Database`命令更新数据库结构. diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-III.md b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-III.md index 6b31e9ba84..0514c8fc1a 100644 --- a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-III.md +++ b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-III.md @@ -14,7 +14,6 @@ 本解决方案中有两个测试项目: -![bookstore-test-projects](images/bookstore-test-projects.png) * `Acme.BookStore.Application.Tests` 项目用于单元测试和集成测试.你可以在这个项目中为Application Service方法写测试代码.这个项目使用了 **EF Core SQLite in-memory** 数据库. * `Acme.BookStore.Web.Tests` 项目用于包含Web层的完整集成测试.所以,你也可以在这里写关于UI页面的测试. diff --git a/docs/zh-Hans/images/bookstore-visual-studio-solution-v2.png b/docs/zh-Hans/images/bookstore-visual-studio-solution-v2.png new file mode 100644 index 0000000000..48b5881e96 Binary files /dev/null and b/docs/zh-Hans/images/bookstore-visual-studio-solution-v2.png differ diff --git a/docs/zh-Hans/images/pcm-update-database-v2.png b/docs/zh-Hans/images/pcm-update-database-v2.png new file mode 100644 index 0000000000..d5bb9c2975 Binary files /dev/null and b/docs/zh-Hans/images/pcm-update-database-v2.png differ 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.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js index bdb7976286..39199ccf93 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js @@ -338,6 +338,23 @@ } } + configuration.language = { + info: localize("PagerInfo"), + infoFiltered: localize("PagerInfoFiltered"), + infoEmpty: localize("PagerInfoEmpty"), + search: localize("PagerSearch"), + processing: localize("ProcessingWithThreeDot"), + loadingRecords: localize("LoadingWithThreeDot"), + lengthMenu: localize("PagerShowMenuEntries"), + emptyTable: localize("NoDataAvailableInDatatable"), + paginate: { + first: localize("PagerFirst"), + last: localize("PagerLast"), + previous: localize("PagerPrevious"), + next: localize("PagerNext") + } + }; + configuration.dom = '<"dataTable_filters"f>rt<"row dataTable_footer"<"col-auto"l><"col-auto"i><"col"p>>'; return configuration; 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..b1741d0ca0 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(_cultureName, 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/Localization/Resources/AbpUi/en.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json index 3a2233c7a6..16af838025 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json @@ -25,14 +25,23 @@ "Delete": "Delete", "Edit": "Edit", "ProcessingWithThreeDot": "Processing...", + "LoadingWithThreeDot": "Loading...", + "Welcome": "Welcome", "Login": "Login", "Register": "Register", "Logout": "Log out", "Submit": "Submit", "Back": "Back", + "PagerSearch": "Search", "PagerNext": "Next", "PagerPrevious": "Previous", - "PagerInfo": "Showing {0} to {1} of {2} entries.", + "PagerFirst": "First", + "PagerLast": "Last", + "PagerInfo": "Showing _START_ to _END_ of _TOTAL_ entries", + "PagerInfoEmpty": "Showing 0 to 0 of 0 entries", + "PagerInfoFiltered": "(filtered from _MAX_ total entries)", + "NoDataAvailableInDatatable": "No data available in table", + "PagerShowMenuEntries": "Show _MENU_ entries", "DatatableActionDropdownDefaultText": "Actions", "ChangePassword": "Change password", "PersonalInfo": "My profile" diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json index 6162f7c35f..a3af7c3a22 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json @@ -25,14 +25,23 @@ "Delete": "Sil", "Edit": "Düzenle", "ProcessingWithThreeDot": "İşleniyor...", + "LoadingWithThreeDot": "Yükleniyor...", + "Welcome": "Hoşgeldiniz", "Login": "Giriş", "Register": "Kaydol", "Logout": "Çıkış", "Submit": "Gönder", "Back": "Geri", + "PagerSearch": "Ara", "PagerNext": "Sonraki", "PagerPrevious": "Önceki", - "PagerInfo": "{2} kayıttan {0} ile {1} arası gösteriliyor.", + "PagerFirst": "İlk", + "PagerLast": "Son", + "PagerInfo": "_TOTAL_ kayıttan _START_ ile _END_ arası gösteriliyor.", + "PagerInfoEmpty": "0 kayıttan 0 ile 0 arası gösteriliyor.", + "PagerInfoFiltered": "(_MAX_ kayıt arasından filtrelendi)", + "NoDataAvailableInDatatable": "Tabloda kayır mevcut değil.", + "PagerShowMenuEntries": "Sayfada _MENU_ kayıt göster.", "DatatableActionDropdownDefaultText": "İşlemler", "ChangePassword": "Şifre değiştir", "PersonalInfo": "Profilim" diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hans.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hans.json index e3d8028f0a..c6979529a7 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hans.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hans.json @@ -25,14 +25,23 @@ "Delete": "删除", "Edit": "修改", "ProcessingWithThreeDot": "处理中...", + "LoadingWithThreeDot": "加载中...", + "Welcome": "欢迎", "Login": "登录", "Register": "注册", "Logout": "注销", "Submit": "提交", "Back": "返回", + "PagerSearch": "搜索", "PagerNext": "下一页", "PagerPrevious": "上一页", + "PagerFirst": "首页", + "PagerLast": "尾页", "PagerInfo": "显示 {0} 到 {1} 个 {2} 条目.", + "PagerInfoEmpty": "显示0个条目中的0到0", + "PagerInfoFiltered": "(从 _MAX_ 总条目中过滤掉)", + "NoDataAvailableInDatatable": "表中没有数据", + "PagerShowMenuEntries": "显示 _MENU_ 实体", "DatatableActionDropdownDefaultText": "操作", "ChangePassword": "修改密码", "PersonalInfo": "个人信息" 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/account/src/Volo.Abp.Account.Web/Areas/Account/Controllers/AccountController.cs b/modules/account/src/Volo.Abp.Account.Web/Areas/Account/Controllers/AccountController.cs index 3992e15f49..700d52b3a9 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Areas/Account/Controllers/AccountController.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Areas/Account/Controllers/AccountController.cs @@ -47,6 +47,12 @@ namespace Volo.Abp.Account.Web.Areas.Account.Controllers { ValidateLoginInfo(login); var identityUser = await _userManager.FindByNameAsync(login.UserNameOrEmailAddress); + + if (identityUser == null) + { + return new AbpLoginResult(LoginResultType.InvalidUserNameOrPassword); + } + return GetAbpLoginResult(await _signInManager.CheckPasswordSignInAsync(identityUser, login.Password, true)); } diff --git a/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/zh-Hans.json b/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/zh-Hans.json index fdaa5ff228..01e0b6b57d 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/zh-Hans.json +++ b/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/zh-Hans.json @@ -11,12 +11,12 @@ "InvalidUserNameOrPassword": "用户名或密码错误!", "LoginIsNotAllowed": "无法登录!你需要验证邮箱地址/手机号.", "SelfRegistrationDisabledMessage": "应用程序未开放注册,请联系管理员添加新用户.", - "Login": "Login", - "Cancel": "Cancel", - "Register": "Register", - "UseAnotherServiceToLogIn.": "Use another service to log in.", - "InvalidLoginRequest": "Invalid login request", - "ThereAreNoLoginSchemesConfiguredForThisClient": "There are no login schemes configured for this client.", - "LogInUsingYourProviderAccount": "Log in using your {0} account" + "Login": "登录", + "Cancel": "取消", + "Register": "注册", + "UseAnotherServiceToLogIn.": "使用其他服务登录.", + "InvalidLoginRequest": "登录请求无效", + "ThereAreNoLoginSchemesConfiguredForThisClient": "没有为此客户端配置登录方案.", + "LogInUsingYourProviderAccount": "使用你的{0}帐户登录" } } 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.EntityFrameworkCore/Volo/Blogging/EntityFrameworkCore/BloggingDbContextModelBuilderExtensions.cs b/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/EntityFrameworkCore/BloggingDbContextModelBuilderExtensions.cs index 7b7728c0f7..397739263a 100644 --- a/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/EntityFrameworkCore/BloggingDbContextModelBuilderExtensions.cs +++ b/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/EntityFrameworkCore/BloggingDbContextModelBuilderExtensions.cs @@ -27,7 +27,7 @@ namespace Volo.Blogging.EntityFrameworkCore { b.ToTable(options.TablePrefix + "Users", options.Schema); - b.ConfigureAbpUser(options); + b.ConfigureAbpUser(); b.ConfigureExtraProperties(); }); 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.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/zh-Hans.json b/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/zh-Hans.json new file mode 100644 index 0000000000..3c8847ceae --- /dev/null +++ b/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/zh-Hans.json @@ -0,0 +1,10 @@ +{ + "culture": "zh-Hans", + "texts": { + "Permission:DocumentManagement": "文档管理", + "Permission:Projects": "项目", + "Permission:Edit": "编辑", + "Permission:Delete": "删除", + "Permission:Create": "创建" + } + } \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Admin.Web/Localization/Resources/Docs/Web/zh-Hans.json b/modules/docs/src/Volo.Docs.Admin.Web/Localization/Resources/Docs/Web/zh-Hans.json new file mode 100644 index 0000000000..36bc285090 --- /dev/null +++ b/modules/docs/src/Volo.Docs.Admin.Web/Localization/Resources/Docs/Web/zh-Hans.json @@ -0,0 +1,26 @@ +{ + "culture": "zh-Hans", + "texts": { + "Menu:DocumentManagement": "文档", + "Menu:ProjectManagement": "项目", + "CreateANewProject": "创建新项目", + "Edit": "编辑", + "Create": "创建", + "Projects": "项目", + "Name": "名称", + "ShortName": "简称", + "DocumentStoreType": "文件存储类型", + "Format": "格式", + "ShortNameInfoText": "将用于唯一的URL.", + "DisplayName:Name": "名称", + "DisplayName:ShortName": "简称", + "DisplayName:Format": "格式", + "DisplayName:DefaultDocumentName": "默认文档名称", + "DisplayName:NavigationDocumentName": "导航文档名称", + "DisplayName:MinimumVersion": "最低版本", + "DisplayName:MainWebsiteUrl": "主网站网址", + "DisplayName:LatestVersionBranchName": "最新版本的分支名称", + "DisplayName:GitHubRootUrl": "GitHub根网址", + "DisplayName:GitHubAccessToken": "GitHub访问令牌" + } + } \ No newline at end of file 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/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hans.json b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hans.json index 03a22148c2..f085265abf 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hans.json +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hans.json @@ -2,14 +2,17 @@ "culture": "zh-Hans", "texts": { "Documents": "文档", - "BackToWebsite": "返回网站", + "BackToWebsite": "返回主网站", + "Contributors": "贡献者", + "ShareOn": "分享到", "Version": "版本", "Edit": "编辑", - "InThisDocument": "在这个文档中", - "GoToTop": "返回顶部", + "Delete": "删除", + "InThisDocument": "在本文中", + "GoToTop": "到顶部", "Projects": "项目", - "NoProjectWarning": "当前并没有项目!", - "DocumentNotFound": "抱歉,您请求的文档不存在!", - "NavigationDocumentNotFound": "此版本并没有导航文档!" + "NoProjectWarning": "还没有项目!", + "DocumentNotFound": "找不到请求的文档!", + "NavigationDocumentNotFound": "这个版本没有导航文件!" } } 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.Contracts/Volo/Abp/Identity/Localization/ApplicationContracts/zh-Hans.json b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/Localization/ApplicationContracts/zh-Hans.json index 616e9a62a3..cae53933d9 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/Localization/ApplicationContracts/zh-Hans.json +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/Localization/ApplicationContracts/zh-Hans.json @@ -1,12 +1,14 @@ { "culture": "zh-Hans", "texts": { - "Permission:IdentityManagement": "身份认证管理", + "Volo.Abp.Identity:010001": "您无法删除自己的帐户!", + "Permission:IdentityManagement": "身份标识管理", "Permission:RoleManagement": "角色管理", "Permission:Create": "创建", "Permission:Edit": "编辑", "Permission:Delete": "删除", "Permission:ChangePermissions": "更改权限", - "Permission:UserManagement": "用户管理" + "Permission:UserManagement": "用户管理", + "Permission:UserLookup": "用户查询" } } \ No newline at end of file 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..251b429d33 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 == 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.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs index ce8e1e47d5..0e3fb21688 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs @@ -25,7 +25,7 @@ namespace Volo.Abp.Identity.EntityFrameworkCore b.ConfigureFullAudited(); b.ConfigureExtraProperties(); b.ConfigureConcurrencyStamp(); - b.ConfigureAbpUser(options); + b.ConfigureAbpUser(); b.Property(u => u.NormalizedUserName).IsRequired().HasMaxLength(IdentityUserConsts.MaxNormalizedUserNameLength).HasColumnName(nameof(IdentityUser.NormalizedUserName)); b.Property(u => u.NormalizedEmail).HasMaxLength(IdentityUserConsts.MaxNormalizedEmailLength).HasColumnName(nameof(IdentityUser.NormalizedEmail)); 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/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml index ed45747c0c..d20d0a0026 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml @@ -31,7 +31,6 @@ } -
diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml index be9cc9b205..247e59e46b 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml @@ -32,7 +32,6 @@ } -
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/src/Volo.Abp.PermissionManagement.Web/Localization/Resources/AbpPermissionManagement/zh-Hans.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Localization/Resources/AbpPermissionManagement/zh-Hans.json index 991c147b58..8384078b86 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Localization/Resources/AbpPermissionManagement/zh-Hans.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Localization/Resources/AbpPermissionManagement/zh-Hans.json @@ -1,6 +1,8 @@ { "culture": "zh-Hans", "texts": { - "Permissions": "权限" + "Permissions": "权限", + "OnlyProviderPermissons": "只有这个提供商", + "All": "所有" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml index beb39387b8..d1081f2f84 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml @@ -14,23 +14,24 @@ - + @for (var i = 0; i < Model.Groups.Count; i++) {

@Model.Groups[i].DisplayName


- - @for (var j = 0; j < Model.Groups[i].Permissions.Count; j++) - { - - - } +
+ @for (var j = 0; j < Model.Groups[i].Permissions.Count; j++) + { + + + } +
}
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/AbpSettingManagementWebModule.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/AbpSettingManagementWebModule.cs index b10d3c69c3..f1fae6b2f3 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/AbpSettingManagementWebModule.cs +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/AbpSettingManagementWebModule.cs @@ -1,5 +1,7 @@ using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; +using Volo.Abp.Localization; using Volo.Abp.Modularity; +using Volo.Abp.SettingManagement.Localization; using Volo.Abp.SettingManagement.Web.Navigation; using Volo.Abp.UI.Navigation; using Volo.Abp.VirtualFileSystem; @@ -22,6 +24,19 @@ namespace Volo.Abp.SettingManagement.Web { options.FileSets.AddEmbedded("Volo.Abp.SettingManagement.Web"); }); + + Configure(options => + { + options.Resources + .Add("en"); + }); + + Configure(options => + { + options.Resources + .Get() + .AddVirtualJson("/Localization/Resources/AbpSettingManagement"); + }); } } } diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Localization/AbpSettingManagementResource.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Localization/AbpSettingManagementResource.cs new file mode 100644 index 0000000000..a89f749e4c --- /dev/null +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Localization/AbpSettingManagementResource.cs @@ -0,0 +1,10 @@ +using Volo.Abp.Localization; + +namespace Volo.Abp.SettingManagement.Localization +{ + [LocalizationResourceName("AbpSettingManagement")] + public class AbpSettingManagementResource + { + + } +} diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Localization/Resources/AbpSettingManagement/en.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Localization/Resources/AbpSettingManagement/en.json new file mode 100644 index 0000000000..654885f10b --- /dev/null +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Localization/Resources/AbpSettingManagement/en.json @@ -0,0 +1,6 @@ +{ + "culture": "en", + "texts": { + "Settings": "Settings" + } +} \ No newline at end of file diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Localization/Resources/AbpSettingManagement/tr.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Localization/Resources/AbpSettingManagement/tr.json new file mode 100644 index 0000000000..0e80027b84 --- /dev/null +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Localization/Resources/AbpSettingManagement/tr.json @@ -0,0 +1,6 @@ +{ + "culture": "tr", + "texts": { + "Settings": "Ayarlar" + } +} \ No newline at end of file diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Localization/Resources/AbpSettingManagement/zh-Hans.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Localization/Resources/AbpSettingManagement/zh-Hans.json new file mode 100644 index 0000000000..060fa17388 --- /dev/null +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Localization/Resources/AbpSettingManagement/zh-Hans.json @@ -0,0 +1,6 @@ +{ + "culture": "zh-Hans", + "texts": { + "Settings": "设置" + } + } \ No newline at end of file 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..79ed300398 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 @@ -1,7 +1,9 @@ using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; using Microsoft.Extensions.Options; using System.Linq; using System.Threading.Tasks; +using Volo.Abp.SettingManagement.Localization; using Volo.Abp.SettingManagement.Web.Pages.SettingManagement; using Volo.Abp.UI.Navigation; @@ -22,10 +24,18 @@ namespace Volo.Abp.SettingManagement.Web.Navigation return Task.CompletedTask; } - //TODO: Localize - //var l = context.ServiceProvider.GetRequiredService>(); + 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, + l["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/src/Volo.Abp.SettingManagement.Web/Pages/SettingManagement/Index.cshtml b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Pages/SettingManagement/Index.cshtml index c14c416a36..edcc541bfe 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Pages/SettingManagement/Index.cshtml +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Pages/SettingManagement/Index.cshtml @@ -1,11 +1,14 @@ @page @using Volo.Abp.AspNetCore.Mvc.UI.Layout +@using Microsoft.AspNetCore.Mvc.Localization +@using Volo.Abp.SettingManagement.Localization @using Volo.Abp.SettingManagement.Web.Navigation @using Volo.Abp.SettingManagement.Web.Pages.SettingManagement @model IndexModel +@inject IHtmlLocalizer L @inject IPageLayout PageLayout @{ - PageLayout.Content.Title = /*L["Settings"].Value*/ "Settings"; + PageLayout.Content.Title = L["Settings"].Value; PageLayout.Content.MenuItemName = SettingManagementMenuNames.GroupName; } @section scripts { @@ -14,21 +17,21 @@ } -
- - +
+ + - - @foreach (var group in Model.SettingPageCreationContext.Groups) - { - + + @foreach (var group in Model.SettingPageCreationContext.Groups) + { + -

@group.DisplayName

-
- @await Component.InvokeAsync(group.ComponentType) -
- } -
-
-
-
\ No newline at end of file +

@group.DisplayName

+
+ @await Component.InvokeAsync(group.ComponentType) + + } + +
+
+
\ No newline at end of file diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Volo.Abp.SettingManagement.Web.csproj b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Volo.Abp.SettingManagement.Web.csproj index b4d9324a10..bef5707d83 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Volo.Abp.SettingManagement.Web.csproj +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Volo.Abp.SettingManagement.Web.csproj @@ -14,6 +14,7 @@ + @@ -25,7 +26,6 @@ - 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/modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo/Abp/Users/EntityFrameworkCore/AbpUsersDbContextModelCreatingExtensions.cs b/modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo/Abp/Users/EntityFrameworkCore/AbpUsersDbContextModelCreatingExtensions.cs index 108b3f4278..1b95984e4b 100644 --- a/modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo/Abp/Users/EntityFrameworkCore/AbpUsersDbContextModelCreatingExtensions.cs +++ b/modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo/Abp/Users/EntityFrameworkCore/AbpUsersDbContextModelCreatingExtensions.cs @@ -1,12 +1,11 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Volo.Abp.EntityFrameworkCore.Modeling; namespace Volo.Abp.Users.EntityFrameworkCore { public static class AbpUsersDbContextModelCreatingExtensions { - public static void ConfigureAbpUser(this EntityTypeBuilder b, ModelBuilderConfigurationOptions options) + public static void ConfigureAbpUser(this EntityTypeBuilder b) where TUser : class, IUser { b.Property(u => u.TenantId).HasColumnName(nameof(IUser.TenantId)); 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..81a4b5c64d 100644 --- a/samples/MicroserviceDemo/k8s/auth-server-deployment.yaml +++ b/samples/MicroserviceDemo/k8s/auth-server-deployment.yaml @@ -1,35 +1,31 @@ -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: - labels: - io.kompose.service: auth-server name: auth-server spec: + selector: + matchLabels: + app: auth-server replicas: 1 - strategy: {} template: metadata: labels: - io.kompose.service: auth-server + app: auth-server spec: containers: - - env: - - name: ASPNETCORE_ENVIRONMENT - value: Development - - name: ASPNETCORE_URLS - value: http://0.0.0.0:64999 - - name: ConnectionStrings__Default - 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 - name: auth-server - ports: - - containerPort: 64999 - resources: {} - imagePullPolicy: Never - restartPolicy: Always -status: {} + - name: auth-server + image: "volosoft/microservice-demo-auth-server" + env: + - name: ASPNETCORE_ENVIRONMENT + value: Development + - name: ASPNETCORE_URLS + 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 + - name: RabbitMQ__Connections__Default__HostName + value: rabbitmq + - name: Redis__Configuration + value: redis + ports: + - name: http + containerPort: 51511 \ No newline at end of file diff --git a/samples/MicroserviceDemo/k8s/auth-server-service.yaml b/samples/MicroserviceDemo/k8s/auth-server-service.yaml index c96e9c3e3d..1ff487ac16 100644 --- a/samples/MicroserviceDemo/k8s/auth-server-service.yaml +++ b/samples/MicroserviceDemo/k8s/auth-server-service.yaml @@ -1,15 +1,11 @@ -apiVersion: v1 kind: Service +apiVersion: v1 metadata: - labels: - io.kompose.service: auth-server name: auth-server spec: - ports: - - name: "64999" - port: 64999 - targetPort: 64999 selector: - io.kompose.service: auth-server -status: - loadBalancer: {} + app: auth-server + ports: + - protocol: TCP + port: 51511 + targetPort: http \ No newline at end of file diff --git a/samples/MicroserviceDemo/k8s/backend-admin-app-deployment.yaml b/samples/MicroserviceDemo/k8s/backend-admin-app-deployment.yaml index ce6c238f7c..4f0f3d4602 100644 --- a/samples/MicroserviceDemo/k8s/backend-admin-app-deployment.yaml +++ b/samples/MicroserviceDemo/k8s/backend-admin-app-deployment.yaml @@ -1,34 +1,31 @@ -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: - labels: - io.kompose.service: backend-admin-app name: backend-admin-app spec: + selector: + matchLabels: + app: backend-admin-app replicas: 1 - strategy: {} template: metadata: labels: - io.kompose.service: backend-admin-app + app: backend-admin-app spec: containers: - - env: - - name: ASPNETCORE_ENVIRONMENT - value: Development - - name: ASPNETCORE_URLS - value: http://0.0.0.0:80 - - name: AuthServer__Authority - value: http://auth-server:64999 - - name: Redis__Configuration - value: redis - - name: RemoteServices__Default__BaseUrl - value: http://backend-admin-app-gateway/ - image: microservice-demo/backend-admin-app:latest - name: backend-admin-app - ports: - - containerPort: 80 - resources: {} - imagePullPolicy: Never - restartPolicy: Always -status: {} + - name: backend-admin-app + image: "volosoft/microservice-demo-backend-admin-app" + env: + - name: ASPNETCORE_ENVIRONMENT + value: Development + - name: ASPNETCORE_URLS + value: http://0.0.0.0:80 + - name: AuthServer__Authority + value: http://auth-server:51511 + - name: Redis__Configuration + value: redis + - name: RemoteServices__Default__BaseUrl + value: http://backend-admin-app-gateway/ + ports: + - name: http + containerPort: 80 \ No newline at end of file diff --git a/samples/MicroserviceDemo/k8s/backend-admin-app-gateway-deployment.yaml b/samples/MicroserviceDemo/k8s/backend-admin-app-gateway-deployment.yaml index 10490961e5..21d4260a98 100644 --- a/samples/MicroserviceDemo/k8s/backend-admin-app-gateway-deployment.yaml +++ b/samples/MicroserviceDemo/k8s/backend-admin-app-gateway-deployment.yaml @@ -1,47 +1,43 @@ -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: - labels: - io.kompose.service: backend-admin-app-gateway name: backend-admin-app-gateway spec: + selector: + matchLabels: + app: backend-admin-app-gateway replicas: 1 - strategy: {} template: metadata: labels: - io.kompose.service: backend-admin-app-gateway + app: backend-admin-app-gateway spec: containers: - - env: - - name: ASPNETCORE_ENVIRONMENT - value: Development - - name: ASPNETCORE_URLS - value: http://0.0.0.0:80 - - name: AuthServer__Authority - value: http://auth-server:64999 - - name: ConnectionStrings__Default - 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 - value: identity-service - - name: ReRoutes__0__DownstreamHostAndPorts__Port - value: "80" - - name: ReRoutes__1__DownstreamHostAndPorts__Host - value: product-service - - name: ReRoutes__1__DownstreamHostAndPorts__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 - name: backend-admin-app-gateway - ports: - - containerPort: 80 - resources: {} - imagePullPolicy: Never - restartPolicy: Always -status: {} + - name: backend-admin-app-gateway + image: "volosoft/microservice-demo-backend-admin-app-gateway" + env: + - name: ASPNETCORE_ENVIRONMENT + value: Development + - name: ASPNETCORE_URLS + value: http://0.0.0.0:80 + - name: AuthServer__Authority + 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: GlobalConfiguration__BaseUrl + value: http://backend-admin-app-gateway + - name: ReRoutes__0__DownstreamHostAndPorts__0__Host + value: identity-service + - name: ReRoutes__0__DownstreamHostAndPorts__0__Port + value: "80" + - name: ReRoutes__1__DownstreamHostAndPorts__0__Host + value: product-service + - name: ReRoutes__1__DownstreamHostAndPorts__0__Port + value: "80" + - name: Redis__Configuration + value: redis + - name: RemoteServices__Default__BaseUrl + value: http://backend-admin-app-gateway/ + ports: + - name: http + containerPort: 80 \ No newline at end of file diff --git a/samples/MicroserviceDemo/k8s/backend-admin-app-gateway-service.yaml b/samples/MicroserviceDemo/k8s/backend-admin-app-gateway-service.yaml index ca3faee62c..d773c940d2 100644 --- a/samples/MicroserviceDemo/k8s/backend-admin-app-gateway-service.yaml +++ b/samples/MicroserviceDemo/k8s/backend-admin-app-gateway-service.yaml @@ -1,15 +1,11 @@ -apiVersion: v1 kind: Service +apiVersion: v1 metadata: - labels: - io.kompose.service: backend-admin-app-gateway name: backend-admin-app-gateway spec: - ports: - - name: "65115" - port: 65115 - targetPort: 80 selector: - io.kompose.service: backend-admin-app-gateway -status: - loadBalancer: {} + app: backend-admin-app-gateway + ports: + - protocol: TCP + port: 80 + targetPort: http \ No newline at end of file diff --git a/samples/MicroserviceDemo/k8s/backend-admin-app-service.yaml b/samples/MicroserviceDemo/k8s/backend-admin-app-service.yaml index f735fdae1c..36a28cb7a1 100644 --- a/samples/MicroserviceDemo/k8s/backend-admin-app-service.yaml +++ b/samples/MicroserviceDemo/k8s/backend-admin-app-service.yaml @@ -1,15 +1,11 @@ -apiVersion: v1 kind: Service +apiVersion: v1 metadata: - labels: - io.kompose.service: backend-admin-app name: backend-admin-app spec: - ports: - - name: "3000" - port: 3000 - targetPort: 80 selector: - io.kompose.service: backend-admin-app -status: - loadBalancer: {} + app: backend-admin-app + ports: + - protocol: TCP + port: 80 + targetPort: http \ No newline at end of file diff --git a/samples/MicroserviceDemo/k8s/blogging-service-deployment.yaml b/samples/MicroserviceDemo/k8s/blogging-service-deployment.yaml index a5b801106b..5fa5c5542d 100644 --- a/samples/MicroserviceDemo/k8s/blogging-service-deployment.yaml +++ b/samples/MicroserviceDemo/k8s/blogging-service-deployment.yaml @@ -1,39 +1,37 @@ -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: - labels: - io.kompose.service: blogging-service name: blogging-service spec: + selector: + matchLabels: + app: blogging-service replicas: 1 - strategy: {} template: metadata: labels: - io.kompose.service: blogging-service + app: blogging-service spec: containers: - - env: - - name: ASPNETCORE_ENVIRONMENT - value: Development - - name: ASPNETCORE_URLS - value: http://0.0.0.0:80 - - name: AuthServer__Authority - value: http://auth-server:64999 - - 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 - - name: RabbitMQ__Connections__Default__HostName - value: rabbitmq - - name: Redis__Configuration - value: redis - image: microservice-demo/blogging-service:latest - name: blogging-service - ports: - - containerPort: 80 - resources: {} - imagePullPolicy: Never - restartPolicy: Always -status: {} + - name: blogging-service + image: "volosoft/microservice-demo-blogging-service" + env: + - name: ASPNETCORE_ENVIRONMENT + value: Development + - name: ASPNETCORE_URLS + value: http://0.0.0.0:80 + - name: AuthServer__Authority + 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 + - name: RabbitMQ__Connections__Default__HostName + value: rabbitmq + - name: Redis__Configuration + value: redis + - name: RemoteServices__Default__BaseUrl + value: http://internal-gateway/ + ports: + - name: http + containerPort: 80 \ No newline at end of file diff --git a/samples/MicroserviceDemo/k8s/blogging-service-service.yaml b/samples/MicroserviceDemo/k8s/blogging-service-service.yaml index d0ab95528e..4e756c01d7 100644 --- a/samples/MicroserviceDemo/k8s/blogging-service-service.yaml +++ b/samples/MicroserviceDemo/k8s/blogging-service-service.yaml @@ -1,15 +1,11 @@ -apiVersion: v1 kind: Service +apiVersion: v1 metadata: - labels: - io.kompose.service: blogging-service name: blogging-service spec: - ports: - - name: "62157" - port: 62157 - targetPort: 80 selector: - io.kompose.service: blogging-service -status: - loadBalancer: {} + app: blogging-service + ports: + - protocol: TCP + port: 80 + targetPort: http \ No newline at end of file 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/dbdata-persistentvolumeclaim.yaml b/samples/MicroserviceDemo/k8s/dbdata-persistentvolumeclaim.yaml index cfed8b93e2..6ba0609fb4 100644 --- a/samples/MicroserviceDemo/k8s/dbdata-persistentvolumeclaim.yaml +++ b/samples/MicroserviceDemo/k8s/dbdata-persistentvolumeclaim.yaml @@ -1,8 +1,6 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: - labels: - io.kompose.service: dbdata name: dbdata spec: accessModes: @@ -10,4 +8,3 @@ spec: resources: requests: storage: 500Mi -status: {} diff --git a/samples/MicroserviceDemo/k8s/expose-service.yaml b/samples/MicroserviceDemo/k8s/expose-service.yaml new file mode 100644 index 0000000000..b1b668a533 --- /dev/null +++ b/samples/MicroserviceDemo/k8s/expose-service.yaml @@ -0,0 +1,41 @@ +apiVersion: v1 +kind: Service +metadata: + name: auth-server-expose +spec: + type: LoadBalancer + selector: + app: auth-server + ports: + - protocol: TCP + port: 51511 + targetPort: 51511 + nodePort: 30001 +--- +apiVersion: v1 +kind: Service +metadata: + name: backend-admin-app-expose +spec: + type: LoadBalancer + selector: + app: backend-admin-app + ports: + - protocol: TCP + port: 80 + targetPort: 80 + nodePort: 30002 +--- +apiVersion: v1 +kind: Service +metadata: + name: public-website-expose +spec: + type: LoadBalancer + selector: + app: 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..add7d662c3 100644 --- a/samples/MicroserviceDemo/k8s/identity-service-deployment.yaml +++ b/samples/MicroserviceDemo/k8s/identity-service-deployment.yaml @@ -1,37 +1,33 @@ -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: - labels: - io.kompose.service: identity-service name: identity-service spec: + selector: + matchLabels: + app: identity-service replicas: 1 - strategy: {} template: metadata: labels: - io.kompose.service: identity-service + app: identity-service spec: containers: - - env: - - name: ASPNETCORE_ENVIRONMENT - value: Development - - name: ASPNETCORE_URLS - value: http://0.0.0.0:80 - - name: AuthServer__Authority - value: http://auth-server:64999 - - name: ConnectionStrings__Default - 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 - name: identity-service - ports: - - containerPort: 80 - resources: {} - imagePullPolicy: Never - restartPolicy: Always -status: {} + - name: identity-service + image: "volosoft/microservice-demo-identity-service" + env: + - name: ASPNETCORE_ENVIRONMENT + value: Development + - name: ASPNETCORE_URLS + value: http://0.0.0.0:80 + - name: AuthServer__Authority + 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: RabbitMQ__Connections__Default__HostName + value: rabbitmq + - name: Redis__Configuration + value: redis + ports: + - name: http + containerPort: 80 \ No newline at end of file diff --git a/samples/MicroserviceDemo/k8s/identity-service-service.yaml b/samples/MicroserviceDemo/k8s/identity-service-service.yaml index 8c601852a3..6157e27437 100644 --- a/samples/MicroserviceDemo/k8s/identity-service-service.yaml +++ b/samples/MicroserviceDemo/k8s/identity-service-service.yaml @@ -1,15 +1,11 @@ -apiVersion: v1 kind: Service +apiVersion: v1 metadata: - labels: - io.kompose.service: identity-service name: identity-service spec: - ports: - - name: "63568" - port: 63568 - targetPort: 80 selector: - io.kompose.service: identity-service -status: - loadBalancer: {} + app: identity-service + ports: + - protocol: TCP + port: 80 + targetPort: http \ No newline at end of file diff --git a/samples/MicroserviceDemo/k8s/internal-gateway-deployment.yaml b/samples/MicroserviceDemo/k8s/internal-gateway-deployment.yaml index 4abd77f1df..e748456a64 100644 --- a/samples/MicroserviceDemo/k8s/internal-gateway-deployment.yaml +++ b/samples/MicroserviceDemo/k8s/internal-gateway-deployment.yaml @@ -1,49 +1,45 @@ -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: - labels: - io.kompose.service: internal-gateway name: internal-gateway spec: + selector: + matchLabels: + app: internal-gateway replicas: 1 - strategy: {} template: metadata: labels: - io.kompose.service: internal-gateway + app: internal-gateway spec: containers: - - env: - - name: ASPNETCORE_ENVIRONMENT - value: Development - - name: ASPNETCORE_URLS - value: http://0.0.0.0:80 - - name: AuthServer__Authority - value: http://auth-server:64999 - - name: ConnectionStrings__Default - 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 - value: identity-service - - name: ReRoutes__0__DownstreamHostAndPorts__Port - value: "80" - - name: ReRoutes__1__DownstreamHostAndPorts__Host - value: product-service - - name: ReRoutes__1__DownstreamHostAndPorts__Port - value: "80" - - name: ReRoutes__2__DownstreamHostAndPorts__Host - value: blogging-service - - name: ReRoutes__2__DownstreamHostAndPorts__Port - value: "80" - - name: Redis__Configuration - value: redis - image: microservice-demo/internal-gateway:latest - name: internal-gateway - ports: - - containerPort: 80 - resources: {} - imagePullPolicy: Never - restartPolicy: Always -status: {} + - name: internal-gateway + image: "volosoft/microservice-demo-internal-gateway" + env: + - name: ASPNETCORE_ENVIRONMENT + value: Development + - name: ASPNETCORE_URLS + value: http://0.0.0.0:80 + - name: AuthServer__Authority + 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: GlobalConfiguration__BaseUrl + value: http://internal-gateway + - name: ReRoutes__0__DownstreamHostAndPorts__0__Host + value: identity-service + - name: ReRoutes__0__DownstreamHostAndPorts__0__Port + value: "80" + - name: ReRoutes__1__DownstreamHostAndPorts__0__Host + value: product-service + - name: ReRoutes__1__DownstreamHostAndPorts__0__Port + value: "80" + - name: ReRoutes__2__DownstreamHostAndPorts__0__Host + value: blogging-service + - name: ReRoutes__2__DownstreamHostAndPorts__0__Port + value: "80" + - name: Redis__Configuration + value: redis + ports: + - name: http + containerPort: 80 \ No newline at end of file diff --git a/samples/MicroserviceDemo/k8s/internal-gateway-service.yaml b/samples/MicroserviceDemo/k8s/internal-gateway-service.yaml index 5b392f5619..b0d80b34a1 100644 --- a/samples/MicroserviceDemo/k8s/internal-gateway-service.yaml +++ b/samples/MicroserviceDemo/k8s/internal-gateway-service.yaml @@ -1,15 +1,11 @@ -apiVersion: v1 kind: Service +apiVersion: v1 metadata: - labels: - io.kompose.service: internal-gateway name: internal-gateway spec: - ports: - - name: "65129" - port: 65129 - targetPort: 80 selector: - io.kompose.service: internal-gateway -status: - loadBalancer: {} + app: internal-gateway + ports: + - protocol: TCP + port: 80 + targetPort: http \ No newline at end of file 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..dbe3b9bde2 100644 --- a/samples/MicroserviceDemo/k8s/mongodb-deployment.yaml +++ b/samples/MicroserviceDemo/k8s/mongodb-deployment.yaml @@ -1,23 +1,19 @@ -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: - labels: - io.kompose.service: mongodb name: mongodb spec: + selector: + matchLabels: + app: mongodb replicas: 1 - strategy: {} template: metadata: labels: - io.kompose.service: mongodb + app: mongodb spec: containers: - - image: mongo - name: mongodb - ports: - - containerPort: 27017 - resources: {} - imagePullPolicy: IfNotPresent - restartPolicy: Always -status: {} + - name: mongodb + image: "mongo" + ports: + - containerPort: 27017 \ No newline at end of file diff --git a/samples/MicroserviceDemo/k8s/mongodb-service.yaml b/samples/MicroserviceDemo/k8s/mongodb-service.yaml index 59480974ff..0752ee87ca 100644 --- a/samples/MicroserviceDemo/k8s/mongodb-service.yaml +++ b/samples/MicroserviceDemo/k8s/mongodb-service.yaml @@ -1,15 +1,10 @@ -apiVersion: v1 kind: Service +apiVersion: v1 metadata: - labels: - io.kompose.service: mongodb name: mongodb spec: - ports: - - name: "27017" - port: 27017 - targetPort: 27017 selector: - io.kompose.service: mongodb -status: - loadBalancer: {} + app: mongodb + ports: + - port: 27017 + targetPort: 27017 \ No newline at end of file diff --git a/samples/MicroserviceDemo/k8s/product-service-deployment.yaml b/samples/MicroserviceDemo/k8s/product-service-deployment.yaml index 5418a8abc7..238c7ea45f 100644 --- a/samples/MicroserviceDemo/k8s/product-service-deployment.yaml +++ b/samples/MicroserviceDemo/k8s/product-service-deployment.yaml @@ -1,40 +1,36 @@ -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: - labels: - io.kompose.service: product-service name: product-service spec: + selector: + matchLabels: + app: product-service replicas: 1 - strategy: {} template: metadata: labels: - io.kompose.service: product-service + app: product-service spec: containers: - - env: - - name: ASPNETCORE_ENVIRONMENT - value: Development - - name: ASPNETCORE_URLS - value: http://0.0.0.0:80 - - name: AuthServer__Authority - value: http://auth-server:64999 - - 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 - - name: RabbitMQ__Connections__Default__HostName - value: rabbitmq - - name: Redis__Configuration - value: redis - image: microservice-demo/product-service:latest - name: product-service - ports: - - containerPort: 80 - resources: {} - imagePullPolicy: Never - restartPolicy: Always -status: {} + - name: product-service + image: "volosoft/microservice-demo-product-service" + env: + - name: ASPNETCORE_ENVIRONMENT + value: Development + - name: ASPNETCORE_URLS + value: http://0.0.0.0:80 + - name: AuthServer__Authority + 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_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 + ports: + - name: http + containerPort: 80 \ No newline at end of file diff --git a/samples/MicroserviceDemo/k8s/product-service-service.yaml b/samples/MicroserviceDemo/k8s/product-service-service.yaml index 25b52f9d7c..b420f35361 100644 --- a/samples/MicroserviceDemo/k8s/product-service-service.yaml +++ b/samples/MicroserviceDemo/k8s/product-service-service.yaml @@ -1,15 +1,11 @@ -apiVersion: v1 kind: Service +apiVersion: v1 metadata: - labels: - io.kompose.service: product-service name: product-service spec: - ports: - - name: "60244" - port: 60244 - targetPort: 80 selector: - io.kompose.service: product-service -status: - loadBalancer: {} + app: product-service + ports: + - protocol: TCP + port: 80 + targetPort: http \ No newline at end of file diff --git a/samples/MicroserviceDemo/k8s/public-website-deployment.yaml b/samples/MicroserviceDemo/k8s/public-website-deployment.yaml index 3c8955a99b..321ad14565 100644 --- a/samples/MicroserviceDemo/k8s/public-website-deployment.yaml +++ b/samples/MicroserviceDemo/k8s/public-website-deployment.yaml @@ -1,34 +1,31 @@ -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: - labels: - io.kompose.service: public-website name: public-website spec: + selector: + matchLabels: + app: public-website replicas: 1 - strategy: {} template: metadata: labels: - io.kompose.service: public-website + app: public-website spec: containers: - - env: - - name: ASPNETCORE_ENVIRONMENT - value: Development - - name: ASPNETCORE_URLS - value: http://0.0.0.0:80 - - name: AuthServer__Authority - value: http://auth-server:64999 - - name: Redis__Configuration - value: redis - - name: RemoteServices__Default__BaseUrl - value: http://public-website-gateway/ - image: microservice-demo/public-website:latest - name: public-website - ports: - - containerPort: 80 - resources: {} - imagePullPolicy: Never - restartPolicy: Always -status: {} + - name: public-website + image: "volosoft/microservice-demo-public-website" + env: + - name: ASPNETCORE_ENVIRONMENT + value: Development + - name: ASPNETCORE_URLS + value: http://0.0.0.0:80 + - name: AuthServer__Authority + value: http://auth-server:51511 + - name: Redis__Configuration + value: redis + - name: RemoteServices__Default__BaseUrl + value: http://public-website-gateway/ + ports: + - name: http + containerPort: 80 \ No newline at end of file diff --git a/samples/MicroserviceDemo/k8s/public-website-gateway-deployment.yaml b/samples/MicroserviceDemo/k8s/public-website-gateway-deployment.yaml index 9532d9334f..43804ea78a 100644 --- a/samples/MicroserviceDemo/k8s/public-website-gateway-deployment.yaml +++ b/samples/MicroserviceDemo/k8s/public-website-gateway-deployment.yaml @@ -1,45 +1,41 @@ -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: - labels: - io.kompose.service: public-website-gateway name: public-website-gateway spec: + selector: + matchLabels: + app: public-website-gateway replicas: 1 - strategy: {} template: metadata: labels: - io.kompose.service: public-website-gateway + app: public-website-gateway spec: containers: - - env: - - name: ASPNETCORE_ENVIRONMENT - value: Development - - name: ASPNETCORE_URLS - value: http://0.0.0.0:80 - - name: AuthServer__Authority - value: http://auth-server:64999 - - name: ConnectionStrings__Default - 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 - value: product-service - - name: ReRoutes__0__DownstreamHostAndPorts__Port - value: "80" - - name: ReRoutes__1__DownstreamHostAndPorts__Host - value: blogging-service - - name: ReRoutes__1__DownstreamHostAndPorts__Port - value: "80" - - name: Redis__Configuration - value: redis - image: microservice-demo/public-website-gateway:latest - name: public-website-gateway - ports: - - containerPort: 80 - resources: {} - imagePullPolicy: Never - restartPolicy: Always -status: {} + - name: public-website-gateway + image: "volosoft/microservice-demo-public-website-gateway" + env: + - name: ASPNETCORE_ENVIRONMENT + value: Development + - name: ASPNETCORE_URLS + value: http://0.0.0.0:80 + - name: AuthServer__Authority + 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: GlobalConfiguration__BaseUrl + value: http://public-website-gateway + - name: ReRoutes__0__DownstreamHostAndPorts__0__Host + value: product-service + - name: ReRoutes__0__DownstreamHostAndPorts__0__Port + value: "80" + - name: ReRoutes__1__DownstreamHostAndPorts__0__Host + value: blogging-service + - name: ReRoutes__1__DownstreamHostAndPorts__0__Port + value: "80" + - name: Redis__Configuration + value: redis + ports: + - name: http + containerPort: 80 \ No newline at end of file diff --git a/samples/MicroserviceDemo/k8s/public-website-gateway-service.yaml b/samples/MicroserviceDemo/k8s/public-website-gateway-service.yaml index 4c007686b7..d04c680685 100644 --- a/samples/MicroserviceDemo/k8s/public-website-gateway-service.yaml +++ b/samples/MicroserviceDemo/k8s/public-website-gateway-service.yaml @@ -1,15 +1,11 @@ -apiVersion: v1 kind: Service +apiVersion: v1 metadata: - labels: - io.kompose.service: public-website-gateway name: public-website-gateway spec: - ports: - - name: "64897" - port: 64897 - targetPort: 80 selector: - io.kompose.service: public-website-gateway -status: - loadBalancer: {} + app: public-website-gateway + ports: + - protocol: TCP + port: 80 + targetPort: http \ No newline at end of file diff --git a/samples/MicroserviceDemo/k8s/public-website-service.yaml b/samples/MicroserviceDemo/k8s/public-website-service.yaml index 77fac13ac3..5ac586cb19 100644 --- a/samples/MicroserviceDemo/k8s/public-website-service.yaml +++ b/samples/MicroserviceDemo/k8s/public-website-service.yaml @@ -1,15 +1,11 @@ -apiVersion: v1 kind: Service +apiVersion: v1 metadata: - labels: - io.kompose.service: public-website name: public-website spec: - ports: - - name: "3002" - port: 3002 - targetPort: 80 selector: - io.kompose.service: public-website -status: - loadBalancer: {} + app: public-website + ports: + - protocol: TCP + port: 80 + targetPort: http \ No newline at end of file diff --git a/samples/MicroserviceDemo/k8s/rabbitmq-deployment.yaml b/samples/MicroserviceDemo/k8s/rabbitmq-deployment.yaml index 5f32e398c1..283b1601a9 100644 --- a/samples/MicroserviceDemo/k8s/rabbitmq-deployment.yaml +++ b/samples/MicroserviceDemo/k8s/rabbitmq-deployment.yaml @@ -1,24 +1,20 @@ -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: - labels: - io.kompose.service: rabbitmq name: rabbitmq spec: + selector: + matchLabels: + app: rabbitmq replicas: 1 - strategy: {} template: metadata: labels: - io.kompose.service: rabbitmq + app: rabbitmq spec: containers: - - image: rabbitmq:3-management-alpine - name: rabbitmq - ports: - - containerPort: 5672 - - containerPort: 15672 - resources: {} - imagePullPolicy: IfNotPresent - restartPolicy: Always -status: {} + - name: rabbitmq + image: "rabbitmq:3-management-alpine" + ports: + - containerPort: 5672 + - containerPort: 15672 \ No newline at end of file diff --git a/samples/MicroserviceDemo/k8s/rabbitmq-service.yaml b/samples/MicroserviceDemo/k8s/rabbitmq-service.yaml index b8595193e8..07e7b2a618 100644 --- a/samples/MicroserviceDemo/k8s/rabbitmq-service.yaml +++ b/samples/MicroserviceDemo/k8s/rabbitmq-service.yaml @@ -1,18 +1,14 @@ -apiVersion: v1 kind: Service +apiVersion: v1 metadata: - labels: - io.kompose.service: rabbitmq name: rabbitmq spec: + selector: + app: rabbitmq ports: - name: "5672" port: 5672 targetPort: 5672 - name: "15672" port: 15672 - targetPort: 15672 - selector: - io.kompose.service: rabbitmq -status: - loadBalancer: {} + targetPort: 15672 \ No newline at end of file diff --git a/samples/MicroserviceDemo/k8s/redis-deployment.yaml b/samples/MicroserviceDemo/k8s/redis-deployment.yaml index b4389df699..5f0c6e50e4 100644 --- a/samples/MicroserviceDemo/k8s/redis-deployment.yaml +++ b/samples/MicroserviceDemo/k8s/redis-deployment.yaml @@ -1,23 +1,19 @@ -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: - labels: - io.kompose.service: redis name: redis spec: + selector: + matchLabels: + app: redis replicas: 1 - strategy: {} template: metadata: labels: - io.kompose.service: redis + app: redis spec: containers: - - image: redis - name: redis - ports: - - containerPort: 6379 - resources: {} - imagePullPolicy: IfNotPresent - restartPolicy: Always -status: {} + - name: redis + image: "redis" + ports: + - containerPort: 6379 \ No newline at end of file diff --git a/samples/MicroserviceDemo/k8s/redis-service.yaml b/samples/MicroserviceDemo/k8s/redis-service.yaml index 702eaab08c..8fef057052 100644 --- a/samples/MicroserviceDemo/k8s/redis-service.yaml +++ b/samples/MicroserviceDemo/k8s/redis-service.yaml @@ -1,15 +1,10 @@ -apiVersion: v1 kind: Service +apiVersion: v1 metadata: - labels: - io.kompose.service: redis name: redis spec: - ports: - - name: "6379" - port: 6379 - targetPort: 6379 selector: - io.kompose.service: redis -status: - loadBalancer: {} + app: redis + ports: + - port: 6379 + targetPort: 6379 \ No newline at end of file diff --git a/samples/MicroserviceDemo/k8s/restore-database-job.yaml b/samples/MicroserviceDemo/k8s/restore-database-job.yaml new file mode 100644 index 0000000000..fa403e95d8 --- /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..e95ce99ee7 100644 --- a/samples/MicroserviceDemo/k8s/sqlserver-deployment.yaml +++ b/samples/MicroserviceDemo/k8s/sqlserver-deployment.yaml @@ -1,36 +1,35 @@ -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: - labels: - io.kompose.service: sqlserver name: sqlserver spec: + selector: + matchLabels: + app: sqlserver replicas: 1 - strategy: - type: Recreate template: metadata: labels: - io.kompose.service: sqlserver + app: sqlserver spec: containers: - - env: + - name: sqlserver + image: mcr.microsoft.com/mssql/server + ports: + - containerPort: 1433 + env: + - name: MSSQL_PID + value: "Developer" - name: ACCEPT_EULA value: "Y" - name: SA_PASSWORD value: yourStrong(!)Password - image: mcr.microsoft.com/mssql/server - name: sqlserver - ports: - - containerPort: 1433 - resources: {} - imagePullPolicy: IfNotPresent + - name: MSSQL_SA_PASSWORD + value: yourStrong(!)Password volumeMounts: - - mountPath: /var/opt/mssql - name: dbdata - restartPolicy: Always + - name: dbdata + mountPath: /var/opt/mssql volumes: - name: dbdata persistentVolumeClaim: - claimName: dbdata -status: {} + claimName: dbdata \ No newline at end of file diff --git a/samples/MicroserviceDemo/k8s/sqlserver-service.yaml b/samples/MicroserviceDemo/k8s/sqlserver-service.yaml index 57e77a1b42..a72adb2567 100644 --- a/samples/MicroserviceDemo/k8s/sqlserver-service.yaml +++ b/samples/MicroserviceDemo/k8s/sqlserver-service.yaml @@ -1,15 +1,11 @@ -apiVersion: v1 kind: Service +apiVersion: v1 metadata: - labels: - io.kompose.service: sqlserver name: sqlserver spec: - ports: - - name: "1433" - port: 1433 - targetPort: 1433 selector: - io.kompose.service: sqlserver -status: - loadBalancer: {} + app: sqlserver + ports: + - protocol: TCP + port: 1433 + targetPort: 1433 \ No newline at end of file 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/app/MyCompanyName.MyProjectName.DemoApp/Migrations/20190107113413_Initial.Designer.cs b/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Migrations/20190320125012_Initial.Designer.cs similarity index 99% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/Migrations/20190107113413_Initial.Designer.cs rename to templates/module/app/MyCompanyName.MyProjectName.DemoApp/Migrations/20190320125012_Initial.Designer.cs index 8d0c81b5f8..91a4c096ff 100644 --- a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Migrations/20190107113413_Initial.Designer.cs +++ b/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Migrations/20190320125012_Initial.Designer.cs @@ -10,7 +10,7 @@ using MyCompanyName.MyProjectName.DemoApp; namespace MyCompanyName.MyProjectName.DemoApp.Migrations { [DbContext(typeof(DemoAppDbContext))] - [Migration("20190107113413_Initial")] + [Migration("20190320125012_Initial")] partial class Initial { protected override void BuildTargetModel(ModelBuilder modelBuilder) diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Migrations/20190107113413_Initial.cs b/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Migrations/20190320125012_Initial.cs similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/Migrations/20190107113413_Initial.cs rename to templates/module/app/MyCompanyName.MyProjectName.DemoApp/Migrations/20190320125012_Initial.cs diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/MyCompanyName.MyProjectName.DemoApp.csproj b/templates/module/app/MyCompanyName.MyProjectName.DemoApp/MyCompanyName.MyProjectName.DemoApp.csproj index 4e019c6d9b..041d6cf9ce 100644 --- a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/MyCompanyName.MyProjectName.DemoApp.csproj +++ b/templates/module/app/MyCompanyName.MyProjectName.DemoApp/MyCompanyName.MyProjectName.DemoApp.csproj @@ -35,4 +35,8 @@ + + + + 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..d939cfca9f --- /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 DbTablePrefix = "App"; + + public const string DbSchema = null; + } +} diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDomainModule.cs b/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDomainModule.cs index f32225025f..5fca840175 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDomainModule.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDomainModule.cs @@ -1,5 +1,4 @@ using MyCompanyName.MyProjectName.Localization.MyProjectName; -using MyCompanyName.MyProjectName.Settings; using Volo.Abp.Auditing; using Volo.Abp.AuditLogging; using Volo.Abp.BackgroundJobs; @@ -8,7 +7,6 @@ using Volo.Abp.Localization; using Volo.Abp.Localization.Resources.AbpValidation; using Volo.Abp.Modularity; using Volo.Abp.PermissionManagement.Identity; -using Volo.Abp.Settings; using Volo.Abp.VirtualFileSystem; namespace MyCompanyName.MyProjectName @@ -36,11 +34,6 @@ namespace MyCompanyName.MyProjectName .AddBaseTypes(typeof(AbpValidationResource)) .AddVirtualJson("/Localization/MyProjectName"); }); - - Configure(options => - { - options.DefinitionProviders.Add(); - }); } } } diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Domain/Users/AppUser.cs b/templates/mvc/src/MyCompanyName.MyProjectName.Domain/Users/AppUser.cs new file mode 100644 index 0000000000..587c9d9440 --- /dev/null +++ b/templates/mvc/src/MyCompanyName.MyProjectName.Domain/Users/AppUser.cs @@ -0,0 +1,53 @@ +using System; +using Volo.Abp.Domain.Entities.Auditing; +using Volo.Abp.Users; + +namespace MyCompanyName.MyProjectName.Users +{ + /* This entity shares the same table/collection ("AbpUsers" by default) with the + * IdentityUser entity of the Identity module. + * + * - You can define your custom properties into this class. + * - You never create or delete this entity, becase it is Identity module's job. + * - You can query users from database with this entity. + * - You can update values of your custom properties. + */ + public class AppUser : FullAuditedAggregateRoot, IUser + { + #region Base properties + + /* These properties are shared with the IdentityUser entity of the Identity module. + * Do not change these properties through this class. Instead, use Identity module + * services (like IdentityUserManager) to change them. + * So, this properties are designed as read only! + */ + + public virtual Guid? TenantId { get; private set; } + + public virtual string UserName { get; private set; } + + public virtual string Name { get; private set; } + + public virtual string Surname { get; private set; } + + public virtual string Email { get; private set; } + + public virtual bool EmailConfirmed { get; private set; } + + public virtual string PhoneNumber { get; private set; } + + public virtual bool PhoneNumberConfirmed { get; private set; } + + #endregion + + /* Add your own properties here. Example: + * + * public virtual string MyProperty { get; set; } + */ + + private AppUser() + { + + } + } +} 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..5fe35526b0 --- /dev/null +++ b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/MyProjectNameMigrationsDbContext.cs @@ -0,0 +1,44 @@ +using Microsoft.EntityFrameworkCore; +using Volo.Abp.AuditLogging.EntityFrameworkCore; +using Volo.Abp.BackgroundJobs.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Identity; +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); + + /* Include modules to your migration db context */ + + builder.ConfigurePermissionManagement(); + builder.ConfigureSettingManagement(); + builder.ConfigureBackgroundJobs(); + builder.ConfigureAuditLogging(); + builder.ConfigureIdentity(); + + /* Configure customizations for entities from the modules included */ + + builder.Entity(b => + { + b.ConfigureCustomUserProperties(); + }); + + /* Configure your own tables/entities inside the ConfigureMyProjectName method */ + + builder.ConfigureMyProjectName(); + } + } +} \ No newline at end of file diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/EntityFrameworkCore/MyProjectNameDbContextFactory.cs b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/MyProjectNameMigrationsDbContextFactory.cs similarity index 64% rename from templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/EntityFrameworkCore/MyProjectNameDbContextFactory.cs rename to templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/MyProjectNameMigrationsDbContextFactory.cs index c878f12fd5..d760790fc8 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/EntityFrameworkCore/MyProjectNameDbContextFactory.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/MyProjectNameMigrationsDbContextFactory.cs @@ -5,22 +5,22 @@ using Microsoft.Extensions.Configuration; namespace MyCompanyName.MyProjectName.EntityFrameworkCore { - public class MyProjectNameDbContextFactory : IDesignTimeDbContextFactory + public class MyProjectNameMigrationsDbContextFactory : IDesignTimeDbContextFactory { - public MyProjectNameDbContext CreateDbContext(string[] args) + public MyProjectNameMigrationsDbContext CreateDbContext(string[] args) { var configuration = BuildConfiguration(); - var builder = new DbContextOptionsBuilder() + var builder = new DbContextOptionsBuilder() .UseSqlServer(configuration.GetConnectionString("Default")); - return new MyProjectNameDbContext(builder.Options); + return new MyProjectNameMigrationsDbContext(builder.Options); } private static IConfigurationRoot BuildConfiguration() { var builder = new ConfigurationBuilder() - .SetBasePath(Path.Combine(Directory.GetCurrentDirectory(), "../MyCompanyName.MyProjectName.Web/")) + .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..b96691dcc7 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/EntityFrameworkCore/MyProjectNameDbContext.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/EntityFrameworkCore/MyProjectNameDbContext.cs @@ -1,32 +1,45 @@ using Microsoft.EntityFrameworkCore; -using Volo.Abp.AuditLogging.EntityFrameworkCore; -using Volo.Abp.BackgroundJobs.EntityFrameworkCore; +using MyCompanyName.MyProjectName.Users; using Volo.Abp.Data; using Volo.Abp.EntityFrameworkCore; -using Volo.Abp.Identity.EntityFrameworkCore; -using Volo.Abp.PermissionManagement.EntityFrameworkCore; -using Volo.Abp.SettingManagement.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.Modeling; +using Volo.Abp.Users.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.EntityFrameworkCore { [ConnectionStringName("Default")] public class MyProjectNameDbContext : AbpDbContext { + public DbSet Users { get; set; } + public MyProjectNameDbContext(DbContextOptions options) : base(options) { } - protected override void OnModelCreating(ModelBuilder modelBuilder) + protected override void OnModelCreating(ModelBuilder builder) { - base.OnModelCreating(modelBuilder); + base.OnModelCreating(builder); + + /* Configure the shared tables (with included modules) here */ + + builder.Entity(b => + { + b.ToTable("AbpUsers"); //Sharing the same table "AbpUsers" with the IdentityUser + + b.ConfigureFullAudited(); + b.ConfigureExtraProperties(); + b.ConfigureConcurrencyStamp(); + b.ConfigureAbpUser(); + + //Moved customization to a method so we can share it with the MyProjectNameMigrationsDbContext class + b.ConfigureCustomUserProperties(); + }); + + /* Configure your own tables/entities inside the ConfigureMyProjectName method */ - 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..3528ed5279 --- /dev/null +++ b/templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/EntityFrameworkCore/MyProjectNameDbContextModelCreatingExtensions.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Volo.Abp; +using Volo.Abp.Users; + +namespace MyCompanyName.MyProjectName.EntityFrameworkCore +{ + public static class MyProjectNameDbContextModelCreatingExtensions + { + public static void ConfigureMyProjectName(this ModelBuilder builder) + { + Check.NotNull(builder, nameof(builder)); + + /* Configure your own tables/entities inside here */ + + //builder.Entity(b => + //{ + // b.ToTable(MyProjectNameConsts.DbTablePrefix + "YourEntities", MyProjectNameConsts.DbSchema); + + // //... + //}); + } + + public static void ConfigureCustomUserProperties(this EntityTypeBuilder b) + where TUser: class, IUser + { + //b.Property(nameof(AppUser.MyProperty))... + } + } +} \ 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/MongoDb/MyProjectNameMongoDbContext.cs b/templates/mvc/src/MyCompanyName.MyProjectName.MongoDB/MongoDb/MyProjectNameMongoDbContext.cs index 19c21ca038..95a041809f 100644 --- a/templates/mvc/src/MyCompanyName.MyProjectName.MongoDB/MongoDb/MyProjectNameMongoDbContext.cs +++ b/templates/mvc/src/MyCompanyName.MyProjectName.MongoDB/MongoDb/MyProjectNameMongoDbContext.cs @@ -1,4 +1,6 @@ -using Volo.Abp.Data; +using MongoDB.Driver; +using MyCompanyName.MyProjectName.Users; +using Volo.Abp.Data; using Volo.Abp.MongoDB; namespace MyCompanyName.MyProjectName.MongoDb @@ -6,6 +8,16 @@ namespace MyCompanyName.MyProjectName.MongoDb [ConnectionStringName("Default")] public class MyProjectNameMongoDbContext : AbpMongoDbContext { - + public IMongoCollection Users => Collection(); + + protected override void CreateModel(IMongoModelBuilder modelBuilder) + { + base.CreateModel(modelBuilder); + + modelBuilder.Entity(b => + { + b.CollectionName = "AbpUsers"; //Sharing the same collection "AbpUsers" with the IdentityUser + }); + } } } 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..808ee9f1fe 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 @@ -7,9 +9,9 @@ - + diff --git a/templates/mvc/test/MyCompanyName.MyProjectName.Application.Tests/MyProjectNameApplicationTestModule.cs b/templates/mvc/test/MyCompanyName.MyProjectName.Application.Tests/MyProjectNameApplicationTestModule.cs index 145710c15b..d44dc0bb5a 100644 --- a/templates/mvc/test/MyCompanyName.MyProjectName.Application.Tests/MyProjectNameApplicationTestModule.cs +++ b/templates/mvc/test/MyCompanyName.MyProjectName.Application.Tests/MyProjectNameApplicationTestModule.cs @@ -53,11 +53,11 @@ namespace MyCompanyName.MyProjectName var connection = new SqliteConnection("Data Source=:memory:"); connection.Open(); - var options = new DbContextOptionsBuilder() + var options = new DbContextOptionsBuilder() .UseSqlite(connection) .Options; - using (var context = new MyProjectNameDbContext(options)) + using (var context = new MyProjectNameMigrationsDbContext(options)) { context.GetService().CreateTables(); } diff --git a/templates/mvc/test/MyCompanyName.MyProjectName.Application.Tests/Samples/SampleTest.cs b/templates/mvc/test/MyCompanyName.MyProjectName.Application.Tests/Samples/SampleTest.cs deleted file mode 100644 index e0f87e86c8..0000000000 --- a/templates/mvc/test/MyCompanyName.MyProjectName.Application.Tests/Samples/SampleTest.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using Shouldly; -using Volo.Abp.Identity; -using Xunit; - -namespace MyCompanyName.MyProjectName.Samples -{ - public class SampleTest : MyProjectNameApplicationTestBase - { - private readonly IIdentityUserAppService _userAppService; - - public SampleTest() - { - _userAppService = ServiceProvider.GetRequiredService(); - } - - [Fact] - public async Task Initial_Data_Should_Contain_Admin_User() - { - var result = await _userAppService.GetListAsync(new GetIdentityUsersInput()); - result.TotalCount.ShouldBeGreaterThan(0); - result.Items.ShouldContain(u => u.UserName == "admin"); - } - } -} diff --git a/templates/mvc/test/MyCompanyName.MyProjectName.Application.Tests/Samples/SampleTests.cs b/templates/mvc/test/MyCompanyName.MyProjectName.Application.Tests/Samples/SampleTests.cs new file mode 100644 index 0000000000..3baff2826f --- /dev/null +++ b/templates/mvc/test/MyCompanyName.MyProjectName.Application.Tests/Samples/SampleTests.cs @@ -0,0 +1,59 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using MyCompanyName.MyProjectName.Users; +using Shouldly; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Identity; +using Volo.Abp.Uow; +using Xunit; + +namespace MyCompanyName.MyProjectName.Samples +{ + public class SampleTests : MyProjectNameApplicationTestBase + { + private readonly IIdentityUserAppService _userAppService; + private readonly IRepository _appUserRepository; + private readonly IUnitOfWorkManager _unitOfWorkManager; + + public SampleTests() + { + _userAppService = ServiceProvider.GetRequiredService(); + _appUserRepository = ServiceProvider.GetRequiredService>(); + _unitOfWorkManager = ServiceProvider.GetRequiredService(); + } + + [Fact] + public async Task Initial_Data_Should_Contain_Admin_User() + { + //Act + var result = await _userAppService.GetListAsync(new GetIdentityUsersInput()); + + //Assert + result.TotalCount.ShouldBeGreaterThan(0); + result.Items.ShouldContain(u => u.UserName == "admin"); + } + + [Fact] + public async Task Should_Query_AppUser() + { + /* Need to manually start Unit Of Work because + * FirstOrDefaultAsync should be executed while db connection / context is available. + */ + using (var uow = _unitOfWorkManager.Begin()) + { + //Act + var adminUser = await _appUserRepository + .Where(u => u.UserName == "admin") + .FirstOrDefaultAsync(); + + //Assert + adminUser.ShouldNotBeNull(); + + await uow.CompleteAsync(); + } + } + } +} 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/host/IdentityServerHost/DemoAppDbContext.cs b/templates/service/host/IdentityServerHost/DemoAppDbContext.cs index 754cd89dff..5d418a19e0 100644 --- a/templates/service/host/IdentityServerHost/DemoAppDbContext.cs +++ b/templates/service/host/IdentityServerHost/DemoAppDbContext.cs @@ -1,7 +1,10 @@ using Microsoft.EntityFrameworkCore; +using Volo.Abp.AuditLogging.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore; using Volo.Abp.Identity.EntityFrameworkCore; using Volo.Abp.IdentityServer.EntityFrameworkCore; +using Volo.Abp.PermissionManagement.EntityFrameworkCore; +using Volo.Abp.SettingManagement.EntityFrameworkCore; namespace IdentityServerHost { @@ -17,6 +20,9 @@ namespace IdentityServerHost { base.OnModelCreating(modelBuilder); + modelBuilder.ConfigurePermissionManagement(); + modelBuilder.ConfigureSettingManagement(); + modelBuilder.ConfigureAuditLogging(); modelBuilder.ConfigureIdentity(); modelBuilder.ConfigureIdentityServer(); } diff --git a/templates/service/host/IdentityServerHost/IdentityServerHost.csproj b/templates/service/host/IdentityServerHost/IdentityServerHost.csproj index c5b8c3826d..7e72681de0 100644 --- a/templates/service/host/IdentityServerHost/IdentityServerHost.csproj +++ b/templates/service/host/IdentityServerHost/IdentityServerHost.csproj @@ -17,6 +17,9 @@ + + + @@ -26,9 +29,4 @@ - - - - - diff --git a/templates/service/host/IdentityServerHost/Migrations/20190107114341_Initial.Designer.cs b/templates/service/host/IdentityServerHost/Migrations/20190320142333_Initial.Designer.cs similarity index 75% rename from templates/service/host/IdentityServerHost/Migrations/20190107114341_Initial.Designer.cs rename to templates/service/host/IdentityServerHost/Migrations/20190320142333_Initial.Designer.cs index 1dd595dfd1..8ef18c7e12 100644 --- a/templates/service/host/IdentityServerHost/Migrations/20190107114341_Initial.Designer.cs +++ b/templates/service/host/IdentityServerHost/Migrations/20190320142333_Initial.Designer.cs @@ -10,7 +10,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace IdentityServerHost.Migrations { [DbContext(typeof(DemoAppDbContext))] - [Migration("20190107114341_Initial")] + [Migration("20190320142333_Initial")] partial class Initial { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -21,6 +21,202 @@ namespace IdentityServerHost.Migrations .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ApplicationName") + .HasColumnName("ApplicationName") + .HasMaxLength(96); + + b.Property("BrowserInfo") + .HasColumnName("BrowserInfo") + .HasMaxLength(512); + + b.Property("ClientId") + .HasColumnName("ClientId") + .HasMaxLength(64); + + b.Property("ClientIpAddress") + .HasColumnName("ClientIpAddress") + .HasMaxLength(64); + + b.Property("ClientName") + .HasColumnName("ClientName") + .HasMaxLength(128); + + b.Property("Comments") + .HasColumnName("Comments") + .HasMaxLength(256); + + b.Property("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasColumnName("CorrelationId") + .HasMaxLength(64); + + b.Property("Exceptions") + .HasColumnName("Exceptions") + .HasMaxLength(4000); + + b.Property("ExecutionDuration") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("HttpMethod") + .HasColumnName("HttpMethod") + .HasMaxLength(16); + + b.Property("HttpStatusCode") + .HasColumnName("HttpStatusCode"); + + b.Property("ImpersonatorTenantId") + .HasColumnName("ImpersonatorTenantId"); + + b.Property("ImpersonatorUserId") + .HasColumnName("ImpersonatorUserId"); + + b.Property("TenantId") + .HasColumnName("TenantId"); + + b.Property("Url") + .HasColumnName("Url") + .HasMaxLength(256); + + b.Property("UserId") + .HasColumnName("UserId"); + + b.Property("UserName") + .HasColumnName("UserName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ExecutionTime"); + + b.HasIndex("TenantId", "UserId", "ExecutionTime"); + + b.ToTable("AbpAuditLogs"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditLogId") + .HasColumnName("AuditLogId"); + + b.Property("ExecutionDuration") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnName("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("MethodName") + .HasColumnName("MethodName") + .HasMaxLength(128); + + b.Property("Parameters") + .HasColumnName("Parameters") + .HasMaxLength(2000); + + b.Property("ServiceName") + .HasColumnName("ServiceName") + .HasMaxLength(256); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "ServiceName", "MethodName", "ExecutionTime"); + + b.ToTable("AbpAuditLogActions"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditLogId") + .HasColumnName("AuditLogId"); + + b.Property("ChangeTime") + .HasColumnName("ChangeTime"); + + b.Property("ChangeType") + .HasColumnName("ChangeType"); + + b.Property("EntityId") + .IsRequired() + .HasColumnName("EntityId") + .HasMaxLength(128); + + b.Property("EntityTypeFullName") + .IsRequired() + .HasColumnName("EntityTypeFullName") + .HasMaxLength(128); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("TenantId") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "EntityTypeFullName", "EntityId"); + + b.ToTable("AbpEntityChanges"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EntityChangeId"); + + b.Property("NewValue") + .HasColumnName("NewValue") + .HasMaxLength(512); + + b.Property("OriginalValue") + .HasColumnName("OriginalValue") + .HasMaxLength(512); + + b.Property("PropertyName") + .IsRequired() + .HasColumnName("PropertyName") + .HasMaxLength(128); + + b.Property("PropertyTypeFullName") + .IsRequired() + .HasColumnName("PropertyTypeFullName") + .HasMaxLength(64); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityChangeId"); + + b.ToTable("AbpEntityPropertyChanges"); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => { b.Property("Id") @@ -445,7 +641,7 @@ namespace IdentityServerHost.Migrations b.Property("BackChannelLogoutSessionRequired"); b.Property("BackChannelLogoutUri") - .HasMaxLength(2000); + .HasMaxLength(300); b.Property("ClientClaimsPrefix") .HasMaxLength(200); @@ -458,7 +654,7 @@ namespace IdentityServerHost.Migrations .HasMaxLength(200); b.Property("ClientUri") - .HasMaxLength(2000); + .HasMaxLength(300); b.Property("ConcurrencyStamp"); @@ -477,14 +673,14 @@ namespace IdentityServerHost.Migrations b.Property("FrontChannelLogoutSessionRequired"); b.Property("FrontChannelLogoutUri") - .HasMaxLength(2000); + .HasMaxLength(300); b.Property("IdentityTokenLifetime"); b.Property("IncludeJwtId"); b.Property("LogoUri") - .HasMaxLength(2000); + .HasMaxLength(300); b.Property("PairWiseSubjectSalt") .HasMaxLength(200); @@ -517,22 +713,15 @@ namespace IdentityServerHost.Migrations modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientClaim", b => { - b.Property("Id") - .ValueGeneratedOnAdd(); - b.Property("ClientId"); b.Property("Type") - .IsRequired() .HasMaxLength(250); b.Property("Value") - .IsRequired() .HasMaxLength(250); - b.HasKey("Id"); - - b.HasIndex("ClientId"); + b.HasKey("ClientId", "Type", "Value"); b.ToTable("IdentityServerClientClaims"); }); @@ -578,7 +767,7 @@ namespace IdentityServerHost.Migrations b.Property("ClientId"); b.Property("PostLogoutRedirectUri") - .HasMaxLength(2000); + .HasMaxLength(200); b.HasKey("ClientId", "PostLogoutRedirectUri"); @@ -590,11 +779,11 @@ namespace IdentityServerHost.Migrations b.Property("ClientId"); b.Property("Key") - .HasMaxLength(250); + .HasMaxLength(64); b.Property("Value") .IsRequired() - .HasMaxLength(2000); + .HasMaxLength(128); b.HasKey("ClientId", "Key"); @@ -606,7 +795,7 @@ namespace IdentityServerHost.Migrations b.Property("ClientId"); b.Property("RedirectUri") - .HasMaxLength(2000); + .HasMaxLength(200); b.HasKey("ClientId", "RedirectUri"); @@ -727,6 +916,82 @@ namespace IdentityServerHost.Migrations b.ToTable("IdentityServerIdentityResources"); }); + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpPermissionGrants"); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasMaxLength(64); + + b.Property("ProviderName") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2048); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpSettings"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog") + .WithMany("Actions") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog") + .WithMany("EntityChanges") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.EntityChange") + .WithMany("PropertyChanges") + .HasForeignKey("EntityChangeId") + .OnDelete(DeleteBehavior.Cascade); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => { b.HasOne("Volo.Abp.Identity.IdentityRole") diff --git a/templates/service/host/IdentityServerHost/Migrations/20190107114341_Initial.cs b/templates/service/host/IdentityServerHost/Migrations/20190320142333_Initial.cs similarity index 77% rename from templates/service/host/IdentityServerHost/Migrations/20190107114341_Initial.cs rename to templates/service/host/IdentityServerHost/Migrations/20190320142333_Initial.cs index ddb2f14c1c..88588fb970 100644 --- a/templates/service/host/IdentityServerHost/Migrations/20190107114341_Initial.cs +++ b/templates/service/host/IdentityServerHost/Migrations/20190320142333_Initial.cs @@ -7,6 +7,37 @@ namespace IdentityServerHost.Migrations { protected override void Up(MigrationBuilder migrationBuilder) { + migrationBuilder.CreateTable( + name: "AbpAuditLogs", + columns: table => new + { + Id = table.Column(nullable: false), + ExtraProperties = table.Column(nullable: true), + ConcurrencyStamp = table.Column(nullable: true), + ApplicationName = table.Column(maxLength: 96, nullable: true), + UserId = table.Column(nullable: true), + UserName = table.Column(maxLength: 256, nullable: true), + TenantId = table.Column(nullable: true), + ImpersonatorUserId = table.Column(nullable: true), + ImpersonatorTenantId = table.Column(nullable: true), + ExecutionTime = table.Column(nullable: false), + ExecutionDuration = table.Column(nullable: false), + ClientIpAddress = table.Column(maxLength: 64, nullable: true), + ClientName = table.Column(maxLength: 128, nullable: true), + ClientId = table.Column(maxLength: 64, nullable: true), + CorrelationId = table.Column(maxLength: 64, nullable: true), + BrowserInfo = table.Column(maxLength: 512, nullable: true), + HttpMethod = table.Column(maxLength: 16, nullable: true), + Url = table.Column(maxLength: 256, nullable: true), + Exceptions = table.Column(maxLength: 4000, nullable: true), + Comments = table.Column(maxLength: 256, nullable: true), + HttpStatusCode = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpAuditLogs", x => x.Id); + }); + migrationBuilder.CreateTable( name: "AbpClaimTypes", columns: table => new @@ -27,6 +58,21 @@ namespace IdentityServerHost.Migrations table.PrimaryKey("PK_AbpClaimTypes", x => x.Id); }); + migrationBuilder.CreateTable( + name: "AbpPermissionGrants", + columns: table => new + { + Id = table.Column(nullable: false), + TenantId = table.Column(nullable: true), + Name = table.Column(maxLength: 128, nullable: false), + ProviderName = table.Column(maxLength: 64, nullable: false), + ProviderKey = table.Column(maxLength: 64, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpPermissionGrants", x => x.Id); + }); + migrationBuilder.CreateTable( name: "AbpRoles", columns: table => new @@ -46,6 +92,21 @@ namespace IdentityServerHost.Migrations table.PrimaryKey("PK_AbpRoles", x => x.Id); }); + migrationBuilder.CreateTable( + name: "AbpSettings", + columns: table => new + { + Id = table.Column(nullable: false), + Name = table.Column(maxLength: 128, nullable: false), + Value = table.Column(maxLength: 2048, nullable: false), + ProviderName = table.Column(maxLength: 64, nullable: true), + ProviderKey = table.Column(maxLength: 64, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpSettings", x => x.Id); + }); + migrationBuilder.CreateTable( name: "AbpUsers", columns: table => new @@ -109,8 +170,8 @@ namespace IdentityServerHost.Migrations ClientId = table.Column(maxLength: 200, nullable: false), ClientName = table.Column(maxLength: 200, nullable: true), Description = table.Column(maxLength: 1000, nullable: true), - ClientUri = table.Column(maxLength: 2000, nullable: true), - LogoUri = table.Column(maxLength: 2000, nullable: true), + ClientUri = table.Column(maxLength: 300, nullable: true), + LogoUri = table.Column(maxLength: 300, nullable: true), Enabled = table.Column(nullable: false), ProtocolType = table.Column(maxLength: 200, nullable: false), RequireClientSecret = table.Column(nullable: false), @@ -120,9 +181,9 @@ namespace IdentityServerHost.Migrations RequirePkce = table.Column(nullable: false), AllowPlainTextPkce = table.Column(nullable: false), AllowAccessTokensViaBrowser = table.Column(nullable: false), - FrontChannelLogoutUri = table.Column(maxLength: 2000, nullable: true), + FrontChannelLogoutUri = table.Column(maxLength: 300, nullable: true), FrontChannelLogoutSessionRequired = table.Column(nullable: false), - BackChannelLogoutUri = table.Column(maxLength: 2000, nullable: true), + BackChannelLogoutUri = table.Column(maxLength: 300, nullable: true), BackChannelLogoutSessionRequired = table.Column(nullable: false), AllowOfflineAccess = table.Column(nullable: false), IdentityTokenLifetime = table.Column(nullable: false), @@ -186,6 +247,55 @@ namespace IdentityServerHost.Migrations table.PrimaryKey("PK_IdentityServerPersistedGrants", x => x.Key); }); + migrationBuilder.CreateTable( + name: "AbpAuditLogActions", + columns: table => new + { + Id = table.Column(nullable: false), + TenantId = table.Column(nullable: true), + AuditLogId = table.Column(nullable: false), + ServiceName = table.Column(maxLength: 256, nullable: true), + MethodName = table.Column(maxLength: 128, nullable: true), + Parameters = table.Column(maxLength: 2000, nullable: true), + ExecutionTime = table.Column(nullable: false), + ExecutionDuration = table.Column(nullable: false), + ExtraProperties = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpAuditLogActions", x => x.Id); + table.ForeignKey( + name: "FK_AbpAuditLogActions_AbpAuditLogs_AuditLogId", + column: x => x.AuditLogId, + principalTable: "AbpAuditLogs", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpEntityChanges", + columns: table => new + { + Id = table.Column(nullable: false), + AuditLogId = table.Column(nullable: false), + TenantId = table.Column(nullable: true), + ChangeTime = table.Column(nullable: false), + ChangeType = table.Column(nullable: false), + EntityId = table.Column(maxLength: 128, nullable: false), + EntityTypeFullName = table.Column(maxLength: 128, nullable: false), + ExtraProperties = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpEntityChanges", x => x.Id); + table.ForeignKey( + name: "FK_AbpEntityChanges_AbpAuditLogs_AuditLogId", + column: x => x.AuditLogId, + principalTable: "AbpAuditLogs", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "AbpRoleClaims", columns: table => new @@ -361,14 +471,13 @@ namespace IdentityServerHost.Migrations name: "IdentityServerClientClaims", columns: table => new { - Id = table.Column(nullable: false), ClientId = table.Column(nullable: false), Type = table.Column(maxLength: 250, nullable: false), Value = table.Column(maxLength: 250, nullable: false) }, constraints: table => { - table.PrimaryKey("PK_IdentityServerClientClaims", x => x.Id); + table.PrimaryKey("PK_IdentityServerClientClaims", x => new { x.ClientId, x.Type, x.Value }); table.ForeignKey( name: "FK_IdentityServerClientClaims_IdentityServerClients_ClientId", column: x => x.ClientId, @@ -436,7 +545,7 @@ namespace IdentityServerHost.Migrations columns: table => new { ClientId = table.Column(nullable: false), - PostLogoutRedirectUri = table.Column(maxLength: 2000, nullable: false) + PostLogoutRedirectUri = table.Column(maxLength: 200, nullable: false) }, constraints: table => { @@ -454,8 +563,8 @@ namespace IdentityServerHost.Migrations columns: table => new { ClientId = table.Column(nullable: false), - Key = table.Column(maxLength: 250, nullable: false), - Value = table.Column(maxLength: 2000, nullable: false) + Key = table.Column(maxLength: 64, nullable: false), + Value = table.Column(maxLength: 128, nullable: false) }, constraints: table => { @@ -473,7 +582,7 @@ namespace IdentityServerHost.Migrations columns: table => new { ClientId = table.Column(nullable: false), - RedirectUri = table.Column(maxLength: 2000, nullable: false) + RedirectUri = table.Column(maxLength: 200, nullable: false) }, constraints: table => { @@ -543,6 +652,29 @@ namespace IdentityServerHost.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "AbpEntityPropertyChanges", + columns: table => new + { + Id = table.Column(nullable: false), + TenantId = table.Column(nullable: true), + EntityChangeId = table.Column(nullable: false), + NewValue = table.Column(maxLength: 512, nullable: true), + OriginalValue = table.Column(maxLength: 512, nullable: true), + PropertyName = table.Column(maxLength: 128, nullable: false), + PropertyTypeFullName = table.Column(maxLength: 64, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpEntityPropertyChanges", x => x.Id); + table.ForeignKey( + name: "FK_AbpEntityPropertyChanges_AbpEntityChanges_EntityChangeId", + column: x => x.EntityChangeId, + principalTable: "AbpEntityChanges", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "IdentityServerApiScopeClaims", columns: table => new @@ -562,6 +694,46 @@ namespace IdentityServerHost.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateIndex( + name: "IX_AbpAuditLogActions_AuditLogId", + table: "AbpAuditLogActions", + column: "AuditLogId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpAuditLogActions_TenantId_ServiceName_MethodName_ExecutionTime", + table: "AbpAuditLogActions", + columns: new[] { "TenantId", "ServiceName", "MethodName", "ExecutionTime" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpAuditLogs_TenantId_ExecutionTime", + table: "AbpAuditLogs", + columns: new[] { "TenantId", "ExecutionTime" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpAuditLogs_TenantId_UserId_ExecutionTime", + table: "AbpAuditLogs", + columns: new[] { "TenantId", "UserId", "ExecutionTime" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpEntityChanges_AuditLogId", + table: "AbpEntityChanges", + column: "AuditLogId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpEntityChanges_TenantId_EntityTypeFullName_EntityId", + table: "AbpEntityChanges", + columns: new[] { "TenantId", "EntityTypeFullName", "EntityId" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpEntityPropertyChanges_EntityChangeId", + table: "AbpEntityPropertyChanges", + column: "EntityChangeId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpPermissionGrants_Name_ProviderName_ProviderKey", + table: "AbpPermissionGrants", + columns: new[] { "Name", "ProviderName", "ProviderKey" }); + migrationBuilder.CreateIndex( name: "IX_AbpRoleClaims_RoleId", table: "AbpRoleClaims", @@ -572,6 +744,11 @@ namespace IdentityServerHost.Migrations table: "AbpRoles", column: "NormalizedName"); + migrationBuilder.CreateIndex( + name: "IX_AbpSettings_Name_ProviderName_ProviderKey", + table: "AbpSettings", + columns: new[] { "Name", "ProviderName", "ProviderKey" }); + migrationBuilder.CreateIndex( name: "IX_AbpUserClaims_UserId", table: "AbpUserClaims", @@ -607,11 +784,6 @@ namespace IdentityServerHost.Migrations table: "AbpUsers", column: "UserName"); - migrationBuilder.CreateIndex( - name: "IX_IdentityServerClientClaims_ClientId", - table: "IdentityServerClientClaims", - column: "ClientId"); - migrationBuilder.CreateIndex( name: "IX_IdentityServerClients_ClientId", table: "IdentityServerClients", @@ -626,12 +798,24 @@ namespace IdentityServerHost.Migrations protected override void Down(MigrationBuilder migrationBuilder) { + migrationBuilder.DropTable( + name: "AbpAuditLogActions"); + migrationBuilder.DropTable( name: "AbpClaimTypes"); + migrationBuilder.DropTable( + name: "AbpEntityPropertyChanges"); + + migrationBuilder.DropTable( + name: "AbpPermissionGrants"); + migrationBuilder.DropTable( name: "AbpRoleClaims"); + migrationBuilder.DropTable( + name: "AbpSettings"); + migrationBuilder.DropTable( name: "AbpUserClaims"); @@ -686,6 +870,9 @@ namespace IdentityServerHost.Migrations migrationBuilder.DropTable( name: "IdentityServerPersistedGrants"); + migrationBuilder.DropTable( + name: "AbpEntityChanges"); + migrationBuilder.DropTable( name: "AbpRoles"); @@ -701,6 +888,9 @@ namespace IdentityServerHost.Migrations migrationBuilder.DropTable( name: "IdentityServerIdentityResources"); + migrationBuilder.DropTable( + name: "AbpAuditLogs"); + migrationBuilder.DropTable( name: "IdentityServerApiResources"); } diff --git a/templates/service/host/IdentityServerHost/Migrations/DemoAppDbContextModelSnapshot.cs b/templates/service/host/IdentityServerHost/Migrations/DemoAppDbContextModelSnapshot.cs index 2a102778ee..1aec48ea05 100644 --- a/templates/service/host/IdentityServerHost/Migrations/DemoAppDbContextModelSnapshot.cs +++ b/templates/service/host/IdentityServerHost/Migrations/DemoAppDbContextModelSnapshot.cs @@ -19,6 +19,202 @@ namespace IdentityServerHost.Migrations .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ApplicationName") + .HasColumnName("ApplicationName") + .HasMaxLength(96); + + b.Property("BrowserInfo") + .HasColumnName("BrowserInfo") + .HasMaxLength(512); + + b.Property("ClientId") + .HasColumnName("ClientId") + .HasMaxLength(64); + + b.Property("ClientIpAddress") + .HasColumnName("ClientIpAddress") + .HasMaxLength(64); + + b.Property("ClientName") + .HasColumnName("ClientName") + .HasMaxLength(128); + + b.Property("Comments") + .HasColumnName("Comments") + .HasMaxLength(256); + + b.Property("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasColumnName("CorrelationId") + .HasMaxLength(64); + + b.Property("Exceptions") + .HasColumnName("Exceptions") + .HasMaxLength(4000); + + b.Property("ExecutionDuration") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("HttpMethod") + .HasColumnName("HttpMethod") + .HasMaxLength(16); + + b.Property("HttpStatusCode") + .HasColumnName("HttpStatusCode"); + + b.Property("ImpersonatorTenantId") + .HasColumnName("ImpersonatorTenantId"); + + b.Property("ImpersonatorUserId") + .HasColumnName("ImpersonatorUserId"); + + b.Property("TenantId") + .HasColumnName("TenantId"); + + b.Property("Url") + .HasColumnName("Url") + .HasMaxLength(256); + + b.Property("UserId") + .HasColumnName("UserId"); + + b.Property("UserName") + .HasColumnName("UserName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ExecutionTime"); + + b.HasIndex("TenantId", "UserId", "ExecutionTime"); + + b.ToTable("AbpAuditLogs"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditLogId") + .HasColumnName("AuditLogId"); + + b.Property("ExecutionDuration") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnName("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("MethodName") + .HasColumnName("MethodName") + .HasMaxLength(128); + + b.Property("Parameters") + .HasColumnName("Parameters") + .HasMaxLength(2000); + + b.Property("ServiceName") + .HasColumnName("ServiceName") + .HasMaxLength(256); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "ServiceName", "MethodName", "ExecutionTime"); + + b.ToTable("AbpAuditLogActions"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditLogId") + .HasColumnName("AuditLogId"); + + b.Property("ChangeTime") + .HasColumnName("ChangeTime"); + + b.Property("ChangeType") + .HasColumnName("ChangeType"); + + b.Property("EntityId") + .IsRequired() + .HasColumnName("EntityId") + .HasMaxLength(128); + + b.Property("EntityTypeFullName") + .IsRequired() + .HasColumnName("EntityTypeFullName") + .HasMaxLength(128); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("TenantId") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "EntityTypeFullName", "EntityId"); + + b.ToTable("AbpEntityChanges"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EntityChangeId"); + + b.Property("NewValue") + .HasColumnName("NewValue") + .HasMaxLength(512); + + b.Property("OriginalValue") + .HasColumnName("OriginalValue") + .HasMaxLength(512); + + b.Property("PropertyName") + .IsRequired() + .HasColumnName("PropertyName") + .HasMaxLength(128); + + b.Property("PropertyTypeFullName") + .IsRequired() + .HasColumnName("PropertyTypeFullName") + .HasMaxLength(64); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityChangeId"); + + b.ToTable("AbpEntityPropertyChanges"); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => { b.Property("Id") @@ -443,7 +639,7 @@ namespace IdentityServerHost.Migrations b.Property("BackChannelLogoutSessionRequired"); b.Property("BackChannelLogoutUri") - .HasMaxLength(2000); + .HasMaxLength(300); b.Property("ClientClaimsPrefix") .HasMaxLength(200); @@ -456,7 +652,7 @@ namespace IdentityServerHost.Migrations .HasMaxLength(200); b.Property("ClientUri") - .HasMaxLength(2000); + .HasMaxLength(300); b.Property("ConcurrencyStamp"); @@ -475,14 +671,14 @@ namespace IdentityServerHost.Migrations b.Property("FrontChannelLogoutSessionRequired"); b.Property("FrontChannelLogoutUri") - .HasMaxLength(2000); + .HasMaxLength(300); b.Property("IdentityTokenLifetime"); b.Property("IncludeJwtId"); b.Property("LogoUri") - .HasMaxLength(2000); + .HasMaxLength(300); b.Property("PairWiseSubjectSalt") .HasMaxLength(200); @@ -515,22 +711,15 @@ namespace IdentityServerHost.Migrations modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientClaim", b => { - b.Property("Id") - .ValueGeneratedOnAdd(); - b.Property("ClientId"); b.Property("Type") - .IsRequired() .HasMaxLength(250); b.Property("Value") - .IsRequired() .HasMaxLength(250); - b.HasKey("Id"); - - b.HasIndex("ClientId"); + b.HasKey("ClientId", "Type", "Value"); b.ToTable("IdentityServerClientClaims"); }); @@ -576,7 +765,7 @@ namespace IdentityServerHost.Migrations b.Property("ClientId"); b.Property("PostLogoutRedirectUri") - .HasMaxLength(2000); + .HasMaxLength(200); b.HasKey("ClientId", "PostLogoutRedirectUri"); @@ -588,11 +777,11 @@ namespace IdentityServerHost.Migrations b.Property("ClientId"); b.Property("Key") - .HasMaxLength(250); + .HasMaxLength(64); b.Property("Value") .IsRequired() - .HasMaxLength(2000); + .HasMaxLength(128); b.HasKey("ClientId", "Key"); @@ -604,7 +793,7 @@ namespace IdentityServerHost.Migrations b.Property("ClientId"); b.Property("RedirectUri") - .HasMaxLength(2000); + .HasMaxLength(200); b.HasKey("ClientId", "RedirectUri"); @@ -725,6 +914,82 @@ namespace IdentityServerHost.Migrations b.ToTable("IdentityServerIdentityResources"); }); + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpPermissionGrants"); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasMaxLength(64); + + b.Property("ProviderName") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2048); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpSettings"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog") + .WithMany("Actions") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog") + .WithMany("EntityChanges") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.EntityChange") + .WithMany("PropertyChanges") + .HasForeignKey("EntityChangeId") + .OnDelete(DeleteBehavior.Cascade); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => { b.HasOne("Volo.Abp.Identity.IdentityRole") diff --git a/templates/service/host/IdentityServerHost/appsettings.json b/templates/service/host/IdentityServerHost/appsettings.json index 6e801d256d..98a4b98d4b 100644 --- a/templates/service/host/IdentityServerHost/appsettings.json +++ b/templates/service/host/IdentityServerHost/appsettings.json @@ -1,6 +1,7 @@ { "ConnectionStrings": { - "Default": "Server=localhost;Database=IdentityServerHost;Trusted_Connection=True;MultipleActiveResultSets=true", - "SqlServerCache": "Server=localhost;Database=MyProjectNameCache;Trusted_Connection=True;MultipleActiveResultSets=true" + "Default": "Server=localhost;Database=MyProjectName_Identity;Trusted_Connection=True;MultipleActiveResultSets=true", + + "SqlServerCache": "Server=localhost;Database=MyProjectName_Cache;Trusted_Connection=True;MultipleActiveResultSets=true" } } \ No newline at end of file diff --git a/templates/service/host/MyCompanyName.MyProjectName.Host/DemoAppDbContext.cs b/templates/service/host/MyCompanyName.MyProjectName.Host/DemoAppDbContext.cs index 1b3540e00b..ab9cad5293 100644 --- a/templates/service/host/MyCompanyName.MyProjectName.Host/DemoAppDbContext.cs +++ b/templates/service/host/MyCompanyName.MyProjectName.Host/DemoAppDbContext.cs @@ -21,8 +21,9 @@ namespace MyCompanyName.MyProjectName.Host modelBuilder.ConfigurePermissionManagement(); modelBuilder.ConfigureSettingManagement(); - modelBuilder.ConfigureMyProjectName(); modelBuilder.ConfigureAuditLogging(); + + modelBuilder.ConfigureMyProjectName(); } } } diff --git a/templates/service/host/MyCompanyName.MyProjectName.Host/Migrations/20190214093219_Initial.Designer.cs b/templates/service/host/MyCompanyName.MyProjectName.Host/Migrations/20190320142412_Initial.Designer.cs similarity index 99% rename from templates/service/host/MyCompanyName.MyProjectName.Host/Migrations/20190214093219_Initial.Designer.cs rename to templates/service/host/MyCompanyName.MyProjectName.Host/Migrations/20190320142412_Initial.Designer.cs index ee67505ed5..423eb1cb0a 100644 --- a/templates/service/host/MyCompanyName.MyProjectName.Host/Migrations/20190214093219_Initial.Designer.cs +++ b/templates/service/host/MyCompanyName.MyProjectName.Host/Migrations/20190320142412_Initial.Designer.cs @@ -10,7 +10,7 @@ using MyCompanyName.MyProjectName.Host; namespace MyCompanyName.MyProjectName.Host.Migrations { [DbContext(typeof(DemoAppDbContext))] - [Migration("20190214093219_Initial")] + [Migration("20190320142412_Initial")] partial class Initial { protected override void BuildTargetModel(ModelBuilder modelBuilder) diff --git a/templates/service/host/MyCompanyName.MyProjectName.Host/Migrations/20190214093219_Initial.cs b/templates/service/host/MyCompanyName.MyProjectName.Host/Migrations/20190320142412_Initial.cs similarity index 100% rename from templates/service/host/MyCompanyName.MyProjectName.Host/Migrations/20190214093219_Initial.cs rename to templates/service/host/MyCompanyName.MyProjectName.Host/Migrations/20190320142412_Initial.cs diff --git a/templates/service/host/MyCompanyName.MyProjectName.Host/_CreateSqlServerCacheDatabase.bat b/templates/service/host/MyCompanyName.MyProjectName.Host/_CreateSqlServerCacheDatabase.bat index 6b8b9d7060..927f8a72d7 100644 --- a/templates/service/host/MyCompanyName.MyProjectName.Host/_CreateSqlServerCacheDatabase.bat +++ b/templates/service/host/MyCompanyName.MyProjectName.Host/_CreateSqlServerCacheDatabase.bat @@ -1 +1 @@ -dotnet sql-cache create "Server=localhost;Database=MyProjectNameCache;Trusted_Connection=True;MultipleActiveResultSets=true" dbo TestCache \ No newline at end of file +dotnet sql-cache create "Server=localhost;Database=MyProjectName_Cache;Trusted_Connection=True;MultipleActiveResultSets=true" dbo TestCache \ No newline at end of file diff --git a/templates/service/host/MyCompanyName.MyProjectName.Host/appsettings.json b/templates/service/host/MyCompanyName.MyProjectName.Host/appsettings.json index 76dc222001..a09353bfef 100644 --- a/templates/service/host/MyCompanyName.MyProjectName.Host/appsettings.json +++ b/templates/service/host/MyCompanyName.MyProjectName.Host/appsettings.json @@ -1,6 +1,11 @@ { "ConnectionStrings": { - "Default": "Server=localhost;Database=MyProjectNameHost;Trusted_Connection=True;MultipleActiveResultSets=true", - "SqlServerCache": "Server=localhost;Database=MyProjectNameCache;Trusted_Connection=True;MultipleActiveResultSets=true" + "Default": "Server=localhost;Database=MyProjectName_ModuleDb;Trusted_Connection=True;MultipleActiveResultSets=true", + + "AbpSettingManagement": "Server=localhost;Database=MyProjectName_Identity;Trusted_Connection=True;MultipleActiveResultSets=true", + "AbpPermissionManagement": "Server=localhost;Database=MyProjectName_Identity;Trusted_Connection=True;MultipleActiveResultSets=true", + "AbpAuditLogging": "Server=localhost;Database=MyProjectName_Identity;Trusted_Connection=True;MultipleActiveResultSets=true", + + "SqlServerCache": "Server=localhost;Database=MyProjectName_Cache;Trusted_Connection=True;MultipleActiveResultSets=true" } } \ No newline at end of file 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(); - }); } } }