Browse Source

Merge branch 'dev' into berkan-engin/community

pull/4957/head
berkansasmaz 6 years ago
parent
commit
37db1f62dc
  1. 4
      build/test-all.ps1
  2. 78
      docs/en/Application-Services.md
  3. 4
      docs/en/Text-Templating.md
  4. 2
      docs/en/Tutorials/Part-2.md
  5. 28
      docs/en/UI/Angular/Localization.md
  6. BIN
      docs/en/UI/Angular/images/locale-error.png
  7. 3
      docs/en/UI/AspNetCore/Dynamic-JavaScript-Proxies.md
  8. 2
      docs/zh-Hans/Authorization.md
  9. 11
      docs/zh-Hans/CLI.md
  10. 70
      docs/zh-Hans/Getting-Started-React-Native.md
  11. 114
      docs/zh-Hans/Getting-Started.md
  12. 14
      docs/zh-Hans/Index.md
  13. 2
      docs/zh-Hans/Modules/Identity.md
  14. 2
      docs/zh-Hans/Modules/Index.md
  15. 47
      docs/zh-Hans/Modules/Organization-Units.md
  16. 4
      docs/zh-Hans/Nightly-Builds.md
  17. 2
      docs/zh-Hans/Options.md
  18. 2
      docs/zh-Hans/Repositories.md
  19. 1
      docs/zh-Hans/Road-Map.md
  20. 4
      docs/zh-Hans/Samples/Index.md
  21. 8
      docs/zh-Hans/Samples/Microservice-Demo.md
  22. 4
      docs/zh-Hans/SignalR-Integration.md
  23. 2
      docs/zh-Hans/Text-Templating.md
  24. 2
      docs/zh-Hans/Tutorials/Part-3.md
  25. 13
      docs/zh-Hans/docs-nav.json
  26. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs
  27. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs
  28. 26
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Toolbar/LanguageSwitch/LanguageSwitchViewComponent.cs
  29. 6
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml
  30. 123
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs
  31. 2
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpCachingModule.cs
  32. 566
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs
  33. 46
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs
  34. 43
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/UnitOfWorkCacheItem.cs
  35. 11
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/UnitOfWorkCacheItemExtensions.cs
  36. 10
      framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeHelper.cs
  37. 39
      framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyCrudAppService.cs
  38. 50
      framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyReadOnlyAppService.cs
  39. 9
      framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/CrudAppService.cs
  40. 14
      framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs
  41. 1
      framework/src/Volo.Abp.EventBus/Volo.Abp.EventBus.csproj
  42. 2
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs
  43. 69
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs
  44. 8
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Local/LocalEventBus.cs
  45. 38
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ApiTypeNameHelper.cs
  46. 6
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/MethodParameterApiDescriptionModel.cs
  47. 6
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ParameterApiDescriptionModel.cs
  48. 26
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs
  49. 4
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ReturnValueApiDescriptionModel.cs
  50. 12
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/TypeApiDescriptionModel.cs
  51. 4
      framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs
  52. 45
      framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkExtensions.cs
  53. 51
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/FormElements.cshtml
  54. 6
      framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/AbpCachingTestModule.cs
  55. 397
      framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs
  56. 61
      framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/TestMemoryDistributedCache.cs
  57. 29
      framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Distributed/LocalDistributedEventBus_Test.cs
  58. 32
      framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Distributed/MySimpleDistributedSingleInstanceEventHandler.cs
  59. 64
      framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/EventBus_MultiTenancy_Test.cs
  60. 12
      framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/MySimpleEventData.cs
  61. 2
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs
  62. 2
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLogoutModel.cs
  63. 16
      modules/account/src/Volo.Abp.Account.Web/Areas/Account/Controllers/AccountController.cs
  64. 3
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs
  65. 6
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs
  66. 2
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/Logout.cshtml.cs
  67. 29
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs
  68. 51
      modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementStore_Tests.cs
  69. 9
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentitySecurityLogContext.cs
  70. 26
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentitySecurityLogManager.cs
  71. 19
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/IdentityServerSecurityLogActionConsts.cs
  72. 7
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/IdentityServerSecurityLogIdentityConsts.cs
  73. 25
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs
  74. 37
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/SignInResultExtensions.cs
  75. 1
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDataSeedContributor.cs
  76. 32
      modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/PermissionDataSeedContributor_Tests.cs
  77. 4
      modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/TestPermissionDefinitionProvider.cs
  78. 31
      modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManagementStore.cs
  79. 34
      modules/setting-management/test/Volo.Abp.SettingManagement.Tests/Volo/Abp/SettingManagement/SettingManagementStore_Tests.cs
  80. 8
      npm/ng-packs/package.json
  81. 9
      npm/ng-packs/packages/core/src/lib/core.module.ts
  82. 1
      npm/ng-packs/packages/core/src/lib/models/common.ts
  83. 4
      npm/ng-packs/packages/core/src/lib/services/localization.service.ts
  84. 11
      npm/ng-packs/packages/core/src/lib/tokens/options.token.ts
  85. 14
      npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts
  86. 24
      npm/ng-packs/packages/theme-basic/src/lib/theme-basic.module.ts
  87. 3
      npm/ng-packs/tsconfig.base.json
  88. 501
      npm/ng-packs/yarn.lock
  89. 1
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName.MyProjectName.Application.Contracts.csproj
  90. 12
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Application.Contracts/MyProjectNameApplicationContractsModule.cs

4
build/test-all.ps1

@ -1,4 +1,6 @@
. ".\common.ps1" -f
$full = $args[0]
. ".\common.ps1" $full
# Test all solutions

78
docs/en/Application-Services.md

@ -367,6 +367,84 @@ public class DistrictKey
}
````
### Authorization (for CRUD App Services)
There are two ways of authorizing the base application service methods;
1. You can set the policy properties (xxxPolicyName) in the constructor of your service. Example:
```csharp
public class MyPeopleAppService : CrudAppService<Person, PersonDto, Guid>
{
public MyPeopleAppService(IRepository<Person, Guid> repository)
: base(repository)
{
GetPolicyName = "...";
GetListPolicyName = "...";
CreatePolicyName = "...";
UpdatePolicyName = "...";
DeletePolicyName = "...";
}
}
```
`CreatePolicyName` is checked by the `CreateAsync` method and so on... You should specify a policy (permission) name defined in your application.
2. You can override the check methods (CheckXxxPolicyAsync) in your service. Example:
```csharp
public class MyPeopleAppService : CrudAppService<Person, PersonDto, Guid>
{
public MyPeopleAppService(IRepository<Person, Guid> repository)
: base(repository)
{
}
protected override async Task CheckDeletePolicyAsync()
{
await AuthorizationService.CheckAsync("...");
}
}
```
You can perform any logic in the `CheckDeletePolicyAsync` method. It is expected to throw an `AbpAuthorizationException` in any unauthorized case, like `AuthorizationService.CheckAsync` already does.
### Base Properties & Methods
CRUD application service base class provides many useful base methods that **you can override** to customize it based on your requirements.
#### CRUD Methods
These are the essential CRUD methods. You can override any of them to completely customize the operation. Here, the definitions of the methods:
````csharp
Task<TGetOutputDto> GetAsync(TKey id);
Task<PagedResultDto<TGetListOutputDto>> GetListAsync(TGetListInput input);
Task<TGetOutputDto> CreateAsync(TCreateInput input);
Task<TGetOutputDto> UpdateAsync(TKey id, TUpdateInput input);
Task DeleteAsync(TKey id);
````
#### Querying
These methods are low level methods those can be control how to query entities from the database.
* `CreateFilteredQuery` can be overridden to create an `IQueryable<TEntity>` that is filtered by the given input. If your `TGetListInput` class contains any filter, it is proper to override this method and filter the query. It returns the (unfiltered) repository (which is already `IQueryable<TEntity>`) by default.
* `ApplyPaging` is used to make paging on the query. If your `TGetListInput` already implements `IPagedResultRequest`, you don't need to override this since the ABP Framework automatically understands it and performs the paging.
* `ApplySorting` is used to sort (order by...) the query. If your `TGetListInput` already implements the `ISortedResultRequest`, ABP Framework automatically sorts the query. If not, it fallbacks to the `ApplyDefaultSorting` which tries to sort by creating time, if your entity implements the standard `IHasCreationTime` interface.
* `GetEntityByIdAsync` is used to get an entity by id, which calls `Repository.GetAsync(id)` by default.
* `DeleteByIdAsync` is used to delete an entity by id, which calls `Repository.DeleteAsync(id)` by default.
#### Object to Object Mapping
These methods are used to convert Entities to DTOs and vice verse. They uses the [IObjectMapper](Object-To-Object-Mapping.md) by default.
* `MapToGetOutputDtoAsync` is used to map the entity to the DTO returned from the `GetAsync`, `CreateAsync` and `UpdateAsync` methods. Alternatively, you can override the `MapToGetOutputDto` if you don't need to perform any async operation.
* `MapToGetListOutputDtosAsync` is used to map a list of entities to a list of DTOs returned from the `GetListAsync` method. It uses the `MapToGetListOutputDtoAsync` to map each entity in the list. You can override one of them based on your case. Alternatively, you can override the `MapToGetListOutputDto` if you don't need to perform any async operation.
* `MapToEntityAsync` method has two overloads;
* `MapToEntityAsync(TCreateInput)` is used to create an entity from `TCreateInput`.
* `MapToEntityAsync(TUpdateInput, TEntity)` is used to update an existing entity from `TUpdateInput`.
## Lifetime
Lifetime of application services are [transient](Dependency-Injection.md) and they are automatically registered to the dependency injection system.

4
docs/en/Text-Templating.md

@ -4,7 +4,7 @@
ABP Framework provides a simple, yet efficient text template system. Text templating is used to dynamically render contents based on a template and a model (a data object):
***TEMPLATE + MODEL ==render==> RENDERED CONTENT***
Template + Model =renderer=> Rendered Content
It is very similar to an ASP.NET Core Razor View (or Page):
@ -454,4 +454,4 @@ Return `null` if your source can not find the content, so `ITemplateContentProvi
* [The source code of the sample application](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo) developed and referred through this document.
* [Localization system](Localization.md).
* [Virtual File System](Virtual-File-System.md).
* [Virtual File System](Virtual-File-System.md).

2
docs/en/Tutorials/Part-2.md

@ -56,7 +56,7 @@ This tutorials has multiple versions based on your **UI** and **Database** prefe
It's common to call the HTTP API endpoints via AJAX from the **JavaScript** side. You can use `$.ajax` or another tool to call the endpoints. However, ABP offers a better way.
ABP **dynamically** creates **[JavaScript Proxies](../UI/AspNetCore/)** for all API endpoints. So, you can use any **endpoint** just like calling a **JavaScript function**.
ABP **dynamically** creates **[JavaScript Proxies](../UI/AspNetCore/Dynamic-JavaScript-Proxies.md)** for all API endpoints. So, you can use any **endpoint** just like calling a **JavaScript function**.
### Testing in the Developer Console

28
docs/en/UI/Angular/Localization.md

@ -193,6 +193,32 @@ import { Component } from '@angular/core';
export class AppComponent {}
```
## Mapping of Culture Name to Angular Locale File Name
Some of the culture names defined in .NET do not match Angular locales. In such cases, the Angular app throws an error like below at runtime:
![locale-error](./images/locale-error.png)
If you see an error like this, you should pass the `cultureNameToLocaleFileNameMapping` property like below to CoreModule's forRoot static method.
```js
// app.module.ts
@NgModule({
imports: [
// other imports
CoreModule.forRoot({
// other options
cultureNameToLocaleFileNameMapping: {
"DotnetCultureName": "AngularLocaleFileName",
"pt-BR": "pt" // example
}
})
//...
```
See [all locale files in Angular](https://github.com/angular/angular/tree/master/packages/common/locales).
## See Also
@ -200,4 +226,4 @@ export class AppComponent {}
## What's Next?
* [Permission Management](./Permission-Management.md)
* [Permission Management](./Permission-Management.md)

BIN
docs/en/UI/Angular/images/locale-error.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

3
docs/en/UI/AspNetCore/Dynamic-JavaScript-Proxies.md

@ -0,0 +1,3 @@
# Dynamic JavaScript HTTP API Proxies
TODO

2
docs/zh-Hans/Authorization.md

@ -285,7 +285,7 @@ public async Task CreateAsync(CreateAuthorDto input)
abp.auth.isGranted('MyPermissionName');
````
参阅 [abp.auth](AspNetCore/JavaScript-API/Auth.md) API 文档了解详情.
参阅 [abp.auth](UI/AspNetCore/JavaScript-API/Index.md) API 文档了解详情.
## 权限管理

11
docs/zh-Hans/CLI.md

@ -90,9 +90,10 @@ abp new Acme.BookStore
* **`console`**: [Console template](Startup-Templates/Console.md).
* `--output-folder` 或者 `-o`: 指定输出文件夹,默认是当前目录.
* `--version` 或者 `-v`: 指定ABP和模板的版本.它可以是 [release tag](https://github.com/abpframework/abp/releases) 或者 [branch name](https://github.com/abpframework/abp/branches). 如果没有指定,则使用最新版本.大多数情况下,你会希望使用最新的版本.
* `--template-source` 或者 `-ts`: 指定自定义模板源用于生成项目,可以使用本地源和网络源(例如 `D\localTemplate` 或 `https://<your url>.zip`).
* `--preview`: 使用最新的预发行版本 (仅在未指定 `--version` 且最新稳定版本之后至少有一个预发行版时).
* `--template-source` 或者 `-ts`: 指定自定义模板源用于生成项目,可以使用本地源和网络源(例如 `D:\local-templat` 或 `https://.../my-template-file.zip`).
* `--create-solution-folder` 或者 `-csf`: 指定项目是在输出文件夹中的新文件夹中还是直接在输出文件夹中.
* `--connection-string` 或者 `-cs`: 重写所有 `appsettings.json` 文件的默认连接字符串. 默认连接字符串是 `Server=localhost;Database=MyProjectName;Trusted_Connection=True;MultipleActiveResultSets=true`. 如果你不想使用默认,你可以设置自己的连接字符串. 默认的数据库提供程序是 `SQL Server`, 所以你只能输入SQL Server连接字符串!
* `--connection-string` 或者 `-cs`: 重写所有 `appsettings.json` 文件的默认连接字符串. 默认连接字符串是 `Server=localhost;Database=MyProjectName;Trusted_Connection=True;MultipleActiveResultSets=true`. 默认的数据库提供程序是 `SQL Server`. 如果你使用EF Core但需要更改DBMS,可以按[这里所述](Entity-Framework-Core-Other-DBMS.md)进行更改(创建解决方案之后).
* `--local-framework-ref --abp-path`: 使用对项目的本地引用,而不是替换为NuGet包引用.
### add-package
@ -173,15 +174,15 @@ abp update [options]
### 切换到每晚构建(预览)包
想要切换到ABP框架的最新预览版可以使用此命令.
想要切换到ABP框架的最新**每晚构建**预览版可以使用此命令.
用法:
````bash
abp switch-to-preview [options]
abp switch-to-nightly [options]
````
你也可以使用切换回稳定版本:
你也可以使用切换回最新稳定版本:
````bash
abp switch-to-stable [options]

70
docs/zh-Hans/Getting-Started-React-Native.md

@ -0,0 +1,70 @@
# React Native入门
ABP平台提供了[React Native](https://reactnative.dev/)模板用于开发移动应用程序.
当你按照[入门文档](Getting-Started.md)中所述**创建新应用程序**时,解决方案默认将React Native应用程序包含在 `react-native` 文件夹中.
## 配置你的本地IP地址
运行在Android模拟器或真机上的React Native应用程序无法连接到 `localhost` 上的后.要修复此问题,需要在本地IP上运行后端.
{{ if Tiered == "No"}}
![React Native host project local IP entry](images/rn-host-local-ip.png)
* 打开 `.HttpApi.Host` 文件夹下的 `appsettings.json` 文件. 将 `SelfUrl` 和 `Authority` 属性的 `localhost` 替换为你本地的IP地址.
* 打开 `.HttpApi.Host/Properties` 文件夹下的 `launchSettings.json` 文件. 将 `applicationUrl` 属性的 `localhost` 替换为你本地的IP地址.
{{ else if Tiered == "Yes" }}
![React Native tiered project local IP entry](images/rn-tiered-local-ip.png)
* 打开 `.IdentityServer` 文件夹下的 `appsettings.json` 文件. 将 `SelfUrl` 属性的 `localhost` 替换为你本地的IP地址.
* 打开 `.IdentityServer/Properties` 文件夹下的 `launchSettings.json` 文件. 将 `applicationUrl` 属性的 `localhost` 替换为你本地的IP地址.
* 打开 `.HttpApi.Host` 文件夹下的 `appsettings.json` 文件. 将 `Authority` 属性的 `localhost` 替换为你本地的IP地址.
* 打开 `.HttpApi.Host/Properties` 文件夹下的 `launchSettings.json` 文件. 将 `applicationUrl` 属性的 `localhost` 替换为你本地的IP地址.
{{ end }}
按照**运行HTTP API Host (服务端口)**那样运行后端.
> React Native应用程序不信任自动生成的.NET HTTPS证书,你可以在开发期间使用HTTP.
在 `react-native` 文件夹打开命令行终端,输入 `yarn` 命令(我们推荐使用[yarn](https://yarnpkg.com/)包管理, `npm install` 在大多数情况下也可以工作).
```bash
yarn
```
* 打开 `react-nativer` 文件夹下的 `Environment.js` 文件. 将 `apiUrl` 和 `issuer` 属性的 `localhost` 替换为你本地的IP地址:
![react native environment local IP](images/rn-environment-local-ip.png)
{{ if Tiered == "Yes" }}
> 确保 `issuer` 与正在运行的 `.IdentityServer` 项目匹配, `apiUrl` 与正在运行的 `.HttpApi.Host` 项目匹配.
{{else}}
> 确保 `issuer` 和 `apiUrl` 与正在运行的 `.HttpApi.Host` 项目匹配
{{ end }}
等到所有node模块加载成功, 执行 `yarn start` (或 `npm start`) 命令:
```bash
yarn start
```
等待Expo CLI启动后Expo CLI在 `http://localhost:19002/` 地址要开管理页面.
![expo-interface](images/rn-expo-interface.png)
在上面的管理界面中,可以通过使用[Expo Client](https://expo.io/tools#client)扫描二维码,使用Android模拟器,iOS模拟器或真机来启动应用程序.
> 请参阅expo.io上的[Android Studio模拟器](https://docs.expo.io/workflow/android-simulator/)和[iOS模拟器文档](https://docs.expo.io/workflow/ios-simulator/).
![React Native login screen on iPhone 11](images/rn-login-iphone.png)
输入用户名 **admin**,密码 **1q2w3E*** 登录到应用程序.
应用程序已经启动并执行,你可以基于该启动模板开发应用程序.

114
docs/zh-Hans/Getting-Started.md

@ -19,24 +19,27 @@
你需要安装以下工具:
* [Visual Studio 2019 (v16.4+)](https://visualstudio.microsoft.com/vs/) for Windows / [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/).
* [.NET Core 3.0+](https://www.microsoft.com/net/download/dotnet-core/)
* [Visual Studio 2019 (v16.4+)](https://visualstudio.microsoft.com/vs/) for Windows / [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/).<sup id="a-editor">[1](#f-editor)</sup>
* [.NET Core 3.1+](https://www.microsoft.com/net/download/dotnet-core/)
* [Node v12+](https://nodejs.org)
* [Node v12 或 v14](https://nodejs.org/en/)
* [Yarn v1.19+](https://classic.yarnpkg.com/)
* [Yarn v1.20+ (not v2)](https://classic.yarnpkg.com/en/docs/install) <sup id="a-yarn">[2](#f-yarn)</sup> 或 npm v6+ (与Node一起安装)
{{ if Tiered == "Yes" }}
* [Redis](https://redis.io/): 应用程序将Redis用作[分布式缓存](../Caching.md). 因此你需要安装并运行Redis.
* [Redis](https://redis.io/): 入门解决方案将Redis用作[分布式缓存](Caching.md). 因此你需要安装并运行Redis.
{{ end }}
> 你可以也使用其他支持.NET Core 和 ASP.NET Core的编辑器.
<sup id="f-editor"><b>1</b></sup> _只要支持.NET Core和ASP.NET Core,就可以使用其他编辑器代替Visual Studio._ <sup>[↩](#a-editor)</sup>
<sup id="f-yarn"><b>2</b></sup> _Yarn v2 的工作方式不同,不受支持._ <sup>[↩](#a-yarn)</sup>
### 安装ABP CLI
[ABP CLI](./CLI.md)是一个命令行页面,用于为基于ABP的应用程序验证和自动化一些任务.
[ABP CLI](./CLI.md)是一个命令行页面,用于自动执行一些基于ABP的应用程序的常见任务.
> ABP CLI是[ABP框架](https://abp.io/)一个免费开源的工具.
> ABP CLI是ABP框架一个免费开源的工具.
你需要使用以下命令安排ABP CLI:
@ -59,10 +62,10 @@ dotnet tool update -g Volo.Abp.Cli
使用ABP CLI的 `new` 命令创建新项目:
````shell
abp new Acme.BookStore -t app{{if UI == "NG"}} -u angular {{end}}{{if DB == "Mongo"}} -d mongodb{{end}}{{if Tiered == "Yes" && UI != "NG"}} --tiered {{else if Tiered == "Yes" && UI == "NG"}}--separate-identity-server{{end}}
abp new Acme.BookStore{{if UI == "NG"}} -u angular {{end}}{{if DB == "Mongo"}} -d mongodb{{end}}{{if Tiered == "Yes" && UI != "NG"}} --tiered {{else if Tiered == "Yes" && UI == "NG"}}--separate-identity-server{{end}}
````
* `-t` 参数指定 [启动模板](Startup-Templates/Application.md) 名称. `app` 是一个启动模板名称,包含了预安装并且配置好的[ABP模块](Modules/Index.md).
* 此命令还会在解决方案文件夹内创建一个React Native移动应用程序. 如果你不想要它,可以安全地删除它,或者在 `abp new` 命令中指定 `-m none` 选项,以使其完全不包含在解决方案中.
{{ if UI == "NG" }}
@ -70,7 +73,7 @@ abp new Acme.BookStore -t app{{if UI == "NG"}} -u angular {{end}}{{if DB == "Mon
{{ if Tiered == "Yes" }}
* `--separate-identity-server` 参数用于将Identity服务器应用程序与API主机应用程序分隔开. 如果未指定,你将只有一个端点.
* `--separate-identity-server` 参数用于将Identity服务器应用程序与API主机应用程序分隔开. 如果未指定,则服务器上将只有一个端点.
{{ end }}
@ -90,6 +93,12 @@ abp new Acme.BookStore -t app{{if UI == "NG"}} -u angular {{end}}{{if DB == "Mon
> 你可以使用不同级别的命令空间; 例如. BookStore, Acme.BookStore or Acme.Retail.BookStore.
#### ABP CLI 命令 & 选项
[ABP CLI文档](./CLI.md)涵盖了ABP CLI的所有可用命令和选项. 本文档使用[应用程序启动模板](Startup-Templates/Application.md)创建新的Web应用程序. 有关其他模板,请参见[ABP启动模板](Startup-Templates/Index.md)文档.
> 或者,您可以从[ABP Framework网站](https://abp.io/get-started)中选择"直接下载"选项卡创建新的解决方案.
## 解决方案结构
{{ if UI == "MVC" }}
@ -146,7 +155,7 @@ abp new Acme.BookStore -t app{{if UI == "NG"}} -u angular {{end}}{{if DB == "Mon
## 创建数据库
### 数据库连接字符串
### 连接字符串
检查 {{if UI == "MVC"}}{{if Tiered == "Yes"}}`.IdentityServer` 和 `.HttpApi.Host` 项目{{else}}`.Web` 项目{{end}}{{else if UI == "NG" }}`.HttpApi.Host` 项目{{end}}下 `appsettings.json` 文件中的 **链接字符串**:
@ -214,7 +223,9 @@ Ef Core具有`Update-Database`命令, 可根据需要创建数据库并应用挂
这将基于配置的连接字符串创建新数据库.
> 使用`.Migrator`工具是建议的方法, 因为它还能初始化初始数据能够正确运行Web应用程序.
> **使用`.DbMigrator`工具是建议的方法**, 因为它能初始化初始数据能够正确运行Web应用程序.
>
> 如果你只是使用 `Update-Database` 命令,你会得到一个空数据库,所以你无法登录到应用程序因为数据库中没有初始管理用户. 不需要种子数据库时,可以在开发期间使用 `Update-Database` 命令. 但是使用 `.DbMigrator` 应用程序会更简单,你始终可以使用它来迁移模式并为数据库添加种子.
{{ else if DB == "Mongo" }}
@ -240,7 +251,7 @@ Ef Core具有`Update-Database`命令, 可根据需要创建数据库并应用挂
![db-migrator-output](images/db-migrator-output.png)
> 数据库创建后会初始化种子数据, 其中包含用于登录的 `admin` 用户. 所以你至少使用 `.DbMigrator` 一次.
> 数据库创建后会初始化[种子数据](Data-Seeding.md), 其中包含用于登录的 `admin` 用户. 所以你至少使用 `.DbMigrator` 一次.
{{ end }}
@ -336,83 +347,18 @@ yarn start
{{ end }}
输入用户名 **admin**,密码 **1q2w3E*** 登录到应用程序.
![bookstore-home](images/bookstore-home.png)
应用程序已经启动并执行,你可以基于该启动模板开发应用程序.
输入用户名 **admin**,密码 **1q2w3E*** 登录到应用程序,应用程序已经启动并执行,你可以基于此启动模板开始开发应用程序.
#### 移动开发
ABP平台提供了[React Native](https://reactnative.dev/)模板用于开发移动应用程序.
> 该解决方案默认 `react-native` 包含了React Native应用程序,如果你不计划使用React Native开发移动应用程序,你可以忽略并删除 `react-native` 文件夹.
运行在Android模拟器或真机上的React Native应用程序无法连接到 `localhost` 上的后.要修复此问题,需要在本地IP上运行后端.
{{ if Tiered == "No"}}
![React Native host project local IP entry](images/rn-host-local-ip.png)
* 打开 `.HttpApi.Host` 文件夹下的 `appsettings.json` 文件. 将 `SelfUrl` 和 `Authority` 属性的 `localhost` 替换为你本地的IP地址.
* 打开 `.HttpApi.Host/Properties` 文件夹下的 `launchSettings.json` 文件. 将 `applicationUrl` 属性的 `localhost` 替换为你本地的IP地址.
{{ else if Tiered == "Yes" }}
![React Native tiered project local IP entry](images/rn-tiered-local-ip.png)
* 打开 `.IdentityServer` 文件夹下的 `appsettings.json` 文件. 将 `SelfUrl` 属性的 `localhost` 替换为你本地的IP地址.
* 打开 `.IdentityServer/Properties` 文件夹下的 `launchSettings.json` 文件. 将 `applicationUrl` 属性的 `localhost` 替换为你本地的IP地址.
* 打开 `.HttpApi.Host` 文件夹下的 `appsettings.json` 文件. 将 `Authority` 属性的 `localhost` 替换为你本地的IP地址.
* 打开 `.HttpApi.Host/Properties` 文件夹下的 `launchSettings.json` 文件. 将 `applicationUrl` 属性的 `localhost` 替换为你本地的IP地址.
{{ end }}
按照**运行HTTP API Host (服务端口)**那样运行后端.
> React Native应用程序不信任自动生成的.NET HTTPS证书,你可以在开发期间使用HTTP.
在 `react-native` 文件夹打开命令行终端,输入 `yarn` 命令(我们推荐使用[yarn](https://yarnpkg.com/)包管理, `npm install` 在大多数情况下也可以工作).
```bash
yarn
```
* 打开 `react-nativer` 文件夹下的 `Environment.js` 文件. 将 `apiUrl` 和 `issuer` 属性的 `localhost` 替换为你本地的IP地址:
![react native environment local IP](images/rn-environment-local-ip.png)
{{ if Tiered == "Yes" }}
> 确保 `issuer` 与正在运行的 `.IdentityServer` 项目匹配, `apiUrl` 与正在运行的 `.HttpApi.Host` 项目匹配.
{{else}}
> 确保 `issuer` 和 `apiUrl` 与正在运行的 `.HttpApi.Host` 项目匹配
{{ end }}
等到所有node模块加载成功, 执行 `yarn start` (或 `npm start`) 命令:
```bash
yarn start
```
等待Expo CLI启动后Expo CLI在 `http://localhost:19002/` 地址要开管理页面.
![expo-interface](images/rn-expo-interface.png)
在上面的管理界面中,可以通过使用[Expo Client](https://expo.io/tools#client)扫描二维码,使用Android模拟器,iOS模拟器或真机来启动应用程序.
> 请参阅expo.io上的[Android Studio模拟器](https://docs.expo.io/workflow/android-simulator/)和[iOS模拟器文档](https://docs.expo.io/workflow/ios-simulator/).
![React Native login screen on iPhone 11](images/rn-login-iphone.png)
当你创建一个新的应用程序时.该解决方案默认包含 `react-native`文件夹. 这是一个基础的[React Native](https://reactnative.dev/)启动模板,用于开发与基于ABP的后端集成的移动应用程序.
输入用户名 **admin**,密码 **1q2w3E*** 登录到应用程序.
如果你不计划使用React Native开发移动应用程序,你可以忽略并删除 `react-native` 文件夹.
应用程序已经启动并执行,你可以基于该启动模板开发应用程序.
> 你可以在ABP CLI中指定 `-m none` 选项,以使 `react-native` 目录完全不包含在解决方案中
> [应用程序启动模板](Startup-Templates/Application.md) 包含租户管理和Identity模块.
请参阅"[React Native入门](Getting-Started-React-Native.md)"文档了解如何配置和运行React Native应用程序.
## 下一步是什么?
[应用程序开发教程](Tutorials/Part-1.md)
[Web应用程序开发教程](Tutorials/Part-1.md)

14
docs/zh-Hans/Index.md

@ -4,23 +4,17 @@
ABP是一个**开源应用程序框架**,专注于基于ASP.NET Core的Web应用程序开发,但也支持开发其他类型的应用程序.
浏览左侧导航菜单以深入了解文档.
浏览导航菜单以深入了解文档.
## 入门
使用ABP开发新项目的最简单方法是使用启动模板:
使用ABP开发新Web应用程序的最简单方法是使用[入门](Getting-Started.md)教程:
* [ASP.NET Core MVC (Razor Pages) UI 启动模板](Getting-Started.md?UI=MVC&DB=EF&Tiered=No)
* [Angular UI 启动模板](Getting-Started.md?UI=NG&DB=EF&Tiered=No)
如果你想从头开始(使用空项目),请手动安装ABP框架并使用以下教程:
* [控制台应用程序](Getting-Started-Console-Application.md)
* [ASP.NET Core Web 应用程序](Getting-Started-AspNetCore-Application.md)
然后你可以继续进行[Web应用程序开发教程](Tutorials/Part-1.md).
## 源码
ABP托管在GitHub上, 参见[源代码](https://github.com/abpframework/abp).
ABP托管在GitHub上, 参见[源代码](https://github.com/abpframework).
## 贡献代码

2
docs/zh-Hans/Modules/Identity.md

@ -1,3 +1,5 @@
# 身份管理模块
身份模块基于Microsoft Identity 库用于管理[组织单元](Organization-Units.md), 角色, 用户和他们的权限.
参阅 [源码](https://github.com/abpframework/abp/tree/dev/modules/identity). 文档很快会被完善.

2
docs/zh-Hans/Modules/Index.md

@ -17,7 +17,7 @@ ABP是一个 **模块化的应用程序框架** 由十多个 **nuget packages**
* **Blogging**: 用于创建精美的博客. ABP的[博客](https://blog.abp.io/) 就使用了此模块.
* [**Docs**](Docs.md): 用于创建技术文档页面. ABP的[文档](https://abp.io/documents/) 就使用了此模块.
* **Feature Management**: 用于保存和管理功能.
* **Identity**: 基于Microsoft Identity管理角色,用户和他们的权限.
* [**Identity**](Identity.md): 基于Microsoft Identity管理组织单元,角色,用户和他们的权限.
* **Identity Server**: 集成了IdentityServer4.
* **Permission Management**: 用于保存权限.
* **Setting Management**: 用于保存设置.

47
docs/zh-Hans/Modules/Organization-Units.md

@ -0,0 +1,47 @@
# 组织单元管理
组织单元(OU)是"身份模块"的一部分,用于**对用户和实体进行分层分组**.
### OrganizationUnit 实体
OU由 **OrganizationUnit** 实体表示. 实体有以下基本属性:
- **TenantId**: OU租户的ID,为null代表是宿主OU.
- **ParentId**: OU的父亲Id,为null为根OU.
- **Code**: 租户唯一的分层字符串代码.
- **DisplayName**: OU的显示名称.
OrganizationUnit实体的主键(Id)是 **Guid** 类型,派生自[**FullAuditedAggregateRoot**](../Entities.md)类.
#### Organization 树
因为OU可以有父亲,租户所有的OU是一个**树**结构. 树有一些规则:
- 可以有多个根(`ParentId` 为 `null`).
- OU的第一级子级数有限制(面说明的固定OU代码单位长度).
#### OU Code
OU代码由OrganizationUnit Manager自动生成和维护. 看起来像这样的字符串:
"**00001.00042.00005**"
此代码可用于轻松查询数据库中OU的所有子级(递归). 代码有一些规则:
- 必须[租户](../Multi-Tenancy.md)**唯一**的.
- 同一OU的所有子代均以**父OU的代码开头**.
- 它是**固定长度**的,并且基于树中OU的级别,如示例中所示.
- 虽然OU代码是唯一的,但是如果移动OU,它是**可更改的**.
- 你必须通过Id而不是代码引用OU.
### OrganizationUnit Manager
可以注入 **OrganizationUnitManager** 管理OU. 常见的用例有:
- 创建,更改或删除OU.
- 在OU树中移动OU.
- 获取有关OU树及其项的信息
#### 多租户
`OrganizationUnitManager` 设置为一次性为 **单个租户** 工作,默认是 **当前租户**.

4
docs/zh-Hans/Nightly-Builds.md

@ -29,13 +29,13 @@
预览NPM包的最新版本可以通过在应用程序的根文件夹命令运行命令安装:
```bash
abp switch-to-preview
abp switch-to-preview --npm
```
如果你正在使用ABP框架预览包,你可以使用此命令切换回稳定版本:
```bash
abp switch-to-stable
abp switch-to-stable --npm
```
参阅 [ABP CLI 文档](./CLI.md) 了解更多信息.

2
docs/zh-Hans/Options.md

@ -82,7 +82,7 @@ public class MyService : ITransientDependency
对于此类情况,ABP为 `IServiceCollection` 引入了 `PreConfigure<TOptions>` 和 `ExecutePreConfiguredActions<TOptions>` 扩展方法. 该模式的工作原理如下所述.
1. 你的模块中定义计划选项类. 例:
在你的模块中定义预先选项类. 例:
````csharp
public class MyPreOptions

2
docs/zh-Hans/Repositories.md

@ -193,8 +193,6 @@ var people = ((IMongoQueryable<Person>)_personRepository
### 选项-3: IAsyncQueryableExecuter
> 注意,此功能在ABP框架3.0以之后的版本可用,虽然它也可以用于较早的版本,但它提供的方法非常有限.
`IAsyncQueryableExecuter` 是一个用于异步执行 `IQueryable<T>` 对象的服务,**不依赖于实际的数据库提供程序**.
**示例: 注入并使用 `IAsyncQueryableExecuter.ToListAsync()` 方法**

1
docs/zh-Hans/Road-Map.md

@ -9,7 +9,6 @@
除了中期目录,还有一些[积压](https://github.com/abpframework/abp/milestone/2)的功能, 这里积压中重要功能的列表:
* [#4098](https://github.com/abpframework/abp/issues/4098) / Blob存储Azure提供者.
* [#2882](https://github.com/abpframework/abp/issues/2882) / 提供 **gRPC集成** 基础设施 (虽然[已经可以](https://github.com/abpframework/abp-samples/tree/master/GrpcDemo)为你的应用程序创建和使用gRPC端点,但我们计划为所有[标准应用程序模块](https://docs.abp.io/en/abp/latest/Modules/Index)创建端点)
* [#236](https://github.com/abpframework/abp/issues/236) 基于权限系统的资源
* [#1754](https://github.com/abpframework/abp/issues/1754) / 多语言实体

4
docs/zh-Hans/Samples/Index.md

@ -7,7 +7,7 @@
演示如何基于微服务体系结构构建系统的完整解决方案.
* [示例的文档](Microservice-Demo.md)
* [源码](https://github.com/abpframework/abp/tree/dev/samples/MicroserviceDemo)
* [源码](https://github.com/abpframework/abp-samples/tree/master/MicroserviceDemo)
* [微服务架构文档](../Microservice-Architecture.md)
### Book Store
@ -48,6 +48,8 @@
* **文本模板 Demo**: 文本模板系统的不同用例.
* [源码](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo)
* [文本模板文档](../Text-Templating.md)
* **存储过程 Demo**: 演示如何以最佳实践使用存储过程,数据库视图和函数.
* [源码](https://github.com/abpframework/abp-samples/tree/master/StoredProcedureDemo)
* **自定义认证**: 如何为ASP.NET Core MVC / Razor Pages应用程序自定义身份验证的解决方案.
* [源码](https://github.com/abpframework/abp-samples/tree/master/Authentication-Customization)
* 相关 "[How To](../How-To/Index.md)" 文档:

8
docs/zh-Hans/Samples/Microservice-Demo.md

@ -28,7 +28,7 @@ ABP框架的主要目标之一就是提供[便捷的基础设施来创建微服
### 源码
你可以从[GitHub仓库](https://github.com/abpframework/abp/tree/master/samples/MicroserviceDemo)获取源码.
你可以从[GitHub仓库](https://github.com/abpframework/abp-samples/tree/master/MicroserviceDemo)获取源码.
## 运行解决方案
@ -43,6 +43,12 @@ ABP框架的主要目标之一就是提供[便捷的基础设施来创建微服
* [ElasticSearch](https://www.elastic.co/downloads/elasticsearch) 6.6+
* [Kibana](https://www.elastic.co/downloads/kibana) 6.6+ (可选,建议显示日志)
### 运行基础设施
- 默认情况下运行Docker-compose可以轻松运行预先要求. 如果你没有安装,在windows环境下你可以在[这里](https://docs.docker.com/docker-for-windows/install/)下载并使用[Docker for Windows](https://docs.docker.com/docker-for-windows/).
* 在 `MicroserviceDemo` 目录下运行命令 `docker-compose -f docker-compose.infrastructure.yml -f docker-compose.infrastructure.override.yml up -d` 或使用powershell运行 `MicroserviceDemo/_run` 文件夹下的 `__Run_Infrastructure.ps1` 脚本.
* 如果你不想使用docker运行预先要求的服务在你的本地环境,你需要更改微服务示例解决方案项目中的 `appsettings.json` 文件.
### 打开并构建Visual Studio解决方案
* 在Visual Studio 2017 (15.9.0+)中打开`samples\MicroserviceDemo\MicroserviceDemo.sln`.

4
docs/zh-Hans/SignalR-Integration.md

@ -115,7 +115,7 @@ public class MessagingHub : Hub
}
````
`MessasingHub` 集线器的路由为 `/signalr-hubs/messasing`:
`MessagingHub` 集线器的路由为 `/signalr-hubs/messaging`:
* 添加了标准 `/signalr-hubs/` 前缀.
* 使用**驼峰命名**集线器名称,不包含 `Hub` 后缀.
@ -123,7 +123,7 @@ public class MessagingHub : Hub
如果你想指定路由,你可以使用 `HubRoute` attribute:
````csharp
[HubRoute("/my-messasing-hub")]
[HubRoute("/my-messaging-hub")]
public class MessagingHub : Hub
{
//...

2
docs/zh-Hans/Text-Templating.md

@ -4,7 +4,7 @@
ABP框架提供了一个简单有效的文本模板系统,文本模板用于动态渲染基于模板和模型(数据对象)内容:
***TEMPLATE + MODEL ==render==> RENDERED CONTENT***
Template + Model =renderer=> Rendered Content
它非常类似于 ASP.NET Core Razor View (或 Page):

2
docs/zh-Hans/Tutorials/Part-3.md

@ -23,7 +23,7 @@ end
### 关于本教程
这是ASP.NET Core{{UI_Value}}系列教程的第二章. 共有三章:
这是ASP.NET Core{{UI_Value}}系列教程的第三章. 共有三章:
- [Part-1: 创建项目和书籍列表页面](Part-1.md)
- [Part 2: 创建,编辑,删除书籍](Part-2.md)

13
docs/zh-Hans/docs-nav.json

@ -446,6 +446,15 @@
}
]
},
{
"text": "React Native",
"items": [
{
"text": "入门",
"path": "Getting-Started-React-Native.md"
}
]
},
{
"text": "通用",
"items": [
@ -624,6 +633,10 @@
{
"text": "API文档",
"path": "{ApiDocumentationUrl}"
},
{
"text": "官方包",
"path": "https://abp.io/packages"
}
]
}

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs

@ -36,6 +36,8 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
public string Value { get; set; }
public bool SuppressLabel { get; set; }
public AbpInputTagHelper(AbpInputTagHelperService tagHelperService)
: base(tagHelperService)
{

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs

@ -247,7 +247,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
protected virtual async Task<string> GetLabelAsHtmlAsync(TagHelperContext context, TagHelperOutput output, TagHelperOutput inputTag, bool isCheckbox)
{
if (IsOutputHidden(inputTag))
if (IsOutputHidden(inputTag) || TagHelper.SuppressLabel)
{
return "";
}

26
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Toolbar/LanguageSwitch/LanguageSwitchViewComponent.cs

@ -1,7 +1,9 @@
using System.Globalization;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RequestLocalization;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Localization;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Themes.Basic.Components.Toolbar.LanguageSwitch
@ -23,12 +25,32 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Themes.Basic.Components.Toolbar
CultureInfo.CurrentUICulture.Name
);
if (currentLanguage == null)
{
var abpRequestLocalizationOptionsProvider = HttpContext.RequestServices.GetRequiredService<IAbpRequestLocalizationOptionsProvider>();
var localizationOptions = await abpRequestLocalizationOptionsProvider.GetLocalizationOptionsAsync();
if (localizationOptions.DefaultRequestCulture != null)
{
currentLanguage = new LanguageInfo(
localizationOptions.DefaultRequestCulture.Culture.Name,
localizationOptions.DefaultRequestCulture.UICulture.Name,
localizationOptions.DefaultRequestCulture.UICulture.DisplayName);
}
else
{
currentLanguage = new LanguageInfo(
CultureInfo.CurrentCulture.Name,
CultureInfo.CurrentUICulture.Name,
CultureInfo.CurrentUICulture.DisplayName);
}
}
var model = new LanguageSwitchViewComponentModel
{
CurrentLanguage = currentLanguage,
OtherLanguages = languages.Where(l => l != currentLanguage).ToList()
};
return View("~/Themes/Basic/Components/Toolbar/LanguageSwitch/Default.cshtml", model);
}
}

6
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml

@ -39,8 +39,10 @@
<title>@(ViewBag.Title == null ? BrandingProvider.AppName : ViewBag.Title)</title>
<meta name="description" content="@(ViewBag.Description != null ? ViewBag.Description as string : "Login or register to check out your ABP account. You need to be logged in to to view pay statements, generate new project and manage your license.")" />
@if (ViewBag.Description != null)
{
<meta name="description" content="@(ViewBag.Description as string)" />
}
<abp-style-bundle name="@BasicThemeBundles.Styles.Global" />
@await RenderSectionAsync("styles", false)

123
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
@ -13,7 +12,6 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Volo.Abp.Application.Services;
using Volo.Abp.AspNetCore.Mvc.ApiExploring;
using Volo.Abp.AspNetCore.Mvc.Conventions;
using Volo.Abp.AspNetCore.Mvc.Utils;
using Volo.Abp.DependencyInjection;
@ -67,34 +65,51 @@ namespace Volo.Abp.AspNetCore.Mvc
private void AddApiDescriptionToModel(
ApiDescription apiDescription,
ApplicationApiDescriptionModel applicationModel,
ApplicationApiDescriptionModel applicationModel,
ApplicationApiDescriptionModelRequestDto input)
{
var controllerType = apiDescription.ActionDescriptor.AsControllerActionDescriptor().ControllerTypeInfo.AsType();
var controllerType = apiDescription
.ActionDescriptor
.AsControllerActionDescriptor()
.ControllerTypeInfo;
var setting = FindSetting(controllerType);
var moduleModel = applicationModel.GetOrAddModule(GetRootPath(controllerType, setting), GetRemoteServiceName(controllerType, setting));
var moduleModel = applicationModel.GetOrAddModule(
GetRootPath(controllerType, setting),
GetRemoteServiceName(controllerType, setting)
);
var controllerModel = moduleModel.GetOrAddController(controllerType.FullName, CalculateControllerName(controllerType, setting), controllerType, _modelOptions.IgnoredInterfaces);
var controllerModel = moduleModel.GetOrAddController(
controllerType.FullName,
CalculateControllerName(controllerType, setting),
controllerType,
_modelOptions.IgnoredInterfaces
);
var method = apiDescription.ActionDescriptor.GetMethodInfo();
var uniqueMethodName = GetUniqueActionName(method);
if (controllerModel.Actions.ContainsKey(uniqueMethodName))
{
Logger.LogWarning($"Controller '{controllerModel.ControllerName}' contains more than one action with name '{uniqueMethodName}' for module '{moduleModel.RootPath}'. Ignored: " + method);
Logger.LogWarning(
$"Controller '{controllerModel.ControllerName}' contains more than one action with name '{uniqueMethodName}' for module '{moduleModel.RootPath}'. Ignored: " +
method);
return;
}
Logger.LogDebug($"ActionApiDescriptionModel.Create: {controllerModel.ControllerName}.{uniqueMethodName}");
var actionModel = controllerModel.AddAction(uniqueMethodName, ActionApiDescriptionModel.Create(
var actionModel = controllerModel.AddAction(
uniqueMethodName,
method,
apiDescription.RelativePath,
apiDescription.HttpMethod,
GetSupportedVersions(controllerType, method, setting)
));
ActionApiDescriptionModel.Create(
uniqueMethodName,
method,
apiDescription.RelativePath,
apiDescription.HttpMethod,
GetSupportedVersions(controllerType, method, setting)
)
);
if (input.IncludeTypes)
{
@ -106,11 +121,14 @@ namespace Volo.Abp.AspNetCore.Mvc
private static string CalculateControllerName(Type controllerType, ConventionalControllerSetting setting)
{
var controllerName = controllerType.Name.RemovePostFix("Controller").RemovePostFix(ApplicationService.CommonPostfixes);
var controllerName = controllerType.Name.RemovePostFix("Controller")
.RemovePostFix(ApplicationService.CommonPostfixes);
if (setting?.UrlControllerNameNormalizer != null)
{
controllerName = setting.UrlControllerNameNormalizer(new UrlControllerNameNormalizerContext(setting.RootPath, controllerName));
controllerName =
setting.UrlControllerNameNormalizer(
new UrlControllerNameNormalizerContext(setting.RootPath, controllerName));
}
return controllerName;
@ -139,7 +157,8 @@ namespace Volo.Abp.AspNetCore.Mvc
return methodNameBuilder.ToString();
}
private static List<string> GetSupportedVersions(Type controllerType, MethodInfo method, ConventionalControllerSetting setting)
private static List<string> GetSupportedVersions(Type controllerType, MethodInfo method,
ConventionalControllerSetting setting)
{
var supportedVersions = new List<ApiVersion>();
@ -172,17 +191,23 @@ namespace Volo.Abp.AspNetCore.Mvc
AddCustomTypesToModel(applicationModel, method.ReturnType);
}
private static void AddCustomTypesToModel(ApplicationApiDescriptionModel applicationModel, [CanBeNull] Type type)
private static void AddCustomTypesToModel(ApplicationApiDescriptionModel applicationModel,
[CanBeNull] Type type)
{
if (type == null)
{
return;
}
if (type.IsGenericParameter)
{
return;
}
type = AsyncHelper.UnwrapTask(type);
if (type == typeof(object) ||
type == typeof(void) ||
type == typeof(void) ||
type == typeof(Enum) ||
type == typeof(ValueType) ||
TypeHelper.IsPrimitiveExtended(type))
@ -190,41 +215,67 @@ namespace Volo.Abp.AspNetCore.Mvc
return;
}
if (TypeHelper.IsEnumerable(type, out var itemType))
if (TypeHelper.IsDictionary(type, out var keyType, out var valueType))
{
AddCustomTypesToModel(applicationModel, itemType);
AddCustomTypesToModel(applicationModel, keyType);
AddCustomTypesToModel(applicationModel, valueType);
return;
}
if (TypeHelper.IsDictionary(type, out var keyType, out var valueType))
if (TypeHelper.IsEnumerable(type, out var itemType))
{
AddCustomTypesToModel(applicationModel, keyType);
AddCustomTypesToModel(applicationModel, valueType);
AddCustomTypesToModel(applicationModel, itemType);
return;
}
/* TODO: Add interfaces
*/
if (type.IsGenericType && !type.IsGenericTypeDefinition)
{
var genericTypeDefinition = type.GetGenericTypeDefinition();
AddCustomTypesToModel(applicationModel, genericTypeDefinition);
foreach (var genericArgument in type.GetGenericArguments())
{
AddCustomTypesToModel(applicationModel, genericArgument);
}
var typeName = TypeHelper.GetFullNameHandlingNullableAndGenerics(type);
return;
}
var typeName = CalculateTypeName(type);
if (applicationModel.Types.ContainsKey(typeName))
{
return;
}
var typeModel = TypeApiDescriptionModel.Create(type);
applicationModel.Types[typeName] = typeModel;
applicationModel.Types[typeName] = TypeApiDescriptionModel.Create(type);
AddCustomTypesToModel(applicationModel, type.BaseType);
foreach (var propertyInfo in type.GetProperties())
foreach (var propertyInfo in type.GetProperties().Where(p => p.DeclaringType == type))
{
AddCustomTypesToModel(applicationModel, propertyInfo.PropertyType);
}
}
private void AddParameterDescriptionsToModel(ActionApiDescriptionModel actionModel, MethodInfo method, ApiDescription apiDescription)
private static string CalculateTypeName(Type type)
{
if (!type.IsGenericTypeDefinition)
{
return TypeHelper.GetFullNameHandlingNullableAndGenerics(type);
}
var i = 0;
var argumentList = type
.GetGenericArguments()
.Select(_ => "T" + i++)
.JoinAsString(",");
return $"{type.FullName.Left(type.FullName.IndexOf('`'))}<{argumentList}>";
}
private void AddParameterDescriptionsToModel(ActionApiDescriptionModel actionModel, MethodInfo method,
ApiDescription apiDescription)
{
if (!apiDescription.ParameterDescriptions.Any())
{
@ -240,8 +291,8 @@ namespace Volo.Abp.AspNetCore.Mvc
{
var parameterDescription = apiDescription.ParameterDescriptions[i];
var matchedMethodParamName = matchedMethodParamNames.Length > i
? matchedMethodParamNames[i]
: parameterDescription.Name;
? matchedMethodParamNames[i]
: parameterDescription.Name;
actionModel.AddParameter(ParameterApiDescriptionModel.Create(
parameterDescription.Name,
@ -273,7 +324,8 @@ namespace Volo.Abp.AspNetCore.Mvc
return modelNameProvider.Name ?? parameterInfo.Name;
}
private static string GetRootPath([NotNull] Type controllerType, [CanBeNull] ConventionalControllerSetting setting)
private static string GetRootPath([NotNull] Type controllerType,
[CanBeNull] ConventionalControllerSetting setting)
{
if (setting != null)
{
@ -296,7 +348,8 @@ namespace Volo.Abp.AspNetCore.Mvc
return setting.RemoteServiceName;
}
var remoteServiceAttr = controllerType.GetCustomAttributes().OfType<RemoteServiceAttribute>().FirstOrDefault();
var remoteServiceAttr =
controllerType.GetCustomAttributes().OfType<RemoteServiceAttribute>().FirstOrDefault();
if (remoteServiceAttr?.Name != null)
{
return remoteServiceAttr.Name;

2
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpCachingModule.cs

@ -5,12 +5,14 @@ using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Serialization;
using Volo.Abp.Threading;
using Volo.Abp.Uow;
namespace Volo.Abp.Caching
{
[DependsOn(
typeof(AbpThreadingModule),
typeof(AbpSerializationModule),
typeof(AbpUnitOfWorkModule),
typeof(AbpMultiTenancyModule),
typeof(AbpJsonModule))]
public class AbpCachingModule : AbpModule

566
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs

@ -14,6 +14,7 @@ using Volo.Abp.DependencyInjection;
using Volo.Abp.ExceptionHandling;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Threading;
using Volo.Abp.Uow;
namespace Volo.Abp.Caching
{
@ -30,13 +31,15 @@ namespace Volo.Abp.Caching
ICancellationTokenProvider cancellationTokenProvider,
IDistributedCacheSerializer serializer,
IDistributedCacheKeyNormalizer keyNormalizer,
IHybridServiceScopeFactory serviceScopeFactory) : base(
distributedCacheOption: distributedCacheOption,
cache: cache,
cancellationTokenProvider: cancellationTokenProvider,
serializer: serializer,
keyNormalizer: keyNormalizer,
serviceScopeFactory: serviceScopeFactory)
IHybridServiceScopeFactory serviceScopeFactory,
IUnitOfWorkManager unitOfWorkManager) : base(
distributedCacheOption: distributedCacheOption,
cache: cache,
cancellationTokenProvider: cancellationTokenProvider,
serializer: serializer,
keyNormalizer: keyNormalizer,
serviceScopeFactory: serviceScopeFactory,
unitOfWorkManager:unitOfWorkManager)
{
}
}
@ -50,6 +53,8 @@ namespace Volo.Abp.Caching
public class DistributedCache<TCacheItem, TCacheKey> : IDistributedCache<TCacheItem, TCacheKey>
where TCacheItem : class
{
public const string UowCacheName = "AbpDistributedCache";
public ILogger<DistributedCache<TCacheItem, TCacheKey>> Logger { get; set; }
protected string CacheName { get; set; }
@ -66,6 +71,8 @@ namespace Volo.Abp.Caching
protected IHybridServiceScopeFactory ServiceScopeFactory { get; }
protected IUnitOfWorkManager UnitOfWorkManager { get; }
protected SemaphoreSlim SyncSemaphore { get; }
protected DistributedCacheEntryOptions DefaultCacheOptions;
@ -78,7 +85,8 @@ namespace Volo.Abp.Caching
ICancellationTokenProvider cancellationTokenProvider,
IDistributedCacheSerializer serializer,
IDistributedCacheKeyNormalizer keyNormalizer,
IHybridServiceScopeFactory serviceScopeFactory)
IHybridServiceScopeFactory serviceScopeFactory,
IUnitOfWorkManager unitOfWorkManager)
{
_distributedCacheOption = distributedCacheOption.Value;
Cache = cache;
@ -87,6 +95,7 @@ namespace Volo.Abp.Caching
Serializer = serializer;
KeyNormalizer = keyNormalizer;
ServiceScopeFactory = serviceScopeFactory;
UnitOfWorkManager = unitOfWorkManager;
SyncSemaphore = new SemaphoreSlim(1, 1);
@ -134,13 +143,24 @@ namespace Volo.Abp.Caching
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <returns>The cache item, or null.</returns>
public virtual TCacheItem Get(
TCacheKey key,
bool? hideErrors = null)
bool? hideErrors = null,
bool considerUow = false)
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
if (ShouldConsiderUow(considerUow))
{
var value = GetUnitOfWorkCache().GetOrDefault(key)?.GetUnRemovedValueOrNull();
if (value != null)
{
return value;
}
}
byte[] cachedBytes;
try
@ -163,7 +183,8 @@ namespace Volo.Abp.Caching
public virtual KeyValuePair<TCacheKey, TCacheItem>[] GetMany(
IEnumerable<TCacheKey> keys,
bool? hideErrors = null)
bool? hideErrors = null,
bool considerUow = false)
{
var keyArray = keys.ToArray();
@ -172,16 +193,39 @@ namespace Volo.Abp.Caching
{
return GetManyFallback(
keyArray,
hideErrors
hideErrors,
considerUow
);
}
var notCachedKeys = new List<TCacheKey>();
var cachedValues = new List<KeyValuePair<TCacheKey, TCacheItem>>();
if (ShouldConsiderUow(considerUow))
{
var uowCache = GetUnitOfWorkCache();
foreach (var key in keyArray)
{
var value = uowCache.GetOrDefault(key)?.GetUnRemovedValueOrNull();
if (value != null)
{
cachedValues.Add(new KeyValuePair<TCacheKey, TCacheItem>(key, value));
}
}
notCachedKeys = keyArray.Except(cachedValues.Select(x => x.Key)).ToList();
if (!notCachedKeys.Any())
{
return cachedValues.ToArray();
}
}
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
byte[][] cachedBytes;
var readKeys = notCachedKeys.Any() ? notCachedKeys.ToArray() : keyArray;
try
{
cachedBytes = cacheSupportsMultipleItems.GetMany(keyArray.Select(NormalizeKey));
cachedBytes = cacheSupportsMultipleItems.GetMany(readKeys.Select(NormalizeKey));
}
catch (Exception ex)
{
@ -193,13 +237,14 @@ namespace Volo.Abp.Caching
throw;
}
return ToCacheItems(cachedBytes, keyArray);
return cachedValues.Concat(ToCacheItems(cachedBytes, readKeys)).ToArray();
}
protected virtual KeyValuePair<TCacheKey, TCacheItem>[] GetManyFallback(
TCacheKey[] keys,
bool? hideErrors = null)
bool? hideErrors = null,
bool considerUow = false)
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
@ -208,7 +253,7 @@ namespace Volo.Abp.Caching
return keys
.Select(key => new KeyValuePair<TCacheKey, TCacheItem>(
key,
Get(key, hideErrors: false)
Get(key, false, considerUow)
)
).ToArray();
}
@ -227,6 +272,7 @@ namespace Volo.Abp.Caching
public virtual async Task<KeyValuePair<TCacheKey, TCacheItem>[]> GetManyAsync(
IEnumerable<TCacheKey> keys,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default)
{
var keyArray = keys.ToArray();
@ -237,17 +283,41 @@ namespace Volo.Abp.Caching
return await GetManyFallbackAsync(
keyArray,
hideErrors,
considerUow,
token
);
}
var notCachedKeys = new List<TCacheKey>();
var cachedValues = new List<KeyValuePair<TCacheKey, TCacheItem>>();
if (ShouldConsiderUow(considerUow))
{
var uowCache = GetUnitOfWorkCache();
foreach (var key in keyArray)
{
var value = uowCache.GetOrDefault(key)?.GetUnRemovedValueOrNull();
if (value != null)
{
cachedValues.Add(new KeyValuePair<TCacheKey, TCacheItem>(key, value));
}
}
notCachedKeys = keyArray.Except(cachedValues.Select(x => x.Key)).ToList();
if (!notCachedKeys.Any())
{
return cachedValues.ToArray();
}
}
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
byte[][] cachedBytes;
var readKeys = notCachedKeys.Any() ? notCachedKeys.ToArray() : keyArray;
try
{
cachedBytes = await cacheSupportsMultipleItems.GetManyAsync(
keyArray.Select(NormalizeKey),
readKeys.Select(NormalizeKey),
CancellationTokenProvider.FallbackToProvider(token)
);
}
@ -261,13 +331,14 @@ namespace Volo.Abp.Caching
throw;
}
return ToCacheItems(cachedBytes, keyArray);
return cachedValues.Concat(ToCacheItems(cachedBytes, readKeys)).ToArray();
}
protected virtual async Task<KeyValuePair<TCacheKey, TCacheItem>[]> GetManyFallbackAsync(
TCacheKey[] keys,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default)
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
@ -280,7 +351,7 @@ namespace Volo.Abp.Caching
{
result.Add(new KeyValuePair<TCacheKey, TCacheItem>(
key,
await GetAsync(key, false, token))
await GetAsync(key, false, considerUow, token: token))
);
}
@ -303,15 +374,26 @@ namespace Volo.Abp.Caching
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The cache item, or null.</returns>
public virtual async Task<TCacheItem> GetAsync(
TCacheKey key,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default)
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
if (ShouldConsiderUow(considerUow))
{
var value = GetUnitOfWorkCache().GetOrDefault(key)?.GetUnRemovedValueOrNull();
if (value != null)
{
return value;
}
}
byte[] cachedBytes;
try
@ -348,14 +430,16 @@ namespace Volo.Abp.Caching
/// <param name="factory">The factory delegate is used to provide the cache item when no cache item is found for the given <paramref name="key" />.</param>
/// <param name="optionsFactory">The cache options for the factory delegate.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <returns>The cache item.</returns>
public virtual TCacheItem GetOrAdd(
TCacheKey key,
Func<TCacheItem> factory,
Func<DistributedCacheEntryOptions> optionsFactory = null,
bool? hideErrors = null)
bool? hideErrors = null,
bool considerUow = false)
{
var value = Get(key, hideErrors);
var value = Get(key, hideErrors, considerUow);
if (value != null)
{
return value;
@ -363,14 +447,28 @@ namespace Volo.Abp.Caching
using (SyncSemaphore.Lock())
{
value = Get(key, hideErrors);
value = Get(key, hideErrors, considerUow);
if (value != null)
{
return value;
}
value = factory();
Set(key, value, optionsFactory?.Invoke(), hideErrors);
if (ShouldConsiderUow(considerUow))
{
var uowCache = GetUnitOfWorkCache();
if (uowCache.TryGetValue(key, out var item))
{
item.SetValue(value);
}
else
{
uowCache.Add(key, new UnitOfWorkCacheItem<TCacheItem>(value));
}
}
Set(key, value, optionsFactory?.Invoke(), hideErrors, considerUow);
}
return value;
@ -384,6 +482,7 @@ namespace Volo.Abp.Caching
/// <param name="factory">The factory delegate is used to provide the cache item when no cache item is found for the given <paramref name="key" />.</param>
/// <param name="optionsFactory">The cache options for the factory delegate.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The cache item.</returns>
public virtual async Task<TCacheItem> GetOrAddAsync(
@ -391,10 +490,11 @@ namespace Volo.Abp.Caching
Func<Task<TCacheItem>> factory,
Func<DistributedCacheEntryOptions> optionsFactory = null,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default)
{
token = CancellationTokenProvider.FallbackToProvider(token);
var value = await GetAsync(key, hideErrors, token);
var value = await GetAsync(key, hideErrors, considerUow, token);
if (value != null)
{
return value;
@ -402,14 +502,28 @@ namespace Volo.Abp.Caching
using (await SyncSemaphore.LockAsync(token))
{
value = await GetAsync(key, hideErrors, token);
value = await GetAsync(key, hideErrors, considerUow, token);
if (value != null)
{
return value;
}
value = await factory();
await SetAsync(key, value, optionsFactory?.Invoke(), hideErrors, token);
if (ShouldConsiderUow(considerUow))
{
var uowCache = GetUnitOfWorkCache();
if (uowCache.TryGetValue(key, out var item))
{
item.SetValue(value);
}
else
{
uowCache.Add(key, new UnitOfWorkCacheItem<TCacheItem>(value));
}
}
await SetAsync(key, value, optionsFactory?.Invoke(), hideErrors, considerUow, token);
}
return value;
@ -422,34 +536,62 @@ namespace Volo.Abp.Caching
/// <param name="value">The cache item value to set in the cache.</param>
/// <param name="options">The cache options for the value.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
public virtual void Set(
TCacheKey key,
TCacheItem value,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null)
bool? hideErrors = null,
bool considerUow = false)
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
void SetRealCache()
{
Cache.Set(
NormalizeKey(key),
Serializer.Serialize(value),
options ?? DefaultCacheOptions
);
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
Cache.Set(
NormalizeKey(key),
Serializer.Serialize(value),
options ?? DefaultCacheOptions
);
}
catch (Exception ex)
{
if (hideErrors == true)
{
HandleException(ex);
return;
}
throw;
}
}
catch (Exception ex)
if (ShouldConsiderUow(considerUow))
{
if (hideErrors == true)
var uowCache = GetUnitOfWorkCache();
if (uowCache.TryGetValue(key, out _))
{
HandleException(ex);
return;
uowCache[key].SetValue(value);
}
else
{
uowCache.Add(key, new UnitOfWorkCacheItem<TCacheItem>(value));
}
throw;
// ReSharper disable once PossibleNullReferenceException
UnitOfWorkManager.Current.OnCompleted(() =>
{
SetRealCache();
return Task.CompletedTask;
});
}
else
{
SetRealCache();
}
}
/// <summary>
/// Sets the cache item value for the provided key.
/// </summary>
@ -457,6 +599,7 @@ namespace Volo.Abp.Caching
/// <param name="value">The cache item value to set in the cache.</param>
/// <param name="options">The cache options for the value.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The <see cref="T:System.Threading.Tasks.Task" /> indicating that the operation is asynchronous.</returns>
public virtual async Task SetAsync(
@ -464,35 +607,60 @@ namespace Volo.Abp.Caching
TCacheItem value,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default)
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
async Task SetRealCache()
{
await Cache.SetAsync(
NormalizeKey(key),
Serializer.Serialize(value),
options ?? DefaultCacheOptions,
CancellationTokenProvider.FallbackToProvider(token)
);
}
catch (Exception ex)
{
if (hideErrors == true)
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
await HandleExceptionAsync(ex);
return;
await Cache.SetAsync(
NormalizeKey(key),
Serializer.Serialize(value),
options ?? DefaultCacheOptions,
CancellationTokenProvider.FallbackToProvider(token)
);
}
catch (Exception ex)
{
if (hideErrors == true)
{
await HandleExceptionAsync(ex);
return;
}
throw;
throw;
}
}
if (ShouldConsiderUow(considerUow))
{
var uowCache = GetUnitOfWorkCache();
if (uowCache.TryGetValue(key, out _))
{
uowCache[key].SetValue(value);
}
else
{
uowCache.Add(key, new UnitOfWorkCacheItem<TCacheItem>(value));
}
// ReSharper disable once PossibleNullReferenceException
UnitOfWorkManager.Current.OnCompleted(SetRealCache);
}
else
{
await SetRealCache();
}
}
public void SetMany(
IEnumerable<KeyValuePair<TCacheKey, TCacheItem>> items,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null)
bool? hideErrors = null,
bool considerUow = false)
{
var itemsArray = items.ToArray();
@ -502,40 +670,73 @@ namespace Volo.Abp.Caching
SetManyFallback(
itemsArray,
options,
hideErrors
hideErrors,
considerUow
);
return;
}
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
void SetRealCache()
{
cacheSupportsMultipleItems.SetMany(
ToRawCacheItems(itemsArray),
options ?? DefaultCacheOptions
);
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
cacheSupportsMultipleItems.SetMany(
ToRawCacheItems(itemsArray),
options ?? DefaultCacheOptions
);
}
catch (Exception ex)
{
if (hideErrors == true)
{
HandleException(ex);
return;
}
throw;
}
}
catch (Exception ex)
if (ShouldConsiderUow(considerUow))
{
if (hideErrors == true)
var uowCache = GetUnitOfWorkCache();
foreach (var pair in itemsArray)
{
HandleException(ex);
return;
if (uowCache.TryGetValue(pair.Key, out _))
{
uowCache[pair.Key].SetValue(pair.Value);
}
else
{
uowCache.Add(pair.Key, new UnitOfWorkCacheItem<TCacheItem>(pair.Value));
}
}
throw;
// ReSharper disable once PossibleNullReferenceException
UnitOfWorkManager.Current.OnCompleted(() =>
{
SetRealCache();
return Task.CompletedTask;
});
}
else
{
SetRealCache();
}
}
protected virtual void SetManyFallback(
KeyValuePair<TCacheKey, TCacheItem>[] items,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null)
bool? hideErrors = null,
bool considerUow = false)
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
foreach (var item in items)
@ -543,8 +744,9 @@ namespace Volo.Abp.Caching
Set(
item.Key,
item.Value,
options: options,
hideErrors: false
options,
false,
considerUow
);
}
}
@ -564,6 +766,7 @@ namespace Volo.Abp.Caching
IEnumerable<KeyValuePair<TCacheKey, TCacheItem>> items,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default)
{
var itemsArray = items.ToArray();
@ -575,38 +778,67 @@ namespace Volo.Abp.Caching
itemsArray,
options,
hideErrors,
considerUow,
token
);
return;
}
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
async Task SetRealCache()
{
await cacheSupportsMultipleItems.SetManyAsync(
ToRawCacheItems(itemsArray),
options ?? DefaultCacheOptions,
CancellationTokenProvider.FallbackToProvider(token)
);
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
await cacheSupportsMultipleItems.SetManyAsync(
ToRawCacheItems(itemsArray),
options ?? DefaultCacheOptions,
CancellationTokenProvider.FallbackToProvider(token)
);
}
catch (Exception ex)
{
if (hideErrors == true)
{
await HandleExceptionAsync(ex);
return;
}
throw;
}
}
catch (Exception ex)
if (ShouldConsiderUow(considerUow))
{
if (hideErrors == true)
var uowCache = GetUnitOfWorkCache();
foreach (var pair in itemsArray)
{
await HandleExceptionAsync(ex);
return;
if (uowCache.TryGetValue(pair.Key, out _))
{
uowCache[pair.Key].SetValue(pair.Value);
}
else
{
uowCache.Add(pair.Key, new UnitOfWorkCacheItem<TCacheItem>(pair.Value));
}
}
throw;
// ReSharper disable once PossibleNullReferenceException
UnitOfWorkManager.Current.OnCompleted(SetRealCache);
}
else
{
await SetRealCache();
}
}
protected virtual async Task SetManyFallbackAsync(
KeyValuePair<TCacheKey, TCacheItem>[] items,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default)
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
@ -618,8 +850,9 @@ namespace Volo.Abp.Caching
await SetAsync(
item.Key,
item.Value,
options: options,
hideErrors: false,
options,
false,
considerUow,
token: token
);
}
@ -636,9 +869,14 @@ namespace Volo.Abp.Caching
}
}
/// <summary>
/// Refreshes the cache value of the given key, and resets its sliding expiration timeout.
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
public virtual void Refresh(
TCacheKey key, bool?
hideErrors = null)
TCacheKey key,
bool? hideErrors = null)
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
@ -658,6 +896,13 @@ namespace Volo.Abp.Caching
}
}
/// <summary>
/// Refreshes the cache value of the given key, and resets its sliding expiration timeout.
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The <see cref="T:System.Threading.Tasks.Task" /> indicating that the operation is asynchronous.</returns>
public virtual async Task RefreshAsync(
TCacheKey key,
bool? hideErrors = null,
@ -681,48 +926,106 @@ namespace Volo.Abp.Caching
}
}
/// <summary>
/// Removes the cache item for given key from cache.
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
public virtual void Remove(
TCacheKey key,
bool? hideErrors = null)
bool? hideErrors = null,
bool considerUow = false)
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
void RemoveRealCache()
{
Cache.Remove(NormalizeKey(key));
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
Cache.Remove(NormalizeKey(key));
}
catch (Exception ex)
{
if (hideErrors == true)
{
HandleException(ex);
return;
}
throw;
}
}
catch (Exception ex)
if (ShouldConsiderUow(considerUow))
{
if (hideErrors == true)
var uowCache = GetUnitOfWorkCache();
if (uowCache.TryGetValue(key, out _))
{
HandleException(ex);
return;
uowCache[key].RemoveValue();
}
throw;
// ReSharper disable once PossibleNullReferenceException
UnitOfWorkManager.Current.OnCompleted(() =>
{
RemoveRealCache();
return Task.CompletedTask;
});
}
else
{
RemoveRealCache();
}
}
/// <summary>
/// Removes the cache item for given key from cache.
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The <see cref="T:System.Threading.Tasks.Task" /> indicating that the operation is asynchronous.</returns>
public virtual async Task RemoveAsync(
TCacheKey key,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default)
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
async Task RemoveRealCache()
{
await Cache.RemoveAsync(NormalizeKey(key), CancellationTokenProvider.FallbackToProvider(token));
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
await Cache.RemoveAsync(NormalizeKey(key), CancellationTokenProvider.FallbackToProvider(token));
}
catch (Exception ex)
{
if (hideErrors == true)
{
await HandleExceptionAsync(ex);
return;
}
throw;
}
}
catch (Exception ex)
if (ShouldConsiderUow(considerUow))
{
if (hideErrors == true)
var uowCache = GetUnitOfWorkCache();
if (uowCache.TryGetValue(key, out _))
{
await HandleExceptionAsync(ex);
return;
uowCache[key].RemoveValue();
}
throw;
// ReSharper disable once PossibleNullReferenceException
UnitOfWorkManager.Current.OnCompleted(RemoveRealCache);
}
else
{
await RemoveRealCache();
}
}
@ -730,7 +1033,7 @@ namespace Volo.Abp.Caching
{
AsyncHelper.RunSync(() => HandleExceptionAsync(ex));
}
protected virtual async Task HandleExceptionAsync(Exception ex)
{
Logger.LogException(ex, LogLevel.Warning);
@ -742,7 +1045,7 @@ namespace Volo.Abp.Caching
.NotifyAsync(new ExceptionNotificationContext(ex, LogLevel.Warning));
}
}
protected virtual KeyValuePair<TCacheKey, TCacheItem>[] ToCacheItems(byte[][] itemBytes, TCacheKey[] itemKeys)
{
if (itemBytes.Length != itemKeys.Length)
@ -764,7 +1067,7 @@ namespace Volo.Abp.Caching
return result.ToArray();
}
[CanBeNull]
protected virtual TCacheItem ToCacheItem([CanBeNull] byte[] bytes)
{
@ -786,12 +1089,33 @@ namespace Volo.Abp.Caching
)
).ToArray();
}
private static KeyValuePair<TCacheKey, TCacheItem>[] ToCacheItemsWithDefaultValues(TCacheKey[] keys)
{
return keys
.Select(key => new KeyValuePair<TCacheKey, TCacheItem>(key, default))
.ToArray();
}
protected virtual bool ShouldConsiderUow(bool considerUow)
{
return considerUow && UnitOfWorkManager.Current != null;
}
protected virtual string GetUnitOfWorkCacheKey()
{
return UowCacheName + CacheName;
}
protected virtual Dictionary<TCacheKey, UnitOfWorkCacheItem<TCacheItem>> GetUnitOfWorkCache()
{
if (UnitOfWorkManager.Current == null)
{
throw new AbpException($"There is no active UOW.");
}
return UnitOfWorkManager.Current.GetOrAddItem(GetUnitOfWorkCacheKey(),
key => new Dictionary<TCacheKey, UnitOfWorkCacheItem<TCacheItem>>());
}
}
}
}

46
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs

@ -30,42 +30,48 @@ namespace Volo.Abp.Caching
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <returns>The cache item, or null.</returns>
TCacheItem Get(
TCacheKey key,
bool? hideErrors = null
bool? hideErrors = null,
bool considerUow = false
);
/// <summary>
/// Gets multiple cache items with the given keys.
///
/// The returned list contains exactly the same count of items specified in the given keys.
/// An item in the return list can not be null, but an item in the list has null value
/// if the related key not found in the cache.
/// if the related key not found in the cache.
/// </summary>
/// <param name="keys">The keys of cached items to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <returns>List of cache items.</returns>
KeyValuePair<TCacheKey, TCacheItem>[] GetMany(
IEnumerable<TCacheKey> keys,
bool? hideErrors = null
bool? hideErrors = null,
bool considerUow = false
);
/// <summary>
/// Gets multiple cache items with the given keys.
///
/// The returned list contains exactly the same count of items specified in the given keys.
/// An item in the return list can not be null, but an item in the list has null value
/// if the related key not found in the cache.
///
///
/// </summary>
/// <param name="keys">The keys of cached items to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// /// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>List of cache items.</returns>
Task<KeyValuePair<TCacheKey, TCacheItem>[]> GetManyAsync(
IEnumerable<TCacheKey> keys,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default
);
@ -74,11 +80,13 @@ namespace Volo.Abp.Caching
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The cache item, or null.</returns>
Task<TCacheItem> GetAsync(
[NotNull] TCacheKey key,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default
);
@ -90,12 +98,14 @@ namespace Volo.Abp.Caching
/// <param name="factory">The factory delegate is used to provide the cache item when no cache item is found for the given <paramref name="key" />.</param>
/// <param name="optionsFactory">The cache options for the factory delegate.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <returns>The cache item.</returns>
TCacheItem GetOrAdd(
TCacheKey key,
Func<TCacheItem> factory,
Func<DistributedCacheEntryOptions> optionsFactory = null,
bool? hideErrors = null
bool? hideErrors = null,
bool considerUow = false
);
/// <summary>
@ -106,6 +116,7 @@ namespace Volo.Abp.Caching
/// <param name="factory">The factory delegate is used to provide the cache item when no cache item is found for the given <paramref name="key" />.</param>
/// <param name="optionsFactory">The cache options for the factory delegate.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The cache item.</returns>
Task<TCacheItem> GetOrAddAsync(
@ -113,6 +124,7 @@ namespace Volo.Abp.Caching
Func<Task<TCacheItem>> factory,
Func<DistributedCacheEntryOptions> optionsFactory = null,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default
);
@ -123,11 +135,13 @@ namespace Volo.Abp.Caching
/// <param name="value">The cache item value to set in the cache.</param>
/// <param name="options">The cache options for the value.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
void Set(
TCacheKey key,
TCacheItem value,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null
bool? hideErrors = null,
bool considerUow = false
);
/// <summary>
@ -137,6 +151,7 @@ namespace Volo.Abp.Caching
/// <param name="value">The cache item value to set in the cache.</param>
/// <param name="options">The cache options for the value.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The <see cref="T:System.Threading.Tasks.Task" /> indicating that the operation is asynchronous.</returns>
Task SetAsync(
@ -144,6 +159,7 @@ namespace Volo.Abp.Caching
[NotNull] TCacheItem value,
[CanBeNull] DistributedCacheEntryOptions options = null,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default
);
@ -154,12 +170,14 @@ namespace Volo.Abp.Caching
/// <param name="items">Items to set on the cache</param>
/// <param name="options">The cache options for the value.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
void SetMany(
IEnumerable<KeyValuePair<TCacheKey, TCacheItem>> items,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null
bool? hideErrors = null,
bool considerUow = false
);
/// <summary>
/// Sets multiple cache items.
/// Based on the implementation, this can be more efficient than setting multiple items individually.
@ -167,12 +185,14 @@ namespace Volo.Abp.Caching
/// <param name="items">Items to set on the cache</param>
/// <param name="options">The cache options for the value.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The <see cref="T:System.Threading.Tasks.Task" /> indicating that the operation is asynchronous.</returns>
Task SetManyAsync(
IEnumerable<KeyValuePair<TCacheKey, TCacheItem>> items,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default
);
@ -204,9 +224,11 @@ namespace Volo.Abp.Caching
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
void Remove(
TCacheKey key,
bool? hideErrors = null
bool? hideErrors = null,
bool considerUow = false
);
/// <summary>
@ -214,11 +236,13 @@ namespace Volo.Abp.Caching
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.</param>
/// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The <see cref="T:System.Threading.Tasks.Task" /> indicating that the operation is asynchronous.</returns>
Task RemoveAsync(
TCacheKey key,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default
);
}

43
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/UnitOfWorkCacheItem.cs

@ -0,0 +1,43 @@
using System;
namespace Volo.Abp.Caching
{
[Serializable]
public class UnitOfWorkCacheItem<TValue>
where TValue : class
{
public bool IsRemoved { get; set; }
public TValue Value { get; set; }
public UnitOfWorkCacheItem()
{
}
public UnitOfWorkCacheItem(TValue value)
{
Value = value;
}
public UnitOfWorkCacheItem(TValue value, bool isRemoved)
{
Value = value;
IsRemoved = isRemoved;
}
public UnitOfWorkCacheItem<TValue> SetValue(TValue value)
{
Value = value;
IsRemoved = false;
return this;
}
public UnitOfWorkCacheItem<TValue> RemoveValue()
{
Value = null;
IsRemoved = true;
return this;
}
}
}

11
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/UnitOfWorkCacheItemExtensions.cs

@ -0,0 +1,11 @@
namespace Volo.Abp.Caching
{
public static class UnitOfWorkCacheItemExtensions
{
public static TValue GetUnRemovedValueOrNull<TValue>(this UnitOfWorkCacheItem<TValue> item)
where TValue : class
{
return item != null && !item.IsRemoved ? item.Value : null;
}
}
}

10
framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeHelper.cs

@ -71,7 +71,7 @@ namespace Volo.Abp.Reflection
return true;
}
if (includeNullables && IsNullable(type))
if (includeNullables && IsNullable(type) && type.GenericTypeArguments.Any())
{
return IsPrimitiveExtendedInternal(type.GenericTypeArguments[0], includeEnums);
}
@ -197,7 +197,7 @@ namespace Volo.Abp.Reflection
return $"{genericType.FullName.Left(genericType.FullName.IndexOf('`'))}<{type.GenericTypeArguments.Select(GetFullNameHandlingNullableAndGenerics).JoinAsString(",")}>";
}
return type.FullName;
return type.FullName ?? type.Name;
}
public static string GetSimplifiedName([NotNull] Type type)
@ -295,8 +295,12 @@ namespace Volo.Abp.Reflection
{
return "number";
}
else if (type == typeof(object))
{
return "object";
}
return type.FullName;
return type.FullName ?? type.Name;
}
public static object ConvertFromString<TTargetType>(string value)

39
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyCrudAppService.cs

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Domain.Entities;
@ -51,6 +52,11 @@ namespace Volo.Abp.Application.Services
}
protected override Task<TEntityDto> MapToGetListOutputDtoAsync(TEntity entity)
{
return MapToGetOutputDtoAsync(entity);
}
protected override TEntityDto MapToGetListOutputDto(TEntity entity)
{
return MapToGetOutputDto(entity);
@ -80,13 +86,13 @@ namespace Volo.Abp.Application.Services
{
await CheckCreatePolicyAsync();
var entity = MapToEntity(input);
var entity = await MapToEntityAsync(input);
TryToSetTenantId(entity);
await Repository.InsertAsync(entity, autoSave: true);
return MapToGetOutputDto(entity);
return await MapToGetOutputDtoAsync(entity);
}
public virtual async Task<TGetOutputDto> UpdateAsync(TKey id, TUpdateInput input)
@ -95,10 +101,10 @@ namespace Volo.Abp.Application.Services
var entity = await GetEntityByIdAsync(id);
//TODO: Check if input has id different than given id and normalize if it's default value, throw ex otherwise
MapToEntity(input, entity);
await MapToEntityAsync(input, entity);
await Repository.UpdateAsync(entity, autoSave: true);
return MapToGetOutputDto(entity);
return await MapToGetOutputDtoAsync(entity);
}
public virtual async Task DeleteAsync(TKey id)
@ -125,6 +131,17 @@ namespace Volo.Abp.Application.Services
await CheckPolicyAsync(DeletePolicyName);
}
/// <summary>
/// Maps <see cref="TCreateInput"/> to <see cref="TEntity"/> to create a new entity.
/// It uses <see cref="MapToEntity(TCreateInput)"/> by default.
/// It can be overriden for custom mapping.
/// Overriding this has higher priority than overriding the <see cref="MapToEntity(TCreateInput)"/>
/// </summary>
protected virtual Task<TEntity> MapToEntityAsync(TCreateInput createInput)
{
return Task.FromResult(MapToEntity(createInput));
}
/// <summary>
/// Maps <see cref="TCreateInput"/> to <see cref="TEntity"/> to create a new entity.
/// It uses <see cref="IObjectMapper"/> by default.
@ -153,6 +170,18 @@ namespace Volo.Abp.Application.Services
}
}
/// <summary>
/// Maps <see cref="TUpdateInput"/> to <see cref="TEntity"/> to update the entity.
/// It uses <see cref="MapToEntity(TUpdateInput, TEntity)"/> by default.
/// It can be overriden for custom mapping.
/// Overriding this has higher priority than overriding the <see cref="MapToEntity(TUpdateInput, TEntity)"/>
/// </summary>
protected virtual Task MapToEntityAsync(TUpdateInput updateInput, TEntity entity)
{
MapToEntity(updateInput, entity);
return Task.CompletedTask;
}
/// <summary>
/// Maps <see cref="TUpdateInput"/> to <see cref="TEntity"/> to update the entity.
/// It uses <see cref="IObjectMapper"/> by default.
@ -190,4 +219,4 @@ namespace Volo.Abp.Application.Services
return entity.GetType().GetProperty(nameof(IMultiTenant.TenantId)) != null;
}
}
}
}

50
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyReadOnlyAppService.cs

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
@ -6,6 +7,7 @@ using Volo.Abp.Application.Dtos;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.ObjectMapping;
namespace Volo.Abp.Application.Services
{
@ -52,7 +54,8 @@ namespace Volo.Abp.Application.Services
await CheckGetPolicyAsync();
var entity = await GetEntityByIdAsync(id);
return MapToGetOutputDto(entity);
return await MapToGetOutputDtoAsync(entity);
}
public virtual async Task<PagedResultDto<TGetListOutputDto>> GetListAsync(TGetListInput input)
@ -67,10 +70,11 @@ namespace Volo.Abp.Application.Services
query = ApplyPaging(query, input);
var entities = await AsyncExecuter.ToListAsync(query);
var entityDtos = await MapToGetListOutputDtosAsync(entities);
return new PagedResultDto<TGetListOutputDto>(
totalCount,
entities.Select(MapToGetListOutputDto).ToList()
entityDtos
);
}
@ -118,9 +122,9 @@ namespace Volo.Abp.Application.Services
/// <param name="query">The query.</param>
protected virtual IQueryable<TEntity> ApplyDefaultSorting(IQueryable<TEntity> query)
{
if (typeof(TEntity).IsAssignableTo<ICreationAuditedObject>())
if (typeof(TEntity).IsAssignableTo<IHasCreationTime>())
{
return query.OrderByDescending(e => ((ICreationAuditedObject)e).CreationTime);
return query.OrderByDescending(e => ((IHasCreationTime)e).CreationTime);
}
throw new AbpException("No sorting specified but this query requires sorting. Override the ApplyDefaultSorting method for your application service derived from AbstractKeyReadOnlyAppService!");
@ -161,6 +165,17 @@ namespace Volo.Abp.Application.Services
return ReadOnlyRepository;
}
/// <summary>
/// Maps <see cref="TEntity"/> to <see cref="TGetOutputDto"/>.
/// It internally calls the <see cref="MapToGetOutputDto"/> by default.
/// It can be overriden for custom mapping.
/// Overriding this has higher priority than overriding the <see cref="MapToGetOutputDto"/>
/// </summary>
protected virtual Task<TGetOutputDto> MapToGetOutputDtoAsync(TEntity entity)
{
return Task.FromResult(MapToGetOutputDto(entity));
}
/// <summary>
/// Maps <see cref="TEntity"/> to <see cref="TGetOutputDto"/>.
/// It uses <see cref="IObjectMapper"/> by default.
@ -171,6 +186,33 @@ namespace Volo.Abp.Application.Services
return ObjectMapper.Map<TEntity, TGetOutputDto>(entity);
}
/// <summary>
/// Maps a list of <see cref="TEntity"/> to <see cref="TGetListOutputDto"/> objects.
/// It uses <see cref="MapToGetListOutputDtoAsync"/> method for each item in the list.
/// </summary>
protected virtual async Task<List<TGetListOutputDto>> MapToGetListOutputDtosAsync(List<TEntity> entities)
{
var dtos = new List<TGetListOutputDto>();
foreach (var entity in entities)
{
dtos.Add(await MapToGetListOutputDtoAsync(entity));
}
return dtos;
}
/// <summary>
/// Maps <see cref="TEntity"/> to <see cref="TGetListOutputDto"/>.
/// It internally calls the <see cref="MapToGetListOutputDto"/> by default.
/// It can be overriden for custom mapping.
/// Overriding this has higher priority than overriding the <see cref="MapToGetListOutputDto"/>
/// </summary>
protected virtual Task<TGetListOutputDto> MapToGetListOutputDtoAsync(TEntity entity)
{
return Task.FromResult(MapToGetListOutputDto(entity));
}
/// <summary>
/// Maps <see cref="TEntity"/> to <see cref="TGetListOutputDto"/>.
/// It uses <see cref="IObjectMapper"/> by default.

9
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/CrudAppService.cs

@ -55,6 +55,11 @@ namespace Volo.Abp.Application.Services
}
protected override Task<TEntityDto> MapToGetListOutputDtoAsync(TEntity entity)
{
return base.MapToGetOutputDtoAsync(entity);
}
protected override TEntityDto MapToGetListOutputDto(TEntity entity)
{
return MapToGetOutputDto(entity);
@ -97,9 +102,9 @@ namespace Volo.Abp.Application.Services
protected override IQueryable<TEntity> ApplyDefaultSorting(IQueryable<TEntity> query)
{
if (typeof(TEntity).IsAssignableTo<ICreationAuditedObject>())
if (typeof(TEntity).IsAssignableTo<IHasCreationTime>())
{
return query.OrderByDescending(e => ((ICreationAuditedObject)e).CreationTime);
return query.OrderByDescending(e => ((IHasCreationTime)e).CreationTime);
}
else
{

14
framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs

@ -9,6 +9,7 @@ using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.MultiTenancy;
using Volo.Abp.RabbitMQ;
using Volo.Abp.Threading;
@ -26,7 +27,7 @@ namespace Volo.Abp.EventBus.RabbitMq
protected AbpDistributedEventBusOptions AbpDistributedEventBusOptions { get; }
protected IConnectionPool ConnectionPool { get; }
protected IRabbitMqSerializer Serializer { get; }
//TODO: Accessing to the List<IEventHandlerFactory> may not be thread-safe!
protected ConcurrentDictionary<Type, List<IEventHandlerFactory>> HandlerFactories { get; }
protected ConcurrentDictionary<string, Type> EventTypes { get; }
@ -37,17 +38,18 @@ namespace Volo.Abp.EventBus.RabbitMq
IOptions<AbpRabbitMqEventBusOptions> options,
IConnectionPool connectionPool,
IRabbitMqSerializer serializer,
IServiceScopeFactory serviceScopeFactory,
IServiceScopeFactory serviceScopeFactory,
IOptions<AbpDistributedEventBusOptions> distributedEventBusOptions,
IRabbitMqMessageConsumerFactory messageConsumerFactory)
: base(serviceScopeFactory)
IRabbitMqMessageConsumerFactory messageConsumerFactory,
ICurrentTenant currentTenant)
: base(serviceScopeFactory, currentTenant)
{
ConnectionPool = connectionPool;
Serializer = serializer;
MessageConsumerFactory = messageConsumerFactory;
AbpDistributedEventBusOptions = distributedEventBusOptions.Value;
AbpRabbitMqEventBusOptions = options.Value;
HandlerFactories = new ConcurrentDictionary<Type, List<IEventHandlerFactory>>();
EventTypes = new ConcurrentDictionary<string, Type>();
}
@ -178,7 +180,7 @@ namespace Volo.Abp.EventBus.RabbitMq
"direct",
durable: true
);
var properties = channel.CreateBasicProperties();
properties.DeliveryMode = RabbitMqConsts.DeliveryModes.Persistent;

1
framework/src/Volo.Abp.EventBus/Volo.Abp.EventBus.csproj

@ -16,6 +16,7 @@
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" />
<ProjectReference Include="..\Volo.Abp.MultiTenancy\Volo.Abp.MultiTenancy.csproj" />
</ItemGroup>
</Project>

2
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs

@ -4,10 +4,12 @@ using System.Collections.Generic;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.EventBus.Local;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Reflection;
namespace Volo.Abp.EventBus
{
[DependsOn(typeof(AbpMultiTenancyModule))]
public class AbpEventBusModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)

69
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs

@ -8,6 +8,7 @@ using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Collections;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Reflection;
namespace Volo.Abp.EventBus
@ -16,9 +17,12 @@ namespace Volo.Abp.EventBus
{
protected IServiceScopeFactory ServiceScopeFactory { get; }
protected EventBusBase(IServiceScopeFactory serviceScopeFactory)
protected ICurrentTenant CurrentTenant { get; }
protected EventBusBase(IServiceScopeFactory serviceScopeFactory, ICurrentTenant currentTenant)
{
ServiceScopeFactory = serviceScopeFactory;
CurrentTenant = currentTenant;
}
/// <inheritdoc/>
@ -162,31 +166,34 @@ namespace Volo.Abp.EventBus
{
var handlerType = eventHandlerWrapper.EventHandler.GetType();
if (ReflectionHelper.IsAssignableToGenericType(handlerType, typeof(ILocalEventHandler<>)))
{
var method = typeof(ILocalEventHandler<>)
.MakeGenericType(eventType)
.GetMethod(
nameof(ILocalEventHandler<object>.HandleEventAsync),
new[] { eventType }
);
await ((Task)method.Invoke(eventHandlerWrapper.EventHandler, new[] { eventData }));
}
else if (ReflectionHelper.IsAssignableToGenericType(handlerType, typeof(IDistributedEventHandler<>)))
{
var method = typeof(IDistributedEventHandler<>)
.MakeGenericType(eventType)
.GetMethod(
nameof(IDistributedEventHandler<object>.HandleEventAsync),
new[] { eventType }
);
await ((Task)method.Invoke(eventHandlerWrapper.EventHandler, new[] { eventData }));
}
else
using (CurrentTenant.Change(GetEventDataTenantId(eventData)))
{
throw new AbpException("The object instance is not an event handler. Object type: " + handlerType.AssemblyQualifiedName);
if (ReflectionHelper.IsAssignableToGenericType(handlerType, typeof(ILocalEventHandler<>)))
{
var method = typeof(ILocalEventHandler<>)
.MakeGenericType(eventType)
.GetMethod(
nameof(ILocalEventHandler<object>.HandleEventAsync),
new[] { eventType }
);
await ((Task)method.Invoke(eventHandlerWrapper.EventHandler, new[] { eventData }));
}
else if (ReflectionHelper.IsAssignableToGenericType(handlerType, typeof(IDistributedEventHandler<>)))
{
var method = typeof(IDistributedEventHandler<>)
.MakeGenericType(eventType)
.GetMethod(
nameof(IDistributedEventHandler<object>.HandleEventAsync),
new[] { eventType }
);
await ((Task)method.Invoke(eventHandlerWrapper.EventHandler, new[] { eventData }));
}
else
{
throw new AbpException("The object instance is not an event handler. Object type: " + handlerType.AssemblyQualifiedName);
}
}
}
catch (TargetInvocationException ex)
@ -200,6 +207,16 @@ namespace Volo.Abp.EventBus
}
}
protected virtual Guid? GetEventDataTenantId(object eventData)
{
return eventData switch
{
IMultiTenant multiTenantEventData => multiTenantEventData.TenantId,
IEventDataMayHaveTenantId eventDataMayHaveTenantId when eventDataMayHaveTenantId.IsMultiTenant(out var tenantId) => tenantId,
_ => CurrentTenant.Id
};
}
protected class EventTypeWithEventHandlerFactories
{
public Type EventType { get; }
@ -246,4 +263,4 @@ namespace Volo.Abp.EventBus
}
}
}
}
}

8
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Local/LocalEventBus.cs

@ -8,6 +8,7 @@ using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Threading;
namespace Volo.Abp.EventBus.Local
@ -29,8 +30,9 @@ namespace Volo.Abp.EventBus.Local
public LocalEventBus(
IOptions<AbpLocalEventBusOptions> options,
IServiceScopeFactory serviceScopeFactory)
: base(serviceScopeFactory)
IServiceScopeFactory serviceScopeFactory,
ICurrentTenant currentTenant)
: base(serviceScopeFactory, currentTenant)
{
Options = options.Value;
Logger = NullLogger<LocalEventBus>.Instance;
@ -166,4 +168,4 @@ namespace Volo.Abp.EventBus.Local
return false;
}
}
}
}

38
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ApiTypeNameHelper.cs

@ -0,0 +1,38 @@
using System;
using Volo.Abp.Reflection;
namespace Volo.Abp.Http.Modeling
{
public static class ApiTypeNameHelper
{
public static string GetTypeName(Type type)
{
if (TypeHelper.IsDictionary(type, out var keyType, out var valueType))
{
return $"{{{GetTypeName(keyType)}:{GetTypeName(valueType)}}}";
}
if (TypeHelper.IsEnumerable(type, out var itemType, includePrimitives: false))
{
return $"[{GetTypeName(itemType)}]";
}
return TypeHelper.GetFullNameHandlingNullableAndGenerics(type);
}
public static string GetSimpleTypeName(Type type)
{
if (TypeHelper.IsDictionary(type, out var keyType, out var valueType))
{
return $"{{{GetSimpleTypeName(keyType)}:{GetSimpleTypeName(valueType)}}}";
}
if (TypeHelper.IsEnumerable(type, out var itemType, includePrimitives: false))
{
return $"[{GetSimpleTypeName(itemType)}]";
}
return TypeHelper.GetSimplifiedName(type);
}
}
}

6
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/MethodParameterApiDescriptionModel.cs

@ -30,11 +30,11 @@ namespace Volo.Abp.Http.Modeling
{
Name = parameterInfo.Name,
TypeAsString = parameterInfo.ParameterType.GetFullNameWithAssemblyName(),
Type = parameterInfo.ParameterType != null ? TypeHelper.GetFullNameHandlingNullableAndGenerics(parameterInfo.ParameterType) : null,
TypeSimple = parameterInfo.ParameterType != null ? TypeHelper.GetSimplifiedName(parameterInfo.ParameterType) : null,
Type = TypeHelper.GetFullNameHandlingNullableAndGenerics(parameterInfo.ParameterType),
TypeSimple = ApiTypeNameHelper.GetSimpleTypeName(parameterInfo.ParameterType),
IsOptional = parameterInfo.IsOptional,
DefaultValue = parameterInfo.HasDefaultValue ? parameterInfo.DefaultValue : null
};
}
}
}
}

6
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ParameterApiDescriptionModel.cs

@ -26,7 +26,7 @@ namespace Volo.Abp.Http.Modeling
private ParameterApiDescriptionModel()
{
}
public static ParameterApiDescriptionModel Create(string name, string nameOnMethod, Type type, bool isOptional = false, object defaultValue = null, string[] constraintTypes = null, string bindingSourceId = null, string descriptorName = null)
@ -36,7 +36,7 @@ namespace Volo.Abp.Http.Modeling
Name = name,
NameOnMethod = nameOnMethod,
Type = type != null ? TypeHelper.GetFullNameHandlingNullableAndGenerics(type) : null,
TypeSimple = type != null ? TypeHelper.GetSimplifiedName(type) : null,
TypeSimple = type != null ? ApiTypeNameHelper.GetSimpleTypeName(type) : null,
IsOptional = isOptional,
DefaultValue = defaultValue,
ConstraintTypes = constraintTypes,
@ -45,4 +45,4 @@ namespace Volo.Abp.Http.Modeling
};
}
}
}
}

26
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs

@ -1,6 +1,5 @@
using System;
using System.Reflection;
using Volo.Abp.Reflection;
namespace Volo.Abp.Http.Modeling
{
@ -16,31 +15,12 @@ namespace Volo.Abp.Http.Modeling
//TODO: Validation rules for this property
public static PropertyApiDescriptionModel Create(PropertyInfo propertyInfo)
{
string typeName;
string simpleTypeName;
if (TypeHelper.IsEnumerable(propertyInfo.PropertyType, out var itemType, includePrimitives: false))
{
typeName = $"[{TypeHelper.GetFullNameHandlingNullableAndGenerics(itemType)}]";
simpleTypeName = $"[{TypeHelper.GetSimplifiedName(itemType)}]";
}
else if (TypeHelper.IsDictionary(propertyInfo.PropertyType, out var keyType, out var valueType))
{
typeName = $"{{{TypeHelper.GetFullNameHandlingNullableAndGenerics(keyType)}:{TypeHelper.GetFullNameHandlingNullableAndGenerics(valueType)}}}";
simpleTypeName = $"{{{TypeHelper.GetSimplifiedName(keyType)}:{TypeHelper.GetSimplifiedName(valueType)}}}";
}
else
{
typeName = TypeHelper.GetFullNameHandlingNullableAndGenerics(propertyInfo.PropertyType);
simpleTypeName = TypeHelper.GetSimplifiedName(propertyInfo.PropertyType);
}
return new PropertyApiDescriptionModel
{
Name = propertyInfo.Name,
Type = typeName,
TypeSimple = simpleTypeName
Type = ApiTypeNameHelper.GetTypeName(propertyInfo.PropertyType),
TypeSimple = ApiTypeNameHelper.GetSimpleTypeName(propertyInfo.PropertyType)
};
}
}
}
}

4
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ReturnValueApiDescriptionModel.cs

@ -23,8 +23,8 @@ namespace Volo.Abp.Http.Modeling
return new ReturnValueApiDescriptionModel
{
Type = TypeHelper.GetFullNameHandlingNullableAndGenerics(unwrappedType),
TypeSimple = TypeHelper.GetSimplifiedName(unwrappedType)
TypeSimple = ApiTypeNameHelper.GetSimpleTypeName(unwrappedType)
};
}
}
}
}

12
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/TypeApiDescriptionModel.cs

@ -15,11 +15,13 @@ namespace Volo.Abp.Http.Modeling
public object[] EnumValues { get; set; }
public string[] GenericArguments { get; set; }
public PropertyApiDescriptionModel[] Properties { get; set; }
private TypeApiDescriptionModel()
{
}
public static TypeApiDescriptionModel Create(Type type)
@ -45,11 +47,17 @@ namespace Volo.Abp.Http.Modeling
{
typeModel.Properties = type
.GetProperties()
.Where(p => p.DeclaringType == type)
.Select(PropertyApiDescriptionModel.Create)
.ToArray();
if (type.IsGenericTypeDefinition)
{
typeModel.GenericArguments = type.GetGenericArguments().Select(a => a.Name).ToArray();
}
}
return typeModel;
}
}
}
}

4
framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
@ -316,4 +316,4 @@ namespace Volo.Abp.Uow
return $"[UnitOfWork {Id}]";
}
}
}
}

45
framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkExtensions.cs

@ -1,4 +1,7 @@
using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
namespace Volo.Abp.Uow
{
@ -10,5 +13,43 @@ namespace Volo.Abp.Uow
return unitOfWork.IsReserved && unitOfWork.ReservationName == reservationName;
}
public static void AddItem<TValue>([NotNull] this IUnitOfWork unitOfWork, string key, TValue value)
where TValue : class
{
Check.NotNull(unitOfWork, nameof(unitOfWork));
if (!unitOfWork.Items.ContainsKey(key))
{
unitOfWork.Items[key] = value;
}
else
{
unitOfWork.Items.Add(key, value);
}
}
public static TValue GetItemOrDefault<TValue>([NotNull] this IUnitOfWork unitOfWork, string key)
where TValue : class
{
Check.NotNull(unitOfWork, nameof(unitOfWork));
return unitOfWork.Items.FirstOrDefault(x => x.Key == key).As<TValue>();
}
public static TValue GetOrAddItem<TValue>([NotNull] this IUnitOfWork unitOfWork, string key, Func<string, TValue> factory)
where TValue : class
{
Check.NotNull(unitOfWork, nameof(unitOfWork));
return unitOfWork.Items.GetOrAdd(key, factory).As<TValue>();
}
public static void RemoveItem([NotNull] this IUnitOfWork unitOfWork, string key)
{
Check.NotNull(unitOfWork, nameof(unitOfWork));
unitOfWork.Items.RemoveAll(x => x.Key == key);
}
}
}
}

51
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/FormElements.cshtml

@ -108,7 +108,7 @@
public class FormElementsModel : PageModel
{
public SampleModel MyModel { get; set; }
public List&lt;SelectListItem&gt; CityList { get; set; } = new List&lt;SelectListItem&gt;
{
new SelectListItem { Value = "NY", Text = "New York"},
@ -260,7 +260,7 @@
public class SampleModel
{
public string SampleInput0 { get; set; }
public string SampleInput1 { get; set; }
public string SampleInput2 { get; set; }
@ -354,7 +354,7 @@
public class FormElementsModel : PageModel
{
public SampleModel MyModel { get; set; }
public List&quot;SelectListItem&quot; CityList { get; set; } = new List&quot;SelectListItem&quot;
{
new SelectListItem { Value = "NY", Text = "New York"},
@ -458,6 +458,51 @@
&lt;option value=&quot;3&quot;&gt;Coupe&lt;/option&gt;
&lt;/select&gt;
&lt;/div&gt;
</code></pre>
</abp-tab>
</abp-tabs>
</div>
</div>
<h4>Suppress Label Generation</h4>
<div class="demo-with-code">
<div class="demo-area">
<abp-input asp-for="@Model.MyModel.Name" suppress-label="true"/>
</div>
<div class="code-area">
<abp-tabs>
<abp-tab title="Modal Class">
<pre><code>
public class FormElementsModel : PageModel
{
public SampleModel MyModel { get; set; }
public void OnGet()
{
MyModel = new SampleModel();
}
public class SampleModel
{
[Required]
public string Name { get; set; }
}
}
</code></pre>
</abp-tab>
<abp-tab title="Tag Helper" active="true">
<pre><code>
&lt;abp-input asp-for=&quot;@@Model.MyModel.Name&quot; suppress-label=&quot;true&quot;/&gt;
</code></pre>
</abp-tab>
<abp-tab title="Rendered">
<pre><code>
&lt;div class=&quot;form-group&quot;&gt;
&lt;input type=&quot;text&quot; id=&quot;MyModel_Name&quot; name=&quot;MyModel.Name&quot; value=&quot;&quot; class=&quot;form-control &quot;&gt;
&lt;span class=&quot;text-danger field-validation-valid&quot; data-valmsg-for=&quot;MyModel.Name&quot; data-valmsg-replace=&quot;true&quot;&gt;&lt;/span&gt;
&lt;/div&gt;
</code></pre>
</abp-tab>
</abp-tabs>

6
framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/AbpCachingTestModule.cs

@ -1,5 +1,7 @@
using Microsoft.Extensions.Caching.Distributed;
using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.Modularity;
namespace Volo.Abp.Caching
@ -26,6 +28,8 @@ namespace Volo.Abp.Caching
option.GlobalCacheEntryOptions.SetSlidingExpiration(TimeSpan.FromMinutes(20));
});
context.Services.Replace(ServiceDescriptor.Singleton<IDistributedCache, TestMemoryDistributedCache>());
}
}
}
}

397
framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs

@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Testing;
using Volo.Abp.Uow;
using Xunit;
namespace Volo.Abp.Caching
@ -30,7 +32,7 @@ namespace Volo.Abp.Caching
cacheItem.ShouldNotBeNull();
cacheItem.Name.ShouldBe(personName);
//Remove
//Remove
await personCache.RemoveAsync(cacheKey);
//Get (not exists since removed)
@ -111,7 +113,7 @@ namespace Volo.Abp.Caching
cacheItem1.ShouldNotBeNull();
cacheItem1.Name.ShouldBe(personName);
//Remove
//Remove
await personCache.RemoveAsync(cacheKey);
@ -145,7 +147,7 @@ namespace Volo.Abp.Caching
cacheItem.ShouldNotBeNull();
cacheItem.Name.ShouldBe(personName);
//Remove
//Remove
await personCache.RemoveAsync(cacheKey);
//Get (not exists since removed)
@ -180,10 +182,10 @@ namespace Volo.Abp.Caching
factoryExecuted = false;
cacheItem = await personCache.GetOrAddAsync(cacheKey,
async () =>
() =>
{
factoryExecuted = true;
return new PersonCacheItem(personName);
return Task.FromResult(new PersonCacheItem(personName));
});
factoryExecuted.ShouldBeFalse();
@ -227,7 +229,7 @@ namespace Volo.Abp.Caching
cacheItem1.ShouldNotBeNull();
cacheItem1.Name.ShouldBe(personName);
//Remove
//Remove
await personCache.RemoveAsync(cacheKey);
@ -261,7 +263,7 @@ namespace Volo.Abp.Caching
cacheItem.ShouldNotBeNull();
cacheItem.Name.ShouldBe(personName);
//Remove
//Remove
await personCache.RemoveAsync(cacheKey);
//Get (not exists since removed)
@ -299,7 +301,7 @@ namespace Volo.Abp.Caching
cacheItem2.ShouldNotBeNull();
cacheItem2.Name.ShouldBe(personName);
//Remove
//Remove
await personCache.RemoveAsync(cache1Key);
await personCache.RemoveAsync(cache2Key);
@ -310,6 +312,377 @@ namespace Volo.Abp.Caching
cacheItem2.ShouldBeNull();
}
[Fact]
public async Task Cache_Should_Only_Available_In_Uow_For_GetAsync()
{
const string key = "testkey";
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
var cacheValue = await personCache.GetAsync(key, considerUow: true);
cacheValue.ShouldBeNull();
await personCache.SetAsync(key, new PersonCacheItem("john"), considerUow: true);
cacheValue = await personCache.GetAsync(key, considerUow: true);
cacheValue.ShouldNotBeNull();
cacheValue.Name.ShouldBe("john");
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
uow.OnCompleted(async () =>
{
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldNotBeNull();
cacheValue.Name.ShouldBe("john");
});
await uow.CompleteAsync();
}
}
[Fact]
public async Task Cache_Should_Rollback_With_Uow_For_GetAsync()
{
const string key = "testkey";
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
var cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{
cacheValue = await personCache.GetAsync(key, considerUow: true);
cacheValue.ShouldBeNull();
await personCache.SetAsync(key, new PersonCacheItem("john"), considerUow: true);
cacheValue = await personCache.GetAsync(key, considerUow: true);
cacheValue.ShouldNotBeNull();
cacheValue.Name.ShouldBe("john");
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
}
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
}
[Fact]
public async Task Cache_Should_Only_Available_In_Uow_For_GetOrAddAsync()
{
const string key = "testkey";
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
var cacheValue = await personCache.GetOrAddAsync(key, () => Task.FromResult(new PersonCacheItem("john")), considerUow: true);
cacheValue.ShouldNotBeNull();
cacheValue.Name.ShouldBe("john");
cacheValue = await personCache.GetAsync(key, considerUow: true);
cacheValue.ShouldNotBeNull();
cacheValue.Name.ShouldBe("john");
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
uow.OnCompleted(async () =>
{
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldNotBeNull();
cacheValue.Name.ShouldBe("john");
});
await uow.CompleteAsync();
}
}
[Fact]
public async Task Cache_Should_Rollback_With_Uow_For_GetOrAddAsync()
{
const string key = "testkey";
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
var cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{
cacheValue = await personCache.GetOrAddAsync(key, () => Task.FromResult(new PersonCacheItem("john")), considerUow: true);
cacheValue.ShouldNotBeNull();
cacheValue.Name.ShouldBe("john");
cacheValue = await personCache.GetAsync(key, considerUow: true);
cacheValue.ShouldNotBeNull();
cacheValue.Name.ShouldBe("john");
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
}
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
}
[Fact]
public async Task Cache_Should_Only_Available_In_Uow_For_SetAsync()
{
const string key = "testkey";
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
await personCache.SetAsync(key, new PersonCacheItem("john"), considerUow: true);
var cacheValue = await personCache.GetAsync(key, considerUow: true);
cacheValue.ShouldNotBeNull();
cacheValue.Name.ShouldBe("john");
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
uow.OnCompleted(async () =>
{
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldNotBeNull();
cacheValue.Name.ShouldBe("john");
});
await uow.CompleteAsync();
}
}
[Fact]
public async Task Cache_Should_Rollback_With_Uow_For_SetAsync()
{
const string key = "testkey";
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
var cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{
await personCache.SetAsync(key, new PersonCacheItem("john"), considerUow: true);
cacheValue = await personCache.GetAsync(key, considerUow: true);
cacheValue.ShouldNotBeNull();
cacheValue.Name.ShouldBe("john");
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
}
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
}
[Fact]
public async Task Cache_Should_Only_Available_In_Uow_For_RemoveAsync()
{
const string key = "testkey";
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
await personCache.SetAsync(key, new PersonCacheItem("john"), considerUow: true);
var cacheValue = await personCache.GetAsync(key, considerUow: true);
cacheValue.ShouldNotBeNull();
cacheValue.Name.ShouldBe("john");
await personCache.RemoveAsync(key, considerUow: true);
cacheValue = await personCache.GetAsync(key, considerUow: true);
cacheValue.ShouldBeNull();
uow.OnCompleted(async () =>
{
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
});
await uow.CompleteAsync();
}
}
[Fact]
public async Task Cache_Should_Rollback_With_Uow_For_RemoveAsync()
{
const string key = "testkey";
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
var cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{
await personCache.SetAsync(key, new PersonCacheItem("john"), considerUow: true);
cacheValue = await personCache.GetAsync(key, considerUow: true);
cacheValue.ShouldNotBeNull();
cacheValue.Name.ShouldBe("john");
await personCache.RemoveAsync(key, considerUow: true);
cacheValue = await personCache.GetAsync(key, considerUow: true);
cacheValue.ShouldBeNull();
}
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
}
[Fact]
public async Task Cache_Should_Only_Available_In_Uow_For_GetManyAsync()
{
var testkey = "testkey";
var testkey2 = "testkey2";
var testKeys = new[] {testkey, testkey2};
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
var cacheValue = await personCache.GetManyAsync(testKeys, considerUow: true);
cacheValue.Where(x => x.Value != null).ShouldBeEmpty();
await personCache.SetManyAsync(new List<KeyValuePair<string, PersonCacheItem>>
{
new KeyValuePair<string, PersonCacheItem>(testkey, new PersonCacheItem("john")),
new KeyValuePair<string, PersonCacheItem>(testkey2, new PersonCacheItem("jack"))
}, considerUow: true);
cacheValue = await personCache.GetManyAsync(testKeys, considerUow: true);
cacheValue.Where(x => x.Value != null).ShouldNotBeEmpty();
cacheValue.ShouldContain(x => x.Value.Name == "john" || x.Value.Name == "jack");
cacheValue = await personCache.GetManyAsync(testKeys, considerUow: false);
cacheValue.Where(x => x.Value != null).ShouldBeEmpty();
uow.OnCompleted(async () =>
{
cacheValue = await personCache.GetManyAsync(testKeys, considerUow: false);
cacheValue.ShouldNotBeEmpty();
cacheValue.ShouldContain(x => x.Value.Name == "john" || x.Value.Name == "jack");
});
await uow.CompleteAsync();
}
}
[Fact]
public async Task Cache_Should_Rollback_With_Uow_For_GetManyAsync()
{
var testkey = "testkey";
var testkey2 = "testkey2";
var testKeys = new[] {testkey, testkey2};
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
var cacheValue = await personCache.GetManyAsync(testKeys, considerUow: false);
cacheValue.Where(x => x.Value != null).ShouldBeEmpty();
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{
cacheValue = await personCache.GetManyAsync(testKeys, considerUow: true);
cacheValue.Where(x => x.Value != null).ShouldBeEmpty();
await personCache.SetManyAsync(new List<KeyValuePair<string, PersonCacheItem>>
{
new KeyValuePair<string, PersonCacheItem>(testkey, new PersonCacheItem("john")),
new KeyValuePair<string, PersonCacheItem>(testkey2, new PersonCacheItem("jack"))
}, considerUow: true);
cacheValue = await personCache.GetManyAsync(testKeys, considerUow: true);
cacheValue.Where(x => x.Value != null).ShouldNotBeEmpty();
cacheValue.ShouldContain(x => x.Value.Name == "john" || x.Value.Name == "jack");
cacheValue = await personCache.GetManyAsync(testKeys, considerUow: false);
cacheValue.Where(x => x.Value != null).ShouldBeEmpty();
}
cacheValue = await personCache.GetManyAsync(testKeys, considerUow: false);
cacheValue.Where(x => x.Value != null).ShouldBeEmpty();
}
[Fact]
public async Task Cache_Should_Only_Available_In_Uow_For_SetManyAsync()
{
var testkey = "testkey";
var testkey2 = "testkey2";
var testKeys = new[] {testkey, testkey2};
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
await personCache.SetManyAsync(new List<KeyValuePair<string, PersonCacheItem>>
{
new KeyValuePair<string, PersonCacheItem>(testkey, new PersonCacheItem("john")),
new KeyValuePair<string, PersonCacheItem>(testkey, new PersonCacheItem("jack"))
}, considerUow: true);
var cacheValue = await personCache.GetManyAsync(testKeys, considerUow: true);
cacheValue.Where(x => x.Value != null).ShouldNotBeEmpty();
cacheValue.ShouldContain(x => x.Value.Name == "john" || x.Value.Name == "jack");
cacheValue = await personCache.GetManyAsync(testKeys, considerUow: false);
cacheValue.Where(x => x.Value != null).ShouldBeEmpty();
uow.OnCompleted(async () =>
{
var cacheValue = await personCache.GetManyAsync(testKeys, considerUow: false);
cacheValue.Where(x => x.Value != null).ShouldNotBeEmpty();
cacheValue.ShouldContain(x => x.Value.Name == "john" || x.Value.Name == "jack");
});
await uow.CompleteAsync();
}
}
[Fact]
public async Task Cache_Should_Rollback_With_Uow_For_SetManyAsync()
{
var testkey = "testkey";
var testkey2 = "testkey2";
var testKeys = new[] {testkey, testkey2};
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
var cacheValue = await personCache.GetManyAsync(testKeys, considerUow: false);
cacheValue.Where(x => x.Value != null).ShouldBeEmpty();
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{
await personCache.SetManyAsync(new List<KeyValuePair<string, PersonCacheItem>>
{
new KeyValuePair<string, PersonCacheItem>(testkey, new PersonCacheItem("john")),
new KeyValuePair<string, PersonCacheItem>(testkey, new PersonCacheItem("jack"))
}, considerUow: true);
cacheValue = await personCache.GetManyAsync(testKeys, considerUow: true);
cacheValue.Where(x => x.Value != null).ShouldNotBeEmpty();
cacheValue.ShouldContain(x => x.Value.Name == "john" || x.Value.Name == "jack");
cacheValue = await personCache.GetManyAsync(testKeys, considerUow: false);
cacheValue.Where(x => x.Value != null).ShouldBeEmpty();
}
cacheValue = await personCache.GetManyAsync(testKeys, considerUow: false);
cacheValue.Where(x => x.Value != null).ShouldBeEmpty();
}
[Fact]
public async Task Should_Set_And_Get_Multiple_Items_Async()
{
@ -317,7 +690,7 @@ namespace Volo.Abp.Caching
await personCache.SetManyAsync(new[]
{
new KeyValuePair<string, PersonCacheItem>("john", new PersonCacheItem("John Nash")),
new KeyValuePair<string, PersonCacheItem>("john", new PersonCacheItem("John Nash")),
new KeyValuePair<string, PersonCacheItem>("thomas", new PersonCacheItem("Thomas Moore"))
});
@ -327,7 +700,7 @@ namespace Volo.Abp.Caching
"thomas",
"baris" //doesn't exist
});
cacheItems.Length.ShouldBe(3);
cacheItems[0].Key.ShouldBe("john");
cacheItems[0].Value.Name.ShouldBe("John Nash");
@ -335,9 +708,9 @@ namespace Volo.Abp.Caching
cacheItems[1].Value.Name.ShouldBe("Thomas Moore");
cacheItems[2].Key.ShouldBe("baris");
cacheItems[2].Value.ShouldBeNull();
(await personCache.GetAsync("john")).Name.ShouldBe("John Nash");
(await personCache.GetAsync("baris")).ShouldBeNull();
}
}
}
}

61
framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/TestMemoryDistributedCache.cs

@ -0,0 +1,61 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Caching
{
[DisableConventionalRegistration]
public class TestMemoryDistributedCache : MemoryDistributedCache, ICacheSupportsMultipleItems
{
public TestMemoryDistributedCache(IOptions<MemoryDistributedCacheOptions> optionsAccessor)
: base(optionsAccessor)
{
}
public TestMemoryDistributedCache(IOptions<MemoryDistributedCacheOptions> optionsAccessor, ILoggerFactory loggerFactory)
: base(optionsAccessor, loggerFactory)
{
}
public byte[][] GetMany(IEnumerable<string> keys)
{
var values = new List<byte[]>();
foreach (var key in keys)
{
values.Add(Get(key));
}
return values.ToArray();
}
public async Task<byte[][]> GetManyAsync(IEnumerable<string> keys, CancellationToken token = default)
{
var values = new List<byte[]>();
foreach (var key in keys)
{
values.Add(await GetAsync(key, token));
}
return values.ToArray();
}
public void SetMany(IEnumerable<KeyValuePair<string, byte[]>> items, DistributedCacheEntryOptions options)
{
foreach (var item in items)
{
Set(item.Key, item.Value, options);
}
}
public async Task SetManyAsync(IEnumerable<KeyValuePair<string, byte[]>> items, DistributedCacheEntryOptions options, CancellationToken token = default)
{
foreach (var item in items)
{
await SetAsync(item.Key, item.Value, options, token);
}
}
}
}

29
framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Distributed/LocalDistributedEventBus_Test.cs

@ -1,4 +1,7 @@
using System.Threading.Tasks;
using System;
using System.Threading.Tasks;
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.MultiTenancy;
using Xunit;
namespace Volo.Abp.EventBus.Distributed
@ -17,5 +20,29 @@ namespace Volo.Abp.EventBus.Distributed
Assert.Equal(3, MySimpleDistributedTransientEventHandler.HandleCount);
Assert.Equal(3, MySimpleDistributedTransientEventHandler.DisposeCount);
}
[Fact]
public async Task Should_Change_TenantId_If_EventData_Is_MultiTenant()
{
var tenantId = Guid.NewGuid();
DistributedEventBus.Subscribe<MySimpleEventData>(GetRequiredService<MySimpleDistributedSingleInstanceEventHandler>());
await DistributedEventBus.PublishAsync(new MySimpleEventData(3, tenantId));
Assert.Equal(tenantId, MySimpleDistributedSingleInstanceEventHandler.TenantId);
}
[Fact]
public async Task Should_Change_TenantId_If_Generic_EventData_Is_MultiTenant()
{
var tenantId = Guid.NewGuid();
DistributedEventBus.Subscribe<EntityCreatedEto<MySimpleEventData>>(GetRequiredService<MySimpleDistributedSingleInstanceEventHandler>());
await DistributedEventBus.PublishAsync(new MySimpleEventData(3, tenantId));
Assert.Equal(tenantId, MySimpleDistributedSingleInstanceEventHandler.TenantId);
}
}
}

32
framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Distributed/MySimpleDistributedSingleInstanceEventHandler.cs

@ -0,0 +1,32 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.EventBus.Distributed
{
public class MySimpleDistributedSingleInstanceEventHandler : IDistributedEventHandler<MySimpleEventData>, IDistributedEventHandler<EntityCreatedEto<MySimpleEventData>>, ITransientDependency
{
private readonly ICurrentTenant _currentTenant;
public MySimpleDistributedSingleInstanceEventHandler(ICurrentTenant currentTenant)
{
_currentTenant = currentTenant;
}
public static Guid? TenantId { get; set; }
public Task HandleEventAsync(MySimpleEventData eventData)
{
TenantId = _currentTenant.Id;
return Task.CompletedTask;
}
public Task HandleEventAsync(EntityCreatedEto<MySimpleEventData> eventData)
{
TenantId = _currentTenant.Id;
return Task.CompletedTask;
}
}
}

64
framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/EventBus_MultiTenancy_Test.cs

@ -0,0 +1,64 @@
using System;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.MultiTenancy;
using Xunit;
namespace Volo.Abp.EventBus.Local
{
public class EventBus_MultiTenancy_Test : EventBusTestBase
{
[Fact]
public async Task Should_Change_TenantId_If_EventData_Is_MultiTenant()
{
var tenantId = Guid.NewGuid();
var handler = new MyEventHandler(GetRequiredService<ICurrentTenant>());
LocalEventBus.Subscribe<EntityChangedEventData<MyEntity>>(handler);
await LocalEventBus.PublishAsync(new EntityCreatedEventData<MyEntity>(new MyEntity(tenantId)));
handler.TenantId.ShouldBe(tenantId);
}
public class MyEntity : Entity, IMultiTenant
{
public override object[] GetKeys()
{
return new object[0];
}
public MyEntity()
{
}
public MyEntity(Guid? tenantId)
{
TenantId = tenantId;
}
public Guid? TenantId { get; }
}
public class MyEventHandler : ILocalEventHandler<EntityChangedEventData<MyEntity>>
{
private readonly ICurrentTenant _currentTenant;
public MyEventHandler(ICurrentTenant currentTenant)
{
_currentTenant = currentTenant;
}
public Guid? TenantId { get; set; }
public Task HandleEventAsync(EntityChangedEventData<MyEntity> eventData)
{
TenantId = _currentTenant.Id;
return Task.CompletedTask;
}
}
}
}

12
framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/MySimpleEventData.cs

@ -1,12 +1,18 @@
using System;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.EventBus
{
public class MySimpleEventData
public class MySimpleEventData : IMultiTenant
{
public int Value { get; set; }
public MySimpleEventData(int value)
public Guid? TenantId { get; }
public MySimpleEventData(int value, Guid? tenantId = null)
{
Value = value;
TenantId = tenantId;
}
}
}
}

2
modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs

@ -132,7 +132,7 @@ namespace Volo.Abp.Account.Web.Pages.Account
true
);
await LocalEventBus.PublishAsync(new IdentitySecurityLogEvent
await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext()
{
Identity = IdentitySecurityLogIdentityConsts.Identity,
Action = result.ToIdentitySecurityLogAction(),

2
modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLogoutModel.cs

@ -20,7 +20,7 @@ namespace Volo.Abp.Account.Web.Pages.Account
public override async Task<IActionResult> OnGetAsync()
{
await LocalEventBus.PublishAsync(new IdentitySecurityLogEvent
await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext()
{
Identity = IdentitySecurityLogIdentityConsts.Identity,
Action = IdentitySecurityLogActionConsts.Logout

16
modules/account/src/Volo.Abp.Account.Web/Areas/Account/Controllers/AccountController.cs

@ -6,10 +6,8 @@ using Volo.Abp.Account.Localization;
using Volo.Abp.Account.Settings;
using Volo.Abp.Account.Web.Areas.Account.Controllers.Models;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.EventBus.Local;
using Volo.Abp.Identity;
using Volo.Abp.Identity.AspNetCore;
using Volo.Abp.SecurityLog;
using Volo.Abp.Settings;
using Volo.Abp.Validation;
using SignInResult = Microsoft.AspNetCore.Identity.SignInResult;
@ -28,22 +26,20 @@ namespace Volo.Abp.Account.Web.Areas.Account.Controllers
protected SignInManager<IdentityUser> SignInManager { get; }
protected IdentityUserManager UserManager { get; }
protected ISettingProvider SettingProvider { get; }
protected ILocalEventBus LocalEventBus { get; }
protected IdentitySecurityLogManager IdentitySecurityLogManager { get; }
public AccountController(
SignInManager<IdentityUser> signInManager,
IdentityUserManager userManager,
ISettingProvider settingProvider,
ISecurityLogManager securityLogManager,
ILocalEventBus localEventBus)
IdentitySecurityLogManager identitySecurityLogManager)
{
LocalizationResource = typeof(AccountResource);
SignInManager = signInManager;
UserManager = userManager;
SettingProvider = settingProvider;
LocalEventBus = localEventBus;
IdentitySecurityLogManager = identitySecurityLogManager;
}
[HttpPost]
@ -62,7 +58,7 @@ namespace Volo.Abp.Account.Web.Areas.Account.Controllers
true
);
await LocalEventBus.PublishAsync(new IdentitySecurityLogEvent
await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext()
{
Identity = IdentitySecurityLogIdentityConsts.Identity,
Action = signInResult.ToIdentitySecurityLogAction(),
@ -76,13 +72,13 @@ namespace Volo.Abp.Account.Web.Areas.Account.Controllers
[Route("logout")]
public virtual async Task Logout()
{
await LocalEventBus.PublishAsync(new IdentitySecurityLogEvent
await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext()
{
Identity = IdentitySecurityLogIdentityConsts.Identity,
Action = IdentitySecurityLogActionConsts.Logout
});
await SignInManager.SignOutAsync();
await SignInManager.SignOutAsync();
}
[HttpPost]

3
modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs

@ -5,7 +5,6 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.Account.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
using Volo.Abp.EventBus.Local;
using Volo.Abp.Identity;
using IdentityUser = Volo.Abp.Identity.IdentityUser;
@ -15,7 +14,7 @@ namespace Volo.Abp.Account.Web.Pages.Account
{
public SignInManager<IdentityUser> SignInManager { get; set; }
public IdentityUserManager UserManager { get; set; }
public ILocalEventBus LocalEventBus { get; set; }
public IdentitySecurityLogManager IdentitySecurityLogManager { get; set; }
protected AccountPageModel()
{

6
modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs

@ -98,7 +98,7 @@ namespace Volo.Abp.Account.Web.Pages.Account
true
);
await LocalEventBus.PublishAsync(new IdentitySecurityLogEvent
await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext()
{
Identity = IdentitySecurityLogIdentityConsts.Identity,
Action = result.ToIdentitySecurityLogAction(),
@ -194,7 +194,7 @@ namespace Volo.Abp.Account.Web.Pages.Account
if (!result.Succeeded)
{
await LocalEventBus.PublishAsync(new IdentitySecurityLogEvent
await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext()
{
Identity = IdentitySecurityLogIdentityConsts.IdentityExternal,
Action = "Login" + result
@ -224,7 +224,7 @@ namespace Volo.Abp.Account.Web.Pages.Account
await SignInManager.SignInAsync(user, false);
await LocalEventBus.PublishAsync(new IdentitySecurityLogEvent
await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext()
{
Identity = IdentitySecurityLogIdentityConsts.IdentityExternal,
Action = result.ToIdentitySecurityLogAction(),

2
modules/account/src/Volo.Abp.Account.Web/Pages/Account/Logout.cshtml.cs

@ -16,7 +16,7 @@ namespace Volo.Abp.Account.Web.Pages.Account
public virtual async Task<IActionResult> OnGetAsync()
{
await LocalEventBus.PublishAsync(new IdentitySecurityLogEvent
await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext()
{
Identity = IdentitySecurityLogIdentityConsts.Identity,
Action = IdentitySecurityLogActionConsts.Logout

29
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs

@ -5,6 +5,7 @@ using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Features;
using Volo.Abp.Guids;
using Volo.Abp.Uow;
namespace Volo.Abp.FeatureManagement
{
@ -27,12 +28,14 @@ namespace Volo.Abp.FeatureManagement
FeatureDefinitionManager = featureDefinitionManager;
}
[UnitOfWork]
public virtual async Task<string> GetOrNullAsync(string name, string providerName, string providerKey)
{
var cacheItem = await GetCacheItemAsync(name, providerName, providerKey);
return cacheItem.Value;
}
[UnitOfWork]
public virtual async Task SetAsync(string name, string value, string providerName, string providerKey)
{
var featureValue = await FeatureValueRepository.FindAsync(name, providerName, providerKey);
@ -46,21 +49,25 @@ namespace Volo.Abp.FeatureManagement
featureValue.Value = value;
await FeatureValueRepository.UpdateAsync(featureValue);
}
await Cache.SetAsync(CalculateCacheKey(name, providerName, providerKey), new FeatureValueCacheItem(featureValue?.Value), considerUow: true);
}
[UnitOfWork]
public virtual async Task DeleteAsync(string name, string providerName, string providerKey)
{
var featureValues = await FeatureValueRepository.FindAllAsync(name, providerName, providerKey);
foreach (var featureValue in featureValues)
{
await FeatureValueRepository.DeleteAsync(featureValue);
await Cache.RemoveAsync(CalculateCacheKey(name, providerName, providerKey), considerUow: true);
}
}
protected virtual async Task<FeatureValueCacheItem> GetCacheItemAsync(string name, string providerName, string providerKey)
{
var cacheKey = CalculateCacheKey(name, providerName, providerKey);
var cacheItem = await Cache.GetAsync(cacheKey);
var cacheItem = await Cache.GetAsync(cacheKey, considerUow: true);
if (cacheItem != null)
{
@ -68,28 +75,28 @@ namespace Volo.Abp.FeatureManagement
}
cacheItem = new FeatureValueCacheItem(null);
await SetCacheItemsAsync(providerName, providerKey, name, cacheItem);
return cacheItem;
}
private async Task SetCacheItemsAsync(
string providerName,
string providerKey,
string currentName,
string providerName,
string providerKey,
string currentName,
FeatureValueCacheItem currentCacheItem)
{
var featureDefinitions = FeatureDefinitionManager.GetAll();
var featuresDictionary = (await FeatureValueRepository.GetListAsync(providerName, providerKey))
.ToDictionary(s => s.Name, s => s.Value);
var cacheItems = new List<KeyValuePair<string, FeatureValueCacheItem>>();
var cacheItems = new List<KeyValuePair<string, FeatureValueCacheItem>>();
foreach (var featureDefinition in featureDefinitions)
{
var featureValue = featuresDictionary.GetOrDefault(featureDefinition.Name);
cacheItems.Add(
new KeyValuePair<string, FeatureValueCacheItem>(
CalculateCacheKey(featureDefinition.Name, providerName, providerKey),
@ -103,7 +110,7 @@ namespace Volo.Abp.FeatureManagement
}
}
await Cache.SetManyAsync(cacheItems);
await Cache.SetManyAsync(cacheItems, considerUow: true);
}
protected virtual string CalculateCacheKey(string name, string providerName, string providerKey)

51
modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementStore_Tests.cs

@ -5,6 +5,7 @@ using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Features;
using Volo.Abp.Modularity;
using Volo.Abp.Uow;
using Xunit;
namespace Volo.Abp.FeatureManagement
@ -14,11 +15,13 @@ namespace Volo.Abp.FeatureManagement
{
private IFeatureManagementStore FeatureManagementStore { get; set; }
private IFeatureValueRepository FeatureValueRepository { get; set; }
private IUnitOfWorkManager UnitOfWorkManager { get; set; }
protected FeatureManagementStore_Tests()
{
FeatureManagementStore = GetRequiredService<IFeatureManagementStore>();
FeatureValueRepository = GetRequiredService<IFeatureValueRepository>();
UnitOfWorkManager = GetRequiredService<IUnitOfWorkManager>();
}
[Fact]
@ -73,6 +76,30 @@ namespace Volo.Abp.FeatureManagement
TestEditionIds.Regular.ToString())).Value.ShouldBe(false.ToString().ToUpperInvariant());
}
[Fact]
public async Task Set_In_UnitOfWork_Should_Be_Consistent()
{
using (UnitOfWorkManager.Begin())
{
// Arrange
(await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString())).ShouldNotBeNull();
// Act
await FeatureManagementStore.SetAsync(TestFeatureDefinitionProvider.SocialLogins,
false.ToString().ToUpperInvariant(),
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString());
// Assert
(await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString())).ShouldBe(false.ToString().ToUpperInvariant());
}
}
[Fact]
public async Task DeleteAsync()
{
@ -94,5 +121,29 @@ namespace Volo.Abp.FeatureManagement
}
[Fact]
public async Task Delete_In_UnitOfWork_Should_Be_Consistent()
{
using (var uow = UnitOfWorkManager.Begin())
{
// Arrange
(await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString())).ShouldNotBeNull();
// Act
await FeatureManagementStore.DeleteAsync(TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString());
await uow.SaveChangesAsync();
// Assert
(await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString())).ShouldBeNull();
}
}
}
}

9
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentitySecurityLogEvent.cs → modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentitySecurityLogContext.cs

@ -1,13 +1,10 @@
using System;
using System.Collections.Generic;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.Identity
{
public class IdentitySecurityLogEvent : IMultiTenant
public class IdentitySecurityLogContext
{
public Guid? TenantId { get; set; }
public string Identity { get; set; }
public string Action { get; set; }
@ -18,12 +15,12 @@ namespace Volo.Abp.Identity
public Dictionary<string, object> ExtraProperties { get; }
public IdentitySecurityLogEvent()
public IdentitySecurityLogContext()
{
ExtraProperties = new Dictionary<string, object>();
}
public virtual IdentitySecurityLogEvent WithProperty(string key, object value)
public virtual IdentitySecurityLogContext WithProperty(string key, object value)
{
ExtraProperties[key] = value;
return this;

26
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentitySecurityLogHandler.cs → modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentitySecurityLogManager.cs

@ -2,15 +2,13 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus;
using Volo.Abp.Security.Claims;
using Volo.Abp.SecurityLog;
using Volo.Abp.Uow;
using Volo.Abp.Users;
namespace Volo.Abp.Identity
{
public class IdentitySecurityLogHandler : ILocalEventHandler<IdentitySecurityLogEvent>, ITransientDependency
public class IdentitySecurityLogManager : ITransientDependency
{
protected ISecurityLogManager SecurityLogManager { get; }
protected IdentityUserManager UserManager { get; }
@ -18,7 +16,7 @@ namespace Volo.Abp.Identity
protected IUserClaimsPrincipalFactory<IdentityUser> UserClaimsPrincipalFactory { get; }
protected ICurrentUser CurrentUser { get; }
public IdentitySecurityLogHandler(
public IdentitySecurityLogManager(
ISecurityLogManager securityLogManager,
IdentityUserManager userManager,
ICurrentPrincipalAccessor currentPrincipalAccessor,
@ -32,24 +30,24 @@ namespace Volo.Abp.Identity
CurrentUser = currentUser;
}
public async Task HandleEventAsync(IdentitySecurityLogEvent eventData)
public async Task SaveAsync(IdentitySecurityLogContext context)
{
Action<SecurityLogInfo> securityLogAction = securityLog =>
{
securityLog.Identity = eventData.Identity;
securityLog.Action = eventData.Action;
securityLog.Identity = context.Identity;
securityLog.Action = context.Action;
if (securityLog.UserName.IsNullOrWhiteSpace())
if (!context.UserName.IsNullOrWhiteSpace())
{
securityLog.UserName = eventData.UserName;
securityLog.UserName = context.UserName;
}
if (securityLog.ClientId.IsNullOrWhiteSpace())
if (!context.ClientId.IsNullOrWhiteSpace())
{
securityLog.ClientId = eventData.ClientId;
securityLog.ClientId = context.ClientId;
}
foreach (var property in eventData.ExtraProperties)
foreach (var property in context.ExtraProperties)
{
securityLog.ExtraProperties[property.Key] = property.Value;
}
@ -61,13 +59,13 @@ namespace Volo.Abp.Identity
}
else
{
if (eventData.UserName.IsNullOrWhiteSpace())
if (context.UserName.IsNullOrWhiteSpace())
{
await SecurityLogManager.SaveAsync(securityLogAction);
}
else
{
var user = await UserManager.FindByNameAsync(eventData.UserName);
var user = await UserManager.FindByNameAsync(context.UserName);
if (user != null)
{
using (CurrentPrincipalAccessor.Change(await UserClaimsPrincipalFactory.CreateAsync(user)))

19
modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/IdentityServerSecurityLogActionConsts.cs

@ -0,0 +1,19 @@
namespace Volo.Abp.IdentityServer
{
public class IdentityServerSecurityLogActionConsts
{
public static string LoginSucceeded { get; set; } = "LoginSucceeded";
public static string LoginLockedout { get; set; } = "LoginLockedout";
public static string LoginNotAllowed { get; set; } = "LoginNotAllowed";
public static string LoginRequiresTwoFactor { get; set; } = "LoginRequiresTwoFactor";
public static string LoginFailed { get; set; } = "LoginFailed";
public static string LoginInvalidUserName { get; set; } = "LoginInvalidUserName";
public static string LoginInvalidUserNameOrPassword { get; set; } = "LoginInvalidUserNameOrPassword";
}
}

7
modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/IdentityServerSecurityLogIdentityConsts.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.IdentityServer
{
public class IdentityServerSecurityLogIdentityConsts
{
public static string IdentityServer { get; set; } = "IdentityServer";
}
}

25
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs

@ -10,6 +10,7 @@ using IdentityServer4.Validation;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Volo.Abp.Identity;
using Volo.Abp.IdentityServer.Localization;
using Volo.Abp.Security.Claims;
using Volo.Abp.Uow;
@ -23,18 +24,21 @@ namespace Volo.Abp.IdentityServer.AspNetIdentity
protected SignInManager<IdentityUser> SignInManager { get; }
protected IEventService Events { get; }
protected UserManager<IdentityUser> UserManager { get; }
protected IdentitySecurityLogManager IdentitySecurityLogManager { get; }
protected ILogger<ResourceOwnerPasswordValidator<IdentityUser>> Logger { get; }
protected IStringLocalizer<AbpIdentityServerResource> Localizer { get; }
public AbpResourceOwnerPasswordValidator(
UserManager<IdentityUser> userManager,
SignInManager<IdentityUser> signInManager,
IdentitySecurityLogManager identitySecurityLogManager,
IEventService events,
ILogger<ResourceOwnerPasswordValidator<IdentityUser>> logger,
ILogger<ResourceOwnerPasswordValidator<IdentityUser>> logger,
IStringLocalizer<AbpIdentityServerResource> localizer)
{
UserManager = userManager;
SignInManager = signInManager;
IdentitySecurityLogManager = identitySecurityLogManager;
Events = events;
Logger = logger;
Localizer = localizer;
@ -71,6 +75,12 @@ namespace Volo.Abp.IdentityServer.AspNetIdentity
additionalClaims.ToArray()
);
await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext()
{
Identity = IdentityServerSecurityLogIdentityConsts.IdentityServer,
Action = result.ToIdentitySecurityLogAction(),
});
return;
}
else if (result.IsLockedOut)
@ -91,12 +101,25 @@ namespace Volo.Abp.IdentityServer.AspNetIdentity
await Events.RaiseAsync(new UserLoginFailureEvent(context.UserName, "invalid credentials", interactive: false));
errorDescription = Localizer["InvalidUserNameOrPassword"];
}
await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext()
{
Identity = IdentityServerSecurityLogIdentityConsts.IdentityServer,
Action = result.ToIdentitySecurityLogAction(),
UserName = context.UserName
});
}
else
{
Logger.LogInformation("No user found matching username: {username}", context.UserName);
await Events.RaiseAsync(new UserLoginFailureEvent(context.UserName, "invalid username", interactive: false));
errorDescription = Localizer["InvalidUsername"];
await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext()
{
Identity = IdentityServerSecurityLogIdentityConsts.IdentityServer,
Action = IdentityServerSecurityLogActionConsts.LoginInvalidUserName
});
}
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, errorDescription);

37
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/SignInResultExtensions.cs

@ -0,0 +1,37 @@
using Microsoft.AspNetCore.Identity;
namespace Volo.Abp.IdentityServer.AspNetIdentity
{
public static class SignInResultExtensions
{
public static string ToIdentitySecurityLogAction(this SignInResult result)
{
if (result.Succeeded)
{
return IdentityServerSecurityLogActionConsts.LoginSucceeded;
}
if (result.IsLockedOut)
{
return IdentityServerSecurityLogActionConsts.LoginLockedout;
}
if (result.RequiresTwoFactor)
{
return IdentityServerSecurityLogActionConsts.LoginRequiresTwoFactor;
}
if (result.IsNotAllowed)
{
return IdentityServerSecurityLogActionConsts.LoginNotAllowed;
}
if (!result.Succeeded)
{
return IdentityServerSecurityLogActionConsts.LoginFailed;
}
return IdentityServerSecurityLogActionConsts.LoginFailed;
}
}
}

1
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDataSeedContributor.cs

@ -30,6 +30,7 @@ namespace Volo.Abp.PermissionManagement
var permissionNames = PermissionDefinitionManager
.GetPermissions()
.Where(p => p.MultiTenancySide.HasFlag(multiTenancySide))
.Where(p => !p.Providers.Any() || p.Providers.Contains(RolePermissionValueProvider.ProviderName))
.Select(p => p.Name)
.ToArray();

32
modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/PermissionDataSeedContributor_Tests.cs

@ -0,0 +1,32 @@
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Data;
using Xunit;
namespace Volo.Abp.PermissionManagement
{
public class PermissionDataSeedContributor_Tests : PermissionTestBase
{
private readonly PermissionDataSeedContributor _permissionDataSeedContributor;
private readonly IPermissionGrantRepository _grantpermissionGrantRepository;
public PermissionDataSeedContributor_Tests()
{
_permissionDataSeedContributor = GetRequiredService<PermissionDataSeedContributor>();
_grantpermissionGrantRepository = GetRequiredService<IPermissionGrantRepository>();
}
[Fact]
public async Task SeedAsync()
{
(await _grantpermissionGrantRepository.FindAsync("MyPermission1", RolePermissionValueProvider.ProviderName, "admin")).ShouldBeNull();
(await _grantpermissionGrantRepository.FindAsync("MyPermission4", RolePermissionValueProvider.ProviderName, "admin")).ShouldBeNull();
await _permissionDataSeedContributor.SeedAsync(new DataSeedContext(null));
(await _grantpermissionGrantRepository.FindAsync("MyPermission1", RolePermissionValueProvider.ProviderName, "admin")).ShouldNotBeNull();
(await _grantpermissionGrantRepository.FindAsync("MyPermission4", RolePermissionValueProvider.ProviderName, "admin")).ShouldBeNull();
}
}
}

4
modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/TestPermissionDefinitionProvider.cs

@ -16,6 +16,8 @@ namespace Volo.Abp.PermissionManagement
myPermission2.AddChild("MyPermission2.ChildPermission1");
testGroup.AddPermission("MyPermission3", multiTenancySide: MultiTenancySides.Host);
testGroup.AddPermission("MyPermission4", multiTenancySide: MultiTenancySides.Host).WithProviders(UserPermissionValueProvider.ProviderName);
}
}
}
}

31
modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManagementStore.cs

@ -5,6 +5,7 @@ using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Volo.Abp.Settings;
using Volo.Abp.Uow;
namespace Volo.Abp.SettingManagement
{
@ -16,8 +17,8 @@ namespace Volo.Abp.SettingManagement
protected IGuidGenerator GuidGenerator { get; }
public SettingManagementStore(
ISettingRepository settingRepository,
IGuidGenerator guidGenerator,
ISettingRepository settingRepository,
IGuidGenerator guidGenerator,
IDistributedCache<SettingCacheItem> cache,
ISettingDefinitionManager settingDefinitionManager)
{
@ -27,11 +28,13 @@ namespace Volo.Abp.SettingManagement
SettingDefinitionManager = settingDefinitionManager;
}
[UnitOfWork]
public virtual async Task<string> GetOrNullAsync(string name, string providerName, string providerKey)
{
return (await GetCacheItemAsync(name, providerName, providerKey)).Value;
}
[UnitOfWork]
public virtual async Task SetAsync(string name, string value, string providerName, string providerKey)
{
var setting = await SettingRepository.FindAsync(name, providerName, providerKey);
@ -45,6 +48,8 @@ namespace Volo.Abp.SettingManagement
setting.Value = value;
await SettingRepository.UpdateAsync(setting);
}
await Cache.SetAsync(CalculateCacheKey(name, providerName, providerKey), new SettingCacheItem(setting?.Value), considerUow: true);
}
public virtual async Task<List<SettingValue>> GetListAsync(string providerName, string providerKey)
@ -53,19 +58,21 @@ namespace Volo.Abp.SettingManagement
return settings.Select(s => new SettingValue(s.Name, s.Value)).ToList();
}
[UnitOfWork]
public virtual async Task DeleteAsync(string name, string providerName, string providerKey)
{
var setting = await SettingRepository.FindAsync(name, providerName, providerKey);
if (setting != null)
{
await SettingRepository.DeleteAsync(setting);
await Cache.RemoveAsync(CalculateCacheKey(name, providerName, providerKey), considerUow: true);
}
}
protected virtual async Task<SettingCacheItem> GetCacheItemAsync(string name, string providerName, string providerKey)
{
var cacheKey = CalculateCacheKey(name, providerName, providerKey);
var cacheItem = await Cache.GetAsync(cacheKey);
var cacheItem = await Cache.GetAsync(cacheKey, considerUow: true);
if (cacheItem != null)
{
@ -75,26 +82,26 @@ namespace Volo.Abp.SettingManagement
cacheItem = new SettingCacheItem(null);
await SetCacheItemsAsync(providerName, providerKey, name, cacheItem);
return cacheItem;
}
private async Task SetCacheItemsAsync(
string providerName,
string providerKey,
string currentName,
string providerName,
string providerKey,
string currentName,
SettingCacheItem currentCacheItem)
{
var settingDefinitions = SettingDefinitionManager.GetAll();
var settingsDictionary = (await SettingRepository.GetListAsync(providerName, providerKey))
.ToDictionary(s => s.Name, s => s.Value);
var cacheItems = new List<KeyValuePair<string, SettingCacheItem>>();
var cacheItems = new List<KeyValuePair<string, SettingCacheItem>>();
foreach (var settingDefinition in settingDefinitions)
{
var settingValue = settingsDictionary.GetOrDefault(settingDefinition.Name);
cacheItems.Add(
new KeyValuePair<string, SettingCacheItem>(
CalculateCacheKey(settingDefinition.Name, providerName, providerKey),
@ -108,7 +115,7 @@ namespace Volo.Abp.SettingManagement
}
}
await Cache.SetManyAsync(cacheItems);
await Cache.SetManyAsync(cacheItems, considerUow: true);
}
protected virtual string CalculateCacheKey(string name, string providerName, string providerKey)

34
modules/setting-management/test/Volo.Abp.SettingManagement.Tests/Volo/Abp/SettingManagement/SettingManagementStore_Tests.cs

@ -4,6 +4,7 @@ using System.Text;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Settings;
using Volo.Abp.Uow;
using Xunit;
namespace Volo.Abp.SettingManagement
@ -13,12 +14,14 @@ namespace Volo.Abp.SettingManagement
private readonly ISettingManagementStore _settingManagementStore;
private readonly ISettingRepository _settingRepository;
private readonly SettingTestData _testData;
private readonly IUnitOfWorkManager _unitOfWorkManager;
public SettingManagementStore_Tests()
{
_settingManagementStore = GetRequiredService<ISettingManagementStore>();
_settingRepository = GetRequiredService<ISettingRepository>();
_testData = GetRequiredService<SettingTestData>();
_unitOfWorkManager= GetRequiredService<IUnitOfWorkManager>();
}
[Fact]
@ -50,6 +53,21 @@ namespace Volo.Abp.SettingManagement
(await _settingRepository.FindAsync(_testData.SettingId)).Value.ShouldBe("43");
}
[Fact]
public async Task Set_In_UnitOfWork_Should_Be_Consistent()
{
using (_unitOfWorkManager.Begin())
{
var value = await _settingManagementStore.GetOrNullAsync("MySetting1", GlobalSettingValueProvider.ProviderName, null);
value.ShouldBe("42");
await _settingManagementStore.SetAsync("MySetting1", "43", GlobalSettingValueProvider.ProviderName, null);
var valueAfterSet = await _settingManagementStore.GetOrNullAsync("MySetting1", GlobalSettingValueProvider.ProviderName, null);
valueAfterSet.ShouldBe("43");
}
}
[Fact]
public async Task DeleteAsync()
{
@ -60,5 +78,21 @@ namespace Volo.Abp.SettingManagement
(await _settingRepository.FindAsync(_testData.SettingId)).ShouldBeNull();
}
[Fact]
public async Task Delete_In_UnitOfWork_Should_Be_Consistent()
{
using (var uow = _unitOfWorkManager.Begin())
{
(await _settingManagementStore.GetOrNullAsync("MySetting1", GlobalSettingValueProvider.ProviderName, null)).ShouldNotBeNull();
await _settingManagementStore.DeleteAsync("MySetting1", GlobalSettingValueProvider.ProviderName, null);
await uow.SaveChangesAsync();
var value = await _settingManagementStore.GetOrNullAsync("MySetting1", GlobalSettingValueProvider.ProviderName, null);
value.ShouldBeNull();
}
}
}
}

8
npm/ng-packs/package.json

@ -39,8 +39,8 @@
"@angular/animations": "~10.0.0",
"@angular/cli": "~10.0.0",
"@angular/common": "~10.0.0",
"@angular/compiler": "~10.0.0",
"@angular/compiler-cli": "~10.0.0",
"@angular/compiler": "10.0.0",
"@angular/compiler-cli": "10.0.0",
"@angular/core": "~10.0.0",
"@angular/forms": "~10.0.0",
"@angular/language-service": "~10.0.0",
@ -92,6 +92,10 @@
"dependencies": {
"tslib": "^2.0.0"
},
"resolutions": {
"@angular/compiler": "10.0.0",
"@angular/compiler-cli": "10.0.0"
},
"config": {
"commitizen": {
"path": "cz-conventional-changelog"

9
npm/ng-packs/packages/core/src/lib/core.module.ts

@ -34,7 +34,7 @@ import { ConfigState } from './states/config.state';
import { ProfileState } from './states/profile.state';
import { ReplaceableComponentsState } from './states/replaceable-components.state';
import { SessionState } from './states/session.state';
import { CORE_OPTIONS } from './tokens/options.token';
import { CORE_OPTIONS, coreOptionsFactory } from './tokens/options.token';
import { noop } from './utils/common-utils';
import './utils/date-extensions';
import { getInitialData, localeInitializer, configureOAuth } from './utils/initial-utils';
@ -171,9 +171,14 @@ export class CoreModule {
useValue: { environment: options.environment },
},
{
provide: CORE_OPTIONS,
provide: 'CORE_OPTIONS',
useValue: options,
},
{
provide: CORE_OPTIONS,
useFactory: coreOptionsFactory,
deps: ['CORE_OPTIONS'],
},
{
provide: HTTP_INTERCEPTORS,
useClass: ApiInterceptor,

1
npm/ng-packs/packages/core/src/lib/models/common.ts

@ -9,6 +9,7 @@ export namespace ABP {
environment: Partial<Config.Environment>;
skipGetAppConfiguration?: boolean;
sendNullsAsQueryParam?: boolean;
cultureNameToLocaleFileNameMapping?: Dictionary<string>;
}
export interface Test {

4
npm/ng-packs/packages/core/src/lib/services/localization.service.ts

@ -8,6 +8,7 @@ import { Config } from '../models/config';
import { ConfigState } from '../states/config.state';
import { registerLocale } from '../utils/initial-utils';
import { createLocalizer, createLocalizerWithFallback } from '../utils/localization-utils';
import { CORE_OPTIONS } from '../tokens/options.token';
type ShouldReuseRoute = (future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot) => boolean;
@ -44,11 +45,12 @@ export class LocalizationService {
registerLocale(locale: string) {
const router = this.injector.get(Router);
const { cultureNameToLocaleFileNameMapping: localeNameMap } = this.injector.get(CORE_OPTIONS);
const { shouldReuseRoute } = router.routeReuseStrategy;
router.routeReuseStrategy.shouldReuseRoute = () => false;
router.navigated = false;
return registerLocale(locale).then(() => {
return registerLocale(locale, localeNameMap).then(() => {
this.ngZone.run(async () => {
await router.navigateByUrl(router.url).catch(noop);
router.routeReuseStrategy.shouldReuseRoute = shouldReuseRoute;

11
npm/ng-packs/packages/core/src/lib/tokens/options.token.ts

@ -1,4 +1,15 @@
import { InjectionToken } from '@angular/core';
import { ABP } from '../models/common';
import differentLocales from '../constants/different-locales';
export const CORE_OPTIONS = new InjectionToken<ABP.Root>('CORE_OPTIONS');
export function coreOptionsFactory({
cultureNameToLocaleFileNameMapping: localeNameMap = {},
...options
}: ABP.Root) {
return {
...options,
cultureNameToLocaleFileNameMapping: { ...differentLocales, ...localeNameMap },
} as ABP.Root;
}

14
npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts

@ -4,7 +4,6 @@ import { Store } from '@ngxs/store';
import { OAuthService } from 'angular-oauth2-oidc';
import { tap } from 'rxjs/operators';
import { GetAppConfiguration } from '../actions/config.actions';
import differentLocales from '../constants/different-locales';
import { ABP } from '../models/common';
import { ConfigState } from '../states/config.state';
import { CORE_OPTIONS } from '../tokens/options.token';
@ -45,22 +44,25 @@ function checkAccessToken(store: Store, injector: Injector) {
export function localeInitializer(injector: Injector) {
const fn = () => {
const store: Store = injector.get(Store);
const options = injector.get(CORE_OPTIONS);
const lang = store.selectSnapshot(state => state.SessionState.language) || 'en';
return new Promise((resolve, reject) => {
registerLocale(lang).then(() => resolve('resolved'), reject);
registerLocale(lang, options.cultureNameToLocaleFileNameMapping).then(
() => resolve('resolved'),
reject,
);
});
};
return fn;
}
export function registerLocale(locale: string) {
export function registerLocale(locale: string, localeNameMap: ABP.Dictionary<string>) {
return import(
/* webpackInclude: /(af|ar|am|ar-SA|as|az-Latn|be|bg|bn-BD|bn-IN|bs|ca|ca-ES-VALENCIA|cs|cy|da|de|de|el|en-GB|en|es|en|es-US|es-MX|et|eu|fa|fi|en|fr|fr|fr-CA|ga|gd|gl|gu|ha|he|hi|hr|hu|hy|id|ig|is|it|it|ja|ka|kk|km|kn|ko|kok|en|en|lb|lt|lv|en|mk|ml|mn|mr|ms|mt|nb|ne|nl|nl-BE|nn|en|or|pa|pa-Arab|pl|en|pt|pt-PT|en|en|ro|ru|rw|pa-Arab|si|sk|sl|sq|sr-Cyrl-BA|sr-Cyrl|sr-Latn|sv|sw|ta|te|tg|th|ti|tk|tn|tr|tt|ug|uk|ur|uz-Latn|vi|wo|xh|yo|zh-Hans|zh-Hant|zu)\.js$/ */
/* webpackChunkName: "[request]"*/
`@angular/common/locales/${differentLocales[locale] || locale}.js`
/* webpackChunkName: "_locale-[request]"*/
`@angular/common/locales/${localeNameMap[locale] || locale}.js`
).then(module => {
registerLocaleData(module.default);
});

24
npm/ng-packs/packages/theme-basic/src/lib/theme-basic.module.ts

@ -42,6 +42,20 @@ export const LAYOUTS = [ApplicationLayoutComponent, AccountLayoutComponent, Empt
NgbCollapseModule,
NgbDropdownModule,
NgxValidateCoreModule,
],
entryComponents: [...LAYOUTS, ValidationErrorComponent, CurrentUserComponent, LanguagesComponent],
})
export class ThemeBasicModule {
static forRoot(): ModuleWithProviders<ThemeBasicModule> {
return {
ngModule: RootThemeBasicModule,
providers: [BASIC_THEME_NAV_ITEM_PROVIDERS, BASIC_THEME_STYLES_PROVIDERS],
};
}
}
@NgModule({
imports: [
NgxValidateCoreModule.forRoot({
targetSelector: '.form-group',
blueprints: {
@ -63,13 +77,5 @@ export const LAYOUTS = [ApplicationLayoutComponent, AccountLayoutComponent, Empt
errorTemplate: ValidationErrorComponent,
}),
],
entryComponents: [...LAYOUTS, ValidationErrorComponent, CurrentUserComponent, LanguagesComponent],
})
export class ThemeBasicModule {
static forRoot(): ModuleWithProviders<ThemeBasicModule> {
return {
ngModule: ThemeBasicModule,
providers: [BASIC_THEME_NAV_ITEM_PROVIDERS, BASIC_THEME_STYLES_PROVIDERS],
};
}
}
export class RootThemeBasicModule {}

3
npm/ng-packs/tsconfig.base.json

@ -17,7 +17,8 @@
"paths": {
"@abp/ng.core": ["packages/core/src/public-api.ts"],
"@abp/ng.theme.shared": ["packages/theme-shared/src/public-api.ts"],
"@abp/ng.theme.shared/extensions": ["packages/theme-shared/src/public-api.ts"],
"@abp/ng.theme.shared/extensions": ["packages/theme-shared/extensions/src/public-api.ts"],
"@abp/ng.components/tree": ["packages/components/tree/src/public-api.ts"],
"@abp/ng.theme.basic": ["packages/theme-basic/src/public-api.ts"],
"@abp/ng.account": ["packages/account/src/public-api.ts"],
"@abp/ng.account/config": ["packages/account/config/src/public-api.ts"],

501
npm/ng-packs/yarn.lock

@ -108,31 +108,31 @@
jest-preset-angular "^8.1.2"
lodash "^4.17.10"
"@angular-devkit/architect@0.1000.3":
version "0.1000.3"
resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1000.3.tgz#f23ea9d370edac8b0dd979d2574b12aa9923c3d4"
integrity sha512-8ZszTAkRvGGMXERFvyLT6SJPfJXjNNfHamA76uDPTBXy+EijJ1XVTUr1+SYEe73E4ovtxqxAnsApEFxS7/Ni5w==
"@angular-devkit/architect@0.1000.4":
version "0.1000.4"
resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1000.4.tgz#2effe58361bea2a77d11671743568ab4d50b1554"
integrity sha512-n0DXdrecxXupxKf37sv5Oky5QpQXOHPgLDAmyAwevr31yLEwyZ3OQoVxaC68AMQJFYMlHYYMwi6gdIcFmL6W0g==
dependencies:
"@angular-devkit/core" "10.0.3"
"@angular-devkit/core" "10.0.4"
rxjs "6.5.5"
"@angular-devkit/architect@>=0.900.0 < 0.1000.0":
version "0.901.11"
resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.901.11.tgz#d429b7fc9952fe6a047a9645786a93eed60f8393"
integrity sha512-RmYOq1VEJdQLzwMno+C56WtgscAtoR/7i4tX5b5VxRa2RmQKTxowllYWwgrF5445VGUqzap9H6zJFXvlY2FA0w==
version "0.901.12"
resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.901.12.tgz#e0f78b7282f851d27af6802dc7a261022421b2d5"
integrity sha512-gLlsxa+3JPV1m1gRvRMujOs4xKox6I5BkYmOD1zfu+dB6y3LuBAvHfXA6FaTDVOMBrmSlWnE4PmOmB6xd7wxMA==
dependencies:
"@angular-devkit/core" "9.1.11"
"@angular-devkit/core" "9.1.12"
rxjs "6.5.4"
"@angular-devkit/build-angular@~0.1000.0":
version "0.1000.3"
resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.1000.3.tgz#c5af97a1868adb2d0be9cb97e5d8b3c3c8428d4d"
integrity sha512-r3KJj39AwkYYzbixSM095l4fOGvhyByr0XvmAEu0l5dGGdL4tNXywvgXkNhEVRDo0jZYpTMegiTqzOik/9YCDw==
dependencies:
"@angular-devkit/architect" "0.1000.3"
"@angular-devkit/build-optimizer" "0.1000.3"
"@angular-devkit/build-webpack" "0.1000.3"
"@angular-devkit/core" "10.0.3"
version "0.1000.4"
resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.1000.4.tgz#447ebfc16afaa7638099e2f183144ebdc0e2a1a4"
integrity sha512-jAl2xsj1g4RdQ4MBLSZ8Dsj3MUKjYKK+e73DxVHSTo0166T1p/7gdgSehLHWVlavJLDVM+fjYdt4kP12Z7FBjQ==
dependencies:
"@angular-devkit/architect" "0.1000.4"
"@angular-devkit/build-optimizer" "0.1000.4"
"@angular-devkit/build-webpack" "0.1000.4"
"@angular-devkit/core" "10.0.4"
"@babel/core" "7.9.6"
"@babel/generator" "7.9.6"
"@babel/plugin-transform-runtime" "7.9.6"
@ -140,8 +140,8 @@
"@babel/runtime" "7.9.6"
"@babel/template" "7.8.6"
"@jsdevtools/coverage-istanbul-loader" "3.0.3"
"@ngtools/webpack" "10.0.3"
ajv "6.12.2"
"@ngtools/webpack" "10.0.4"
ajv "6.12.3"
autoprefixer "9.8.0"
babel-loader "8.1.0"
browserslist "^4.9.1"
@ -196,86 +196,86 @@
worker-plugin "4.0.3"
"@angular-devkit/build-ng-packagr@~0.1000.0":
version "0.1000.3"
resolved "https://registry.yarnpkg.com/@angular-devkit/build-ng-packagr/-/build-ng-packagr-0.1000.3.tgz#b9f10e6e1fcc821c7b35a07903f6f7e6e3f41f97"
integrity sha512-k5dEjx8g+ZLLwlViue7931PNxbAbePJ1NdAmXodg2U0W4l5g22egiUFG1DGsMm7p/3LlZGv3q9Kwdg8Ox1acVg==
version "0.1000.4"
resolved "https://registry.yarnpkg.com/@angular-devkit/build-ng-packagr/-/build-ng-packagr-0.1000.4.tgz#a449169210b994491cf6048d07b246daf74eb1c7"
integrity sha512-YcqXv0+mNEAUtFI2XtoabXNxlTfgIsFobeoGwdfe1tVn4b465FIgqFDiNFEgaZ4UZbdbLuk0yrVHpk/f8X/wzw==
dependencies:
"@angular-devkit/architect" "0.1000.3"
"@angular-devkit/architect" "0.1000.4"
rxjs "6.5.5"
"@angular-devkit/build-optimizer@0.1000.3":
version "0.1000.3"
resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.1000.3.tgz#05f7a853a5907ad3381a598896a2e053e47e925d"
integrity sha512-6mFoubg08UCWC0fE2mGoawEt2R1VlGStvUNAP2PRCjoj1ZySa1NnVYoKk65cyAAA3K2o7vSoDZesNq1uABjZbg==
"@angular-devkit/build-optimizer@0.1000.4":
version "0.1000.4"
resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.1000.4.tgz#bdeb610b042533dbbc3c14c52f81f2f67bfb6513"
integrity sha512-usscQipxIU2yJVmBdyIDPmJZ+ltOjsX4VbMkGM2IdJHCIi9uEk2E3+YLxzyU5Xl5tXEK61AQt4YWUimt8Kg0GA==
dependencies:
loader-utils "2.0.0"
source-map "0.7.3"
tslib "2.0.0"
webpack-sources "1.4.3"
"@angular-devkit/build-webpack@0.1000.3":
version "0.1000.3"
resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.1000.3.tgz#2709ccfc5201c2be9301551ef1d815c6aafedfbc"
integrity sha512-+vmn9d9THFubSWS28K1+nElUfOrhT576ptVZMd0a5S24momV8loW3J8iBOBfnGal/P86ZCAyP46kSirlAzH9Jg==
"@angular-devkit/build-webpack@0.1000.4":
version "0.1000.4"
resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.1000.4.tgz#781e7d67dbc80932e933010fea96339e10a7eec8"
integrity sha512-ZPXrm1+UyKbMZtnU+b5jdtZPFDYs8Pu5lHhJDzX6bk0TxtuXws+nwaeUs6WkfwXft8cksxK2WwGr57XX3OT7nw==
dependencies:
"@angular-devkit/architect" "0.1000.3"
"@angular-devkit/core" "10.0.3"
"@angular-devkit/architect" "0.1000.4"
"@angular-devkit/core" "10.0.4"
rxjs "6.5.5"
"@angular-devkit/core@10.0.3":
version "10.0.3"
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-10.0.3.tgz#7654766c9f2d3c510beaad8bca3e2a71643c6b8e"
integrity sha512-m27ogjq44j80x64RnEswSvy8UewUqeCVJBbEuY6fzrWoaiCf12sgPlrSCwjwfhtQrLgl1e/i9zYA7U6ulGRXyg==
"@angular-devkit/core@10.0.4":
version "10.0.4"
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-10.0.4.tgz#105df3e7a7df56005f3637121ef010dd3849f5eb"
integrity sha512-Pe60BJCcAVtOkhXn2c+ql1NfyMCu421jWmKwl1KsW1DXclpGB+UIa560ICBgEwla6DyltMKEN/lX+P4WOskpkQ==
dependencies:
ajv "6.12.2"
ajv "6.12.3"
fast-json-stable-stringify "2.1.0"
magic-string "0.25.7"
rxjs "6.5.5"
source-map "0.7.3"
"@angular-devkit/core@8.3.28", "@angular-devkit/core@^8.0.3":
version "8.3.28"
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-8.3.28.tgz#0a733132821d916788a8ed34eff0f9149a9f3a15"
integrity sha512-tM0oPjzftJMdfuAuZF2EspVKtJyhtS2IQ02S7eKTZqkh5p/oVv710aND2D2IN5qdFrzpUSearx6D+Mr/Jo8jrA==
"@angular-devkit/core@8.3.29", "@angular-devkit/core@^8.0.3":
version "8.3.29"
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-8.3.29.tgz#3477edd6458653f83e6d78684b100c1bef81382f"
integrity sha512-4jdja9QPwR6XG14ZSunyyOWT3nE2WtZC5IMDIBZADxujXvhzOU0n4oWpy6/JVHLUAxYNNgzLz+/LQORRWndcPg==
dependencies:
ajv "6.10.2"
ajv "6.12.3"
fast-json-stable-stringify "2.0.0"
magic-string "0.25.3"
rxjs "6.4.0"
source-map "0.7.3"
"@angular-devkit/core@9.1.11", "@angular-devkit/core@^9.0.0":
version "9.1.11"
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-9.1.11.tgz#a2de3de721488164297bfccefcb08f28a7a9108e"
integrity sha512-uiEkDvWfMgPHuO4jVgBEr9Kl/LuxHaWYGD2ZtKsOnnHYZyRGp61ot7UcDF+KNdXTiq01JJH84VTd3IttEewmhQ==
"@angular-devkit/core@9.1.12", "@angular-devkit/core@^9.0.0":
version "9.1.12"
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-9.1.12.tgz#7cebce51918fe7f3462d8c58c9fbd5a3e2c3b3e7"
integrity sha512-D/GnBeSlmdgGn7EhuE32HuPuRAjvUuxi7Q6WywBI8PSsXKAGnrypghBwMATNnOA24//CgbW2533Y9VWHaeXdeA==
dependencies:
ajv "6.12.0"
ajv "6.12.3"
fast-json-stable-stringify "2.1.0"
magic-string "0.25.7"
rxjs "6.5.4"
source-map "0.7.3"
"@angular-devkit/schematics@10.0.3":
version "10.0.3"
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-10.0.3.tgz#84e1a337692ba36cbae145b5e678592878df722f"
integrity sha512-TjA2ZSPCgUK9l4FiRTIQY7DceXMAvNzOMWffy9o3kv2HPtxG9kuBrQXk++Z99zpylK0cAsugV7t/5ANpUkrIiA==
"@angular-devkit/schematics@10.0.4":
version "10.0.4"
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-10.0.4.tgz#73e0021b01e4c35d3bf54111824793527c5ec6e5"
integrity sha512-k9b73XkKQK8yFbq+dQqJnxAMG3mm/YR1KQlsQzOEVdw8cqqEALLoUXg/73hT5eUnSsNWmyDLBzEAyNtemmmI4w==
dependencies:
"@angular-devkit/core" "10.0.3"
"@angular-devkit/core" "10.0.4"
ora "4.0.4"
rxjs "6.5.5"
"@angular-devkit/schematics@^8.0.6":
version "8.3.28"
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-8.3.28.tgz#80b93755e7712f838fe6a3a4a5f3f44312188a0e"
integrity sha512-WFktBtTynViexVrgHiQV9rKfNS2YNONpi25A8+5tQR5kGKuQyHI3aEN0UMNA2sztWnPQ9AI8xz2uciQsHljgDw==
version "8.3.29"
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-8.3.29.tgz#b3ba658b90fb3226a80ff12977be7dd583e99c49"
integrity sha512-AFJ9EK0XbcNlO5Dm9vr0OlBo1Nw6AaFXPR+DmHGBdcDDHxqEmYYLWfT+JU/8U2YFIdgrtlwvdtf6UQ3V2jdz1g==
dependencies:
"@angular-devkit/core" "8.3.28"
"@angular-devkit/core" "8.3.29"
rxjs "6.4.0"
"@angular/animations@~10.0.0":
version "10.0.4"
resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-10.0.4.tgz#179e4e63e3f2468ee1fe961c12eb143dd8350b23"
integrity sha512-UzQiWhDHY6wixS1Nh+Jwpzq1weiLGXJPt3Pa4pETpt3Hg7MIUu62dik6OFWuGYQPbn9DJYH+CH+sRxN1GCVjww==
version "10.0.5"
resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-10.0.5.tgz#41c1419f5233cf8adc2139461a552969e1f938c1"
integrity sha512-vHWn7NJiiDAok36GSBJ0y/nqjd4UFl9WGIEsvydUqnyBJ0scuqJa+hADrWwqkK7b/xVW9ARVG00NXmemEPUVGA==
dependencies:
tslib "^2.0.0"
@ -287,15 +287,15 @@
parse5 "^5.0.0"
"@angular/cli@~10.0.0":
version "10.0.3"
resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-10.0.3.tgz#0b8c5e263deaa63d05b6efda790a2000b8a24a85"
integrity sha512-ONK8YG20KuakQetY0lPKDAOA3uBoLurdpSfFspFkcECyDimwJYSEydi3FUnCxEexeoKvrQWcol+q+u9YPoHCyg==
dependencies:
"@angular-devkit/architect" "0.1000.3"
"@angular-devkit/core" "10.0.3"
"@angular-devkit/schematics" "10.0.3"
"@schematics/angular" "10.0.3"
"@schematics/update" "0.1000.3"
version "10.0.4"
resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-10.0.4.tgz#5c13500f3135e43b9e6e53534fdfbc283abb83cc"
integrity sha512-hmr1fXT8+6wOoh7abIg154wdP9QCMPHMShWPEYHks6ARmlRfX5k+BykQMzgI8H6tMNLoR+DAlO5+pPouJEyilQ==
dependencies:
"@angular-devkit/architect" "0.1000.4"
"@angular-devkit/core" "10.0.4"
"@angular-devkit/schematics" "10.0.4"
"@schematics/angular" "10.0.4"
"@schematics/update" "0.1000.4"
"@yarnpkg/lockfile" "1.1.0"
ansi-colors "4.1.1"
debug "4.1.1"
@ -313,16 +313,16 @@
uuid "8.1.0"
"@angular/common@~10.0.0":
version "10.0.4"
resolved "https://registry.yarnpkg.com/@angular/common/-/common-10.0.4.tgz#f585e0b1ef723bb96e54af831138184999490e3d"
integrity sha512-9DJMD8GgHz7i2fMz0f1IHZlDSIz83uE6fcH8SIq1iCSWT2dubRRpCX000VpIyhAgfkCgdNCYXQ7VGNsZceoagQ==
version "10.0.5"
resolved "https://registry.yarnpkg.com/@angular/common/-/common-10.0.5.tgz#f1c1b8671dbacc263d5d4f63606a82bc968644ff"
integrity sha512-AKmD/X13q3zzo5uTV/M4dWpAqJ136187rL4VweRc6b5t/oZ3VGcejjElZebgtoyGTye8luIP6fZ/LMb580lBlQ==
dependencies:
tslib "^2.0.0"
"@angular/compiler-cli@~10.0.0":
version "10.0.4"
resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-10.0.4.tgz#b241f8d41ca01dfed67b5ed4928ab0f13e2879af"
integrity sha512-uZKk6Ab4Pw8qcaXhpORuEoCbPSHWx3TWcs9OXIKIhzOOoMMe9OSt2SzOkHCrySSaik1IhQODnHww7sGRw5mxwQ==
"@angular/compiler-cli@10.0.0":
version "10.0.0"
resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-10.0.0.tgz#6fe60e239b01fd10d3923a3de88877512e0e2d40"
integrity sha512-KusRkS1NKPrjhj3BXoxRgeejuJVy/ra4dcDV3hpscRmR8FSdim3rs2H+lYK2hZ26ISGsYUvC8cHNaMqP2U3X+g==
dependencies:
canonical-path "1.0.0"
chokidar "^3.0.0"
@ -338,36 +338,36 @@
tslib "^2.0.0"
yargs "15.3.0"
"@angular/compiler@~10.0.0":
version "10.0.4"
resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-10.0.4.tgz#786a5c1653a429233bee9f53614b5a82c62841ce"
integrity sha512-1rnEmSHJtrKC1QD+PyF36xwMnSqG4Slgi5+PYk3BxIa5vbWBibrYijtd/uCNhscfPSpfb06MVM2mRsrc+BmbQg==
"@angular/compiler@10.0.0":
version "10.0.0"
resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-10.0.0.tgz#15f0a0e5760ebc6fcbc06b0b3bb5f110282cf03d"
integrity sha512-meyJKPLLhkgjxF4dvdJq/sw7MI7KvpgYADoFt2K/5dt48ExXRj+kDlyQC6FAwm9mbgSea/A+JpDG8HMPU7AK5Q==
dependencies:
tslib "^2.0.0"
"@angular/core@~10.0.0":
version "10.0.4"
resolved "https://registry.yarnpkg.com/@angular/core/-/core-10.0.4.tgz#700eda7711b50796298679d245e091cb54155071"
integrity sha512-lA8RDagJ/O0gUX95h00+nnUZs/1QmmhAVWVtHcuI12ueC836tJhLtPGnEx9ib9NXrgRyNwb8lO1xJPmmuQgdQQ==
version "10.0.5"
resolved "https://registry.yarnpkg.com/@angular/core/-/core-10.0.5.tgz#52a11d1b6ce065f6c26d919ab3bea233f51902be"
integrity sha512-zoIxIi/UaRImeC1uD+eIKrdcihvQR/zfbqKgK67IPy/tghijxge1Tbl8FWIAT0ncAj0XH/JHsWnVdUV/TUI+CA==
dependencies:
tslib "^2.0.0"
"@angular/forms@~10.0.0":
version "10.0.4"
resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-10.0.4.tgz#d37fe47d5c91b0ef249066e904338905b2687847"
integrity sha512-SJSYZCfHua9fi/dks1q+ad9OGBblRUn3Q1V4B7r99fJYr39qRiIHcegikhY4h8H3Wk1bJRGJG7iXmxJhjWXK3Q==
version "10.0.5"
resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-10.0.5.tgz#c1e3d7218770e56ab0060b28dbf33d76a481a1c5"
integrity sha512-Bv+KEPjqd+ndl/Z0w1uWsBmvb8Je1JObHRjbKAcW7c4r1CVobm+QJv6r6MDL+FOs78lnrm8baekGLQyHH1uO7g==
dependencies:
tslib "^2.0.0"
"@angular/language-service@~10.0.0":
version "10.0.4"
resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-10.0.4.tgz#fe61e5a82c494a3aaabcca4997aff94fa891d12e"
integrity sha512-m5dWL1Lg9M4kDA/kjpdyKsrx9arbIebrgsSzr/QG7cPon6SQMRs9ECZBM+3GalkQghrPO1mLgnrUwFBUH2QNPw==
version "10.0.5"
resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-10.0.5.tgz#2e4b24f7440257d3038ad717b19c5c0065684937"
integrity sha512-WJz5+XxOb0ORUERwNEUsX+0L7V4Tm7tz6i/mKAcz5SbE5eVfi+ywT+Zg7xMVRhyEpwDycqLXHK4piBOuGIjk+g==
"@angular/localize@~10.0.0":
version "10.0.4"
resolved "https://registry.yarnpkg.com/@angular/localize/-/localize-10.0.4.tgz#2c30eb604c931d98d52a2dea098ebb6a320431a3"
integrity sha512-MMMmgd6zFggWn01q/O8GYY/wRrbVxxR5H/XyQIqCjRQJgcrM0lwBnvVfFRGYa4oDHgik3Ri7Tbi1envhXSCMEA==
version "10.0.5"
resolved "https://registry.yarnpkg.com/@angular/localize/-/localize-10.0.5.tgz#cc51976e0ad3cdd05df0ce8448b85d1f2b9ff492"
integrity sha512-iyZ58H7+WBm9J1K2OLKgFFHno33KOu1FMQKaGdj/XCZaG1PphanOUivNxvxXo/6r71UW6uX/U17Rk//F8M0zPA==
dependencies:
"@babel/core" "7.8.3"
glob "7.1.2"
@ -383,23 +383,23 @@
yargs "15.3.0"
"@angular/platform-browser-dynamic@~10.0.0":
version "10.0.4"
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-10.0.4.tgz#77844511154eeb5eec5e68566767c215654f34e8"
integrity sha512-RoUMqYhUwF6+Mvk/aH0IZQ1D0SDEi9k5EZlx9CJ3RvNuKygk7to+S4vMWVpGxFQlwdS3bytRLKi+Kki6f4nLkg==
version "10.0.5"
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-10.0.5.tgz#bf0e6af90677e08c38747b1d1b9bacb2463872e0"
integrity sha512-Qa6RM9aJuuw5E7BeGBG9/kCOtssI6Wyzbb/8awzZFF6Fjqom1kef185nwx7SgQs2cdrhqRAgjt0eDJqTfSVb+Q==
dependencies:
tslib "^2.0.0"
"@angular/platform-browser@~10.0.0":
version "10.0.4"
resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-10.0.4.tgz#58131215e414be7ee9d4024e47f61b4a02166b28"
integrity sha512-iaZ8pFS5XUgPCO6/C47TzSFPzlzgleayq099cVOOx9z0t/SwUCSKt4AdAVhyQ8RTnx6l1JmmwBgRaXpScZlqzg==
version "10.0.5"
resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-10.0.5.tgz#0b90263a3858385cfb3ab440358d19eb9e8e91d3"
integrity sha512-bmknyK18yMv+iLCqcrfcpq3/2Ls9mRA1lOe7YhSGpHxM969Rb6Av8vJNW+uv0y7+gTvxacmpiQO8iryr/xvzRg==
dependencies:
tslib "^2.0.0"
"@angular/router@~10.0.0":
version "10.0.4"
resolved "https://registry.yarnpkg.com/@angular/router/-/router-10.0.4.tgz#d68c2711b53f3bdc1508d9b7e90b181b4e09f42c"
integrity sha512-iDLWdmltU5pZ6M/fBKC5Kg2o9Aqb1YJ+oHXFu186BQAl2RNeNCmMQ0VaCxjpMgD/MoSxpuRuGQ6rRrCSFCxtcQ==
version "10.0.5"
resolved "https://registry.yarnpkg.com/@angular/router/-/router-10.0.5.tgz#3e5f8be439b827b1d7bc980d673dec216a2f0769"
integrity sha512-hUGffVOjkdaN5IN9fSipm5ED+XGyfc9yOr5hYr/6YA3H4xaQzXZGlzrrDhAVltTyYalD2LJeXhzlvp59lXgcjg==
dependencies:
tslib "^2.0.0"
@ -2309,12 +2309,12 @@
jquery "3.5.0"
replace-in-file "^4.1.3"
"@ngtools/webpack@10.0.3":
version "10.0.3"
resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-10.0.3.tgz#2f8b2674108f7c36ffcdfc9e9aa024abfe3c7e04"
integrity sha512-0TuvYMCLtsApLtCHXeDBYGEoAQXzsRLpgFxPM5W7CGcj0ecthZO4NYrMAt+J8ky//KmbxqQSFHWmss2cbirIPA==
"@ngtools/webpack@10.0.4":
version "10.0.4"
resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-10.0.4.tgz#25c0a932319f8a26cbc849f4d13587f179fdc494"
integrity sha512-qzWkmVUUvr+2kiCtVLyopRwaBbExWtsGrgNVnJoAhtKKDflT4W33FpyJZJvxHwL6nfGGdb261Gfw7teIlCGjog==
dependencies:
"@angular-devkit/core" "10.0.3"
"@angular-devkit/core" "10.0.4"
enhanced-resolve "4.1.1"
rxjs "6.5.5"
webpack-sources "1.4.3"
@ -2402,12 +2402,12 @@
"@octokit/types" "^5.0.0"
"@octokit/endpoint@^6.0.1":
version "6.0.4"
resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.4.tgz#da3eafdee1fabd6e5b6ca311efcba26f0dd99848"
integrity sha512-ZJHIsvsClEE+6LaZXskDvWIqD3Ao7+2gc66pRG5Ov4MQtMvCU9wGu1TItw9aGNmRuU9x3Fei1yb+uqGaQnm0nw==
version "6.0.5"
resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.5.tgz#43a6adee813c5ffd2f719e20cfd14a1fee7c193a"
integrity sha512-70K5u6zd45ItOny6aHQAsea8HHQjlQq85yqOMe+Aj8dkhN2qSJ9T+Q3YjUjEYfPRBcuUWNgMn62DQnP/4LAIiQ==
dependencies:
"@octokit/types" "^5.0.0"
is-plain-object "^3.0.0"
is-plain-object "^4.0.0"
universal-user-agent "^6.0.0"
"@octokit/plugin-enterprise-rest@^6.0.1":
@ -2454,15 +2454,15 @@
once "^1.4.0"
"@octokit/request@^5.2.0":
version "5.4.6"
resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.6.tgz#e8cc8d4cfc654d30428ea92aaa62168fd5ead7eb"
integrity sha512-9r8Sn4CvqFI9LDLHl9P17EZHwj3ehwQnTpTE+LEneb0VBBqSiI/VS4rWIBfBhDrDs/aIGEGZRSB0QWAck8u+2g==
version "5.4.7"
resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.7.tgz#fd703ee092e0463ceba49ff7a3e61cb4cf8a0fde"
integrity sha512-FN22xUDP0i0uF38YMbOfx6TotpcENP5W8yJM1e/LieGXn6IoRxDMnBf7tx5RKSW4xuUZ/1P04NFZy5iY3Rax1A==
dependencies:
"@octokit/endpoint" "^6.0.1"
"@octokit/request-error" "^2.0.0"
"@octokit/types" "^5.0.0"
deprecation "^2.0.0"
is-plain-object "^3.0.0"
is-plain-object "^4.0.0"
node-fetch "^2.3.0"
once "^1.4.0"
universal-user-agent "^6.0.0"
@ -2497,9 +2497,9 @@
"@types/node" ">= 8"
"@octokit/types@^5.0.0", "@octokit/types@^5.0.1":
version "5.1.0"
resolved "https://registry.yarnpkg.com/@octokit/types/-/types-5.1.0.tgz#4377a3f39edad3e60753fb5c3c310756f1ded57f"
integrity sha512-OFxUBgrEllAbdEmWp/wNmKIu5EuumKHG4sgy56vjZ8lXPgMhF05c76hmulfOdFHHYRpPj49ygOZJ8wgVsPecuA==
version "5.2.0"
resolved "https://registry.yarnpkg.com/@octokit/types/-/types-5.2.0.tgz#d075dc23bf293f540739250b6879e2c1be2fc20c"
integrity sha512-XjOk9y4m8xTLIKPe1NFxNWBdzA2/z3PFFA/bwf4EoH6oS8hM0Y46mEa4Cb+KCyj/tFDznJFahzQ0Aj3o1FYq4A==
dependencies:
"@types/node" ">= 8"
@ -2545,21 +2545,21 @@
estree-walker "^1.0.1"
picomatch "^2.2.2"
"@schematics/angular@10.0.3":
version "10.0.3"
resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-10.0.3.tgz#a4d54389918a7f9c82345d4f639d930f22b62c6b"
integrity sha512-Or2pCqjpPbAvmbxtfMosGwQbNbSL4xodK5Key7678ZAPGB+rcxrVkBI9yxEJ/qzF/LrmMoKqy0JCmVLK7Grpog==
"@schematics/angular@10.0.4":
version "10.0.4"
resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-10.0.4.tgz#5a84fe6f4e8d8e4e7e2925c05b7689f70dab597f"
integrity sha512-wTpuVfPKALOcOHfD7Y222r/IfqJgpqASyoXdkzf7dZVrLXjNy/XrKuc2Vfn85Ce9lnme0xT5ww+KjbSRc0cSUw==
dependencies:
"@angular-devkit/core" "10.0.3"
"@angular-devkit/schematics" "10.0.3"
"@angular-devkit/core" "10.0.4"
"@angular-devkit/schematics" "10.0.4"
"@schematics/update@0.1000.3":
version "0.1000.3"
resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.1000.3.tgz#d361f23ae49cd3ac9b547b6f4c2470d7ca86aabd"
integrity sha512-Nncdklmzi1tyzkoAh7GlSslxriRhftlmfqPVmFHrrPRttYACtT/QH5qcWsrPgTPpHGINYEHrPjpeljsMoMchBQ==
"@schematics/update@0.1000.4":
version "0.1000.4"
resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.1000.4.tgz#e0780c5313ee90480f8a26b66c0104f664df64a4"
integrity sha512-NkIUsqnEqG6jhW6b1xXYmgW02WYAN9/IoswnZnaQrbGzxroNVQKURiV4AP22sZLCr7gQ9NJwjXxO5rxbzbY8wg==
dependencies:
"@angular-devkit/core" "10.0.3"
"@angular-devkit/schematics" "10.0.3"
"@angular-devkit/core" "10.0.4"
"@angular-devkit/schematics" "10.0.4"
"@yarnpkg/lockfile" "1.1.0"
ini "1.3.5"
npm-package-arg "^8.0.0"
@ -2721,19 +2721,19 @@
integrity sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=
"@types/node@*", "@types/node@>= 8":
version "14.0.23"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.23.tgz#676fa0883450ed9da0bb24156213636290892806"
integrity sha512-Z4U8yDAl5TFkmYsZdFPdjeMa57NOvnaf1tljHzhouaPEp7LCj2JKkejpI1ODviIAQuW4CcQmxkQ77rnLsOOoKw==
version "14.0.26"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.26.tgz#22a3b8a46510da8944b67bfc27df02c34a35331c"
integrity sha512-W+fpe5s91FBGE0pEa0lnqGLL4USgpLgs4nokw16SrBBco/gQxuua7KnArSEOd5iaMqbbSHV10vUDkJYJJqpXKA==
"@types/node@^12.11.1":
version "12.12.50"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.50.tgz#e9b2e85fafc15f2a8aa8fdd41091b983da5fd6ee"
integrity sha512-5ImO01Fb8YsEOYpV+aeyGYztcYcjGsBvN4D7G5r1ef2cuQOpymjWNQi5V0rKHE6PC2ru3HkoUr/Br2/8GUA84w==
version "12.12.53"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.53.tgz#be0d375933c3d15ef2380dafb3b0350ea7021129"
integrity sha512-51MYTDTyCziHb70wtGNFRwB4l+5JNvdqzFSkbDvpbftEgVUBEE+T5f7pROhWMp/fxp07oNIEQZd5bbfAH22ohQ==
"@types/node@^8.0.31":
version "8.10.61"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.61.tgz#d299136ce54bcaf1abaa4a487f9e4bedf6b0d393"
integrity sha512-l+zSbvT8TPRaCxL1l9cwHCb0tSqGAGcjPJFItGGYat5oCTiq1uQQKYg5m7AF1mgnEBzFXGLJ2LRmNjtreRX76Q==
version "8.10.62"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.62.tgz#8d0d0db44a46ae7679d92e2e36d41bf1c3625d6a"
integrity sha512-76fupxOYVxk36kb7O/6KtrAPZ9jnSK3+qisAX4tQMEuGNdlvl7ycwatlHqjoE6jHfVtXFM3pCrCixZOidc5cuw==
"@types/normalize-package-data@^2.4.0":
version "2.4.0"
@ -3091,41 +3091,11 @@ ajv-errors@^1.0.0:
integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==
ajv-keywords@^3.1.0, ajv-keywords@^3.4.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.1.tgz#b83ca89c5d42d69031f424cad49aada0236c6957"
integrity sha512-KWcq3xN8fDjSB+IMoh2VaXVhRI0BBGxoYp3rx7Pkb6z0cFjYR9Q9l4yZqqals0/zsioCmocC5H6UvsGD4MoIBA==
ajv@6.10.2:
version "6.10.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52"
integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==
dependencies:
fast-deep-equal "^2.0.1"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
ajv@6.12.0:
version "6.12.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7"
integrity sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==
dependencies:
fast-deep-equal "^3.1.1"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
ajv@6.12.2:
version "6.12.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd"
integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==
dependencies:
fast-deep-equal "^3.1.1"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
version "3.5.2"
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.5.5:
ajv@6.12.3, ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.5.5:
version "6.12.3"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.3.tgz#18c5af38a111ddeb4f2697bd78d68abc1cabd706"
integrity sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==
@ -4116,9 +4086,9 @@ caniuse-api@^3.0.0:
lodash.uniq "^4.5.0"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001032, caniuse-lite@^1.0.30001061, caniuse-lite@^1.0.30001093, caniuse-lite@^1.0.30001097:
version "1.0.30001102"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001102.tgz#3275e7a8d09548f955f665e532df88de0b63741a"
integrity sha512-fOjqRmHjRXv1H1YD6QVLb96iKqnu17TjcLSaX64TwhGYed0P1E1CCWZ9OujbbK4Z/7zax7zAzvQidzdtjx8RcA==
version "1.0.30001107"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001107.tgz#809360df7a5b3458f627aa46b0f6ed6d5239da9a"
integrity sha512-86rCH+G8onCmdN4VZzJet5uPELII59cUzDphko3thQFgAQG1RNa+sVLDoALIhRYmflo5iSIzWY3vu1XTWtNMQQ==
canonical-path@1.0.0:
version "1.0.0"
@ -4201,7 +4171,7 @@ chartjs-color@^2.1.0:
chartjs-color-string "^0.6.0"
color-convert "^1.9.3"
"chokidar@>=2.0.0 <4.0.0", chokidar@^3.0.0, chokidar@^3.0.2, chokidar@^3.2.1, chokidar@^3.4.0:
"chokidar@>=2.0.0 <4.0.0", chokidar@^3.0.0, chokidar@^3.0.2, chokidar@^3.2.1, chokidar@^3.4.1:
version "3.4.1"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.1.tgz#e905bdecf10eaa0a0b1db0c664481cc4cbc22ba1"
integrity sha512-TQTJyr2stihpC4Sya9hs2Xh+O2wf+igjL36Y75xx2WdHuiICcn/XJza46Jwt0eT5hVpQOzo3FpY3cj3RVYLX0g==
@ -4305,9 +4275,9 @@ cli-cursor@^3.1.0:
restore-cursor "^3.1.0"
cli-spinners@^2.0.0, cli-spinners@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.3.0.tgz#0632239a4b5aa4c958610142c34bb7a651fc8df5"
integrity sha512-Xs2Hf2nzrvJMFKimOR7YR0QwZ8fc0u98kdtwN1eNAZzNQgH3vK2pXzff6GJtKh7S5hoJ87ECiAiZFS2fb5Ii2w==
version "2.4.0"
resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.4.0.tgz#c6256db216b878cfba4720e719cec7cf72685d7f"
integrity sha512-sJAofoarcm76ZGpuooaO0eDy8saEy+YoZBLjC4h8srt4jeBnkYeOgqxgsJQTpyt2LjI5PTfLJHSL+41Yu4fEJA==
cli-width@^2.0.0:
version "2.2.1"
@ -5051,13 +5021,12 @@ css-select@^2.0.0:
nth-check "^1.0.2"
css-selector-tokenizer@^0.7.1:
version "0.7.2"
resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.2.tgz#11e5e27c9a48d90284f22d45061c303d7a25ad87"
integrity sha512-yj856NGuAymN6r8bn8/Jl46pR+OC3eEvAhfGYDUe7YPtTPAYrSSw4oAniZ9Y8T5B92hjhwTBLUen0/vKPxf6pw==
version "0.7.3"
resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz#735f26186e67c749aaf275783405cf0661fae8f1"
integrity sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==
dependencies:
cssesc "^3.0.0"
fastparse "^1.1.2"
regexpu-core "^4.6.0"
css-tree@1.0.0-alpha.37:
version "1.0.0-alpha.37"
@ -5291,9 +5260,9 @@ data-urls@^2.0.0:
whatwg-url "^8.0.0"
date-fns@^2.10.0:
version "2.14.0"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.14.0.tgz#359a87a265bb34ef2e38f93ecf63ac453f9bc7ba"
integrity sha512-1zD+68jhFgDIM0rF05rcwYO8cExdNqxjq4xP1QKM60Q45mnO6zaMWB4tOzrIr4M4GSLntsKeE4c9Bdl2jhL/yw==
version "2.15.0"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.15.0.tgz#424de6b3778e4e69d3ff27046ec136af58ae5d5f"
integrity sha512-ZCPzAMJZn3rNUvvQIMlXhDr4A+Ar07eLeGsGREoWU19a3Pqf5oYa+ccd+B3F6XVtQY6HANMFdOQ8A+ipFnvJdQ==
dateformat@^3.0.0:
version "3.0.3"
@ -5697,9 +5666,9 @@ ee-first@1.1.1:
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
electron-to-chromium@^1.3.488:
version "1.3.499"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.499.tgz#06949f19877dafa42915e57dfeb4c1cfb86a8649"
integrity sha512-y7FwtQm/8xuLMnYQfBQDYzCpNn+VkSnf4c3Km5TWMNXg7JA5RQBuxmcLaKdDVcIK0K5xGIa7TlxpRt4BdNxNoA==
version "1.3.510"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.510.tgz#dee781ff8b595c0deb60172b75d50b6889757eda"
integrity sha512-sLtGB0znXdmo6lM8hy5wTVo+fLqvIuO8hEpgc0DvPmFZqvBu/WB7AarEwhxVKjf3rVbws/rC8Xf+AlsOb36lJQ==
elliptic@^6.0.0, elliptic@^6.5.2:
version "6.5.3"
@ -5782,9 +5751,9 @@ env-paths@^2.2.0:
integrity sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==
envinfo@^7.3.1:
version "7.5.1"
resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.5.1.tgz#93c26897225a00457c75e734d354ea9106a72236"
integrity sha512-hQBkDf2iO4Nv0CNHpCuSBeaSrveU6nThVxFGTrq/eDlV716UQk09zChaJae4mZRsos1x4YLY2TaH3LHUae3ZmQ==
version "7.7.2"
resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.7.2.tgz#098f97a0e902f8141f9150553c92dbb282c4cabe"
integrity sha512-k3Eh5bKuQnZjm49/L7H4cHzs2FlL5QjbTB3JrPxoTI8aJG7hVMe4uKyJxSYH4ahseby2waUwk5OaKX/nAsaYgg==
err-code@^1.0.0:
version "1.1.2"
@ -5957,9 +5926,9 @@ eventemitter3@^4.0.0:
integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==
events@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/events/-/events-3.1.0.tgz#84279af1b34cb75aa88bf5ff291f6d0bd9b31a59"
integrity sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==
version "3.2.0"
resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379"
integrity sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==
eventsource@^1.0.7:
version "1.0.7"
@ -6158,11 +6127,6 @@ extsprintf@^1.2.0:
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
fast-deep-equal@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=
fast-deep-equal@^3.1.1:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
@ -7727,10 +7691,10 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4:
dependencies:
isobject "^3.0.1"
is-plain-object@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-3.0.1.tgz#662d92d24c0aa4302407b0d45d21f2251c85f85b"
integrity sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==
is-plain-object@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-4.1.1.tgz#1a14d6452cbd50790edc7fdaa0aed5a40a35ebb5"
integrity sha512-5Aw8LLVsDlZsETVMhoMXzqsXwQqr/0vlnBYzIXJbYo2F4yYlhLHs+Ez7Bod7IIQKWkJbJfxrWD7pA1Dw1TKrwA==
is-reference@^1.1.2:
version "1.2.1"
@ -7894,10 +7858,10 @@ jasmine-core@~2.8.0:
resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.8.0.tgz#bcc979ae1f9fd05701e45e52e65d3a5d63f1a24e"
integrity sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=
jasmine-core@~3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.5.0.tgz#132c23e645af96d85c8bca13c8758b18429fc1e4"
integrity sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA==
jasmine-core@~3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.6.0.tgz#491f3bb23941799c353ceb7a45b38a950ebc5a20"
integrity sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw==
jasmine@2.8.0:
version "2.8.0"
@ -7909,12 +7873,12 @@ jasmine@2.8.0:
jasmine-core "~2.8.0"
jasmine@^3.3.1:
version "3.5.0"
resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-3.5.0.tgz#7101eabfd043a1fc82ac24e0ab6ec56081357f9e"
integrity sha512-DYypSryORqzsGoMazemIHUfMkXM7I7easFaxAvNM3Mr6Xz3Fy36TupTrAOxZWN8MVKEU5xECv22J4tUQf3uBzQ==
version "3.6.1"
resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-3.6.1.tgz#a20456b309a669b547a3c24bb2120f16f70cfc65"
integrity sha512-Jqp8P6ZWkTVFGmJwBK46p+kJNrZCdqkQ4GL+PGuBXZwK1fM4ST9BizkYgIwCFqYYqnTizAy6+XG2Ej5dFrej9Q==
dependencies:
glob "^7.1.4"
jasmine-core "~3.5.0"
fast-glob "^2.2.6"
jasmine-core "~3.6.0"
jasminewd2@^2.1.0:
version "2.2.0"
@ -8865,9 +8829,9 @@ lru-cache@^6.0.0:
yallist "^4.0.0"
macos-release@^2.2.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.4.0.tgz#837b39fc01785c3584f103c5599e0f0c8068b49e"
integrity sha512-ko6deozZYiAkqa/0gmcsz+p4jSy3gY7/ZsCEokPaYd8k+6/aXGkiTgr61+Owup7Sf+xjqW8u2ElhoM9SEcEfuA==
version "2.4.1"
resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.4.1.tgz#64033d0ec6a5e6375155a74b1a1eba8e509820ac"
integrity sha512-H/QHeBIN1fIGJX517pvK8IEK53yQOW7YcEI55oYtgjDdoCQQz7eJS94qt5kNrscReEyuD/JcdFCm2XBEcGOITg==
magic-string@0.25.3:
version "0.25.3"
@ -9412,9 +9376,9 @@ nanomatch@^1.2.9:
to-regex "^3.0.1"
native-request@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/native-request/-/native-request-1.0.5.tgz#da67637c0a0ed0758243d1a688d647612f2d5a0a"
integrity sha512-7wU3DvBGAJQxWuMR3F62zrhB7hxNj2DdlC/eBVrCgavc6+ZpFZOqS/PsR7QyUPLMkFk0GvvzoeeOAZGLLnObnA==
version "1.0.7"
resolved "https://registry.yarnpkg.com/native-request/-/native-request-1.0.7.tgz#ff742dc555b4c8f2f1c14b548639ba174e573856"
integrity sha512-9nRjinI9bmz+S7dgNtf4A70+/vPhnd+2krGpy4SUlADuOuSa24IDkNaZ+R/QT1wQ6S8jBdi6wE7fLekFZNfUpQ==
natural-compare@^1.4.0:
version "1.4.0"
@ -9437,14 +9401,14 @@ next-tick@~1.0.0:
integrity sha1-yobR/ogoFpsBICCOPchCS524NCw=
ng-packagr@^10.0.0:
version "10.0.2"
resolved "https://registry.yarnpkg.com/ng-packagr/-/ng-packagr-10.0.2.tgz#31aed1cc373a7b84baaef73c1dae29ccc667a57f"
integrity sha512-nmw6nQLwPBVIdU1M0Omo6BLeg5Iugj3hmq7pd+Un7hZ33T+D8vfB88jXUf/DklRPLIwRWlu+5evQNj76VPhYUw==
version "10.0.3"
resolved "https://registry.yarnpkg.com/ng-packagr/-/ng-packagr-10.0.3.tgz#ef3fa4c3dbc826695b8e12b307172e23504e1229"
integrity sha512-M10y3eeAbIaU+NvNZEeGnuoWxZis0GFj9EKetYmmsXMjHxsPqEAZ1PUkKioIo0RPXuR1N4QGW0MSegHp5kBFeg==
dependencies:
"@rollup/plugin-commonjs" "^14.0.0"
"@rollup/plugin-json" "^4.0.0"
"@rollup/plugin-node-resolve" "^8.0.0"
ajv "^6.10.2"
ajv "^6.12.3"
autoprefixer "^9.6.5"
browserslist "^4.7.0"
chalk "^4.0.0"
@ -9590,9 +9554,9 @@ node-notifier@^6.0.0:
which "^1.3.1"
node-releases@^1.1.58:
version "1.1.59"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.59.tgz#4d648330641cec704bff10f8e4fe28e453ab8e8e"
integrity sha512-H3JrdUczbdiwxN5FuJPyCHnGHIFqQ0wWxo+9j1kAXAzqNMAHlo+4I/sYYxpyK0irQ73HgdiyzD32oqQDcU2Osw==
version "1.1.60"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.60.tgz#6948bdfce8286f0b5d0e5a88e8384e954dfe7084"
integrity sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA==
node-sass-tilde-importer@^1.0.0:
version "1.0.2"
@ -10241,9 +10205,9 @@ parse-json@^4.0.0:
json-parse-better-errors "^1.0.1"
parse-json@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.0.0.tgz#73e5114c986d143efa3712d4ea24db9a4266f60f"
integrity sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==
version "5.0.1"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.0.1.tgz#7cfe35c1ccd641bce3981467e6c2ece61b3b3878"
integrity sha512-ztoZ4/DYeXQq4E21v169sC8qWINGpcosGv9XhTDvg9/hWvx/zrFkc9BiWxR58OJLHGk28j5BL0SDLeV2WmFZlQ==
dependencies:
"@babel/code-frame" "^7.0.0"
error-ex "^1.3.1"
@ -10458,9 +10422,9 @@ pnp-webpack-plugin@1.6.4:
ts-pnp "^1.1.6"
portfinder@^1.0.26:
version "1.0.26"
resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.26.tgz#475658d56ca30bed72ac7f1378ed350bd1b64e70"
integrity sha512-Xi7mKxJHHMI3rIUrnm/jjUgwhbYMkp/XKEcZX3aG4BrumLpq3nmoQMX+ClYnDZnZ/New7IatC1no5RX0zo1vXQ==
version "1.0.27"
resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.27.tgz#a41333c116b5e5f3d380f9745ac2f35084c4c758"
integrity sha512-bJ3U3MThKnyJ9Dx1Idtm5pQmxXqw08+XOHhi/Lie8OF1OlhVaBFhsntAIhkZYjfDcCzszSr0w1yCbccThhzgxQ==
dependencies:
async "^2.6.2"
debug "^3.1.1"
@ -10625,14 +10589,14 @@ postcss-modules-extract-imports@^2.0.0:
postcss "^7.0.5"
postcss-modules-local-by-default@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.2.tgz#e8a6561be914aaf3c052876377524ca90dbb7915"
integrity sha512-jM/V8eqM4oJ/22j0gx4jrp63GSvDH6v86OqyTHHUvk4/k1vceipZsaymiZ5PvocqZOl5SFHiFJqjs3la0wnfIQ==
version "3.0.3"
resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz#bb14e0cc78279d504dbdcbfd7e0ca28993ffbbb0"
integrity sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==
dependencies:
icss-utils "^4.1.1"
postcss "^7.0.16"
postcss "^7.0.32"
postcss-selector-parser "^6.0.2"
postcss-value-parser "^4.0.0"
postcss-value-parser "^4.1.0"
postcss-modules-scope@^2.2.0:
version "2.2.0"
@ -10813,7 +10777,7 @@ postcss-value-parser@^3.0.0, postcss-value-parser@^3.2.3:
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281"
integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==
postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2, postcss-value-parser@^4.0.3, postcss-value-parser@^4.1.0:
postcss-value-parser@^4.0.2, postcss-value-parser@^4.0.3, postcss-value-parser@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb"
integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==
@ -10836,7 +10800,7 @@ postcss@7.0.31:
source-map "^0.6.1"
supports-color "^6.1.0"
postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.2, postcss@^7.0.27, postcss@^7.0.29, postcss@^7.0.30, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6:
postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.2, postcss@^7.0.27, postcss@^7.0.29, postcss@^7.0.30, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6:
version "7.0.32"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d"
integrity sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==
@ -11358,11 +11322,16 @@ regenerate@^1.4.0:
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.1.tgz#cad92ad8e6b591773485fbe05a485caf4f457e6f"
integrity sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==
regenerator-runtime@0.13.5, regenerator-runtime@^0.13.4:
regenerator-runtime@0.13.5:
version "0.13.5"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697"
integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==
regenerator-runtime@^0.13.4:
version "0.13.7"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
regenerator-transform@^0.14.2:
version "0.14.5"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4"
@ -11391,7 +11360,7 @@ regexp.prototype.flags@^1.2.0:
define-properties "^1.1.3"
es-abstract "^1.17.0-next.1"
regexpu-core@^4.6.0, regexpu-core@^4.7.0:
regexpu-core@^4.7.0:
version "4.7.0"
resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.0.tgz#fcbf458c50431b0bb7b45d6967b8192d91f3d938"
integrity sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==
@ -11460,19 +11429,19 @@ replace-in-file@^4.1.3:
glob "^7.1.6"
yargs "^15.0.2"
request-promise-core@1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.3.tgz#e9a3c081b51380dfea677336061fea879a829ee9"
integrity sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==
request-promise-core@1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f"
integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==
dependencies:
lodash "^4.17.15"
lodash "^4.17.19"
request-promise-native@^1.0.7:
version "1.0.8"
resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.8.tgz#a455b960b826e44e2bf8999af64dff2bfe58cb36"
integrity sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==
version "1.0.9"
resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28"
integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==
dependencies:
request-promise-core "1.1.3"
request-promise-core "1.1.4"
stealthy-require "^1.1.1"
tough-cookie "^2.3.3"
@ -11708,9 +11677,9 @@ rollup@2.10.9:
fsevents "~2.1.2"
rollup@^2.8.0:
version "2.21.0"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.21.0.tgz#d2e114533812043d5c9b7b0a83f1b2a242e4e1d6"
integrity sha512-BEGgy+wSzux7Ycq58pRiWEOBZaXRXTuvzl1gsm7gqmsAHxkWf9nyA5V2LN9fGSHhhDQd0/C13iRzSh4bbIpWZQ==
version "2.23.0"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.23.0.tgz#b7ab1fee0c0e60132fd0553c4df1e9cdacfada9d"
integrity sha512-vLNmZFUGVwrnqNAJ/BvuLk1MtWzu4IuoqsH9UWK5AIdO3rt8/CSiJNvPvCIvfzrbNsqKbNzPAG1V2O4eTe2XZg==
optionalDependencies:
fsevents "~2.1.2"
@ -12733,9 +12702,9 @@ symbol-tree@^3.2.2:
integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
symlink-manager@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/symlink-manager/-/symlink-manager-1.5.0.tgz#106576eecfb611b6c320d3f643d714583248b33f"
integrity sha512-+5n/FYEhoxsLhMqxjTn74676xox3Nkvw71lMgk0jjeTwHnqiqTjp16IPONy6Rld25dCtrTgcDJ7lYkx+b1Diog==
version "1.5.1"
resolved "https://registry.yarnpkg.com/symlink-manager/-/symlink-manager-1.5.1.tgz#75721cacf2247ad6817b0ca5c033b87c8d4599fd"
integrity sha512-LKRsow5vrpOpk7dOxfj9uWDfb2g+f7GoaJdvTnUm5NyodQuHZJxHZpqsHvoKIvMyrlpkL4qbC8HXx2DbkQ+w8Q==
dependencies:
arg "^4.1.0"
chokidar "^3.0.2"
@ -13076,9 +13045,9 @@ trim-off-newlines@^1.0.0:
integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM=
ts-jest@^26.0.0:
version "26.1.3"
resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.1.3.tgz#aac928a05fdf13e3e6dfbc8caec3847442667894"
integrity sha512-beUTSvuqR9SmKQEylewqJdnXWMVGJRFqSz2M8wKJe7GBMmLZ5zw6XXKSJckbHNMxn+zdB3guN2eOucSw2gBMnw==
version "26.1.4"
resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.1.4.tgz#87d41a96016a8efe4b8cc14501d3785459af6fa6"
integrity sha512-Nd7diUX6NZWfWq6FYyvcIPR/c7GbEF75fH1R6coOp3fbNzbRJBZZAn0ueVS0r8r9ral1VcrpneAFAwB3TsVS1Q==
dependencies:
bs-logger "0.x"
buffer-from "1.x"
@ -13111,9 +13080,9 @@ ts-pnp@^1.1.6:
integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==
ts-toolbelt@^6.9.9:
version "6.13.11"
resolved "https://registry.yarnpkg.com/ts-toolbelt/-/ts-toolbelt-6.13.11.tgz#46260d1cff0f2d635931df8d32239b73f2ed4f8d"
integrity sha512-QmvHwAofS1MAn1ndXCn2AVhxJLipaj4tK26SrhBTiydfgKtbLlU2efpIP/8Jocrc9ynNslZqrhqAesruJMlxCQ==
version "6.13.34"
resolved "https://registry.yarnpkg.com/ts-toolbelt/-/ts-toolbelt-6.13.34.tgz#3bd6e1e333a75c5ddab584d293f35494cb8c9222"
integrity sha512-ikZwSUPO+fW+OUnyER1KCG58khTi51Di3+iVdhkK7UshqkL0Ir749B2TQ44o/khkKvDSOMzF8NuVkQLycDXnsg==
tsickle@^0.38.1:
version "0.38.1"
@ -13572,14 +13541,14 @@ watchpack-chokidar2@^2.0.0:
chokidar "^2.1.8"
watchpack@^1.6.1:
version "1.7.2"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.2.tgz#c02e4d4d49913c3e7e122c3325365af9d331e9aa"
integrity sha512-ymVbbQP40MFTp+cNMvpyBpBtygHnPzPkHqoIwRRj/0B8KhqQwV8LaKjtbaxF2lK4vl8zN9wCxS46IFCU5K4W0g==
version "1.7.4"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.4.tgz#6e9da53b3c80bb2d6508188f5b200410866cd30b"
integrity sha512-aWAgTW4MoSJzZPAicljkO1hsi1oKj/RRq/OJQh2PKI2UKL04c2Bs+MBOB+BBABHTXJpf9mCwHN7ANCvYsvY2sg==
dependencies:
graceful-fs "^4.1.2"
neo-async "^2.5.0"
optionalDependencies:
chokidar "^3.4.0"
chokidar "^3.4.1"
watchpack-chokidar2 "^2.0.0"
wbuf@^1.1.0, wbuf@^1.7.3:

1
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName.MyProjectName.Application.Contracts.csproj

@ -13,7 +13,6 @@
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.ObjectExtending\Volo.Abp.ObjectExtending.csproj" />
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AutoMapper\Volo.Abp.AutoMapper.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\account\src\Volo.Abp.Account.Application.Contracts\Volo.Abp.Account.Application.Contracts.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\identity\src\Volo.Abp.Identity.Application.Contracts\Volo.Abp.Identity.Application.Contracts.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\permission-management\src\Volo.Abp.PermissionManagement.Application.Contracts\Volo.Abp.PermissionManagement.Application.Contracts.csproj" />

12
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Application.Contracts/MyProjectNameApplicationContractsModule.cs

@ -1,5 +1,4 @@
using Volo.Abp.Account;
using Volo.Abp.AutoMapper;
using Volo.Abp.FeatureManagement;
using Volo.Abp.Identity;
using Volo.Abp.Modularity;
@ -16,8 +15,7 @@ namespace MyCompanyName.MyProjectName
typeof(AbpIdentityApplicationContractsModule),
typeof(AbpPermissionManagementApplicationContractsModule),
typeof(AbpTenantManagementApplicationContractsModule),
typeof(AbpObjectExtendingModule),
typeof(AbpAutoMapperModule)
typeof(AbpObjectExtendingModule)
)]
public class MyProjectNameApplicationContractsModule : AbpModule
{
@ -25,13 +23,5 @@ namespace MyCompanyName.MyProjectName
{
MyProjectNameDtoExtensions.Configure();
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpAutoMapperOptions>(options =>
{
options.AddMaps<MyProjectNameApplicationContractsModule>();
});
}
}
}

Loading…
Cancel
Save